一個GET請求拿到flag——XCTF 2018 Final PUBG(WEB 2) Writeup
環境
XCTF Final 和 HITB 趕在了週四週五,週四晚上拿到題目,此時隊友已經對PHP程式碼完成了解密工作。
- 解密後的程式碼: http://static.cdxy.me/DECODED.zip
- github:https://github.com/Xyntax/XCTF-2018-Final-WEB2-PUBG
解密後的程式碼讀起來有點麻煩,並無大礙。
有點魔幻的get flag過程
當晚並沒有找到突破口,但發現 kss_admin/admin_update
函式疑似是exp鏈路中的一環。
其中120行發現CMS更新功能,從遠端主站拉取程式碼寫入本地:
$_obfuscate_koiKkIiPjI6UkYeRlIqNhoc� = _obfuscate_lY6Gk5KMkYmPjIyPhpCOlYc�( "http://api.hphu.com/import/".$_obfuscate_koaSiYqGjIqMiZSLk4uGiZU�.".php?phpver=".PHP_VERSION."&webid=".WEBID."&rid=".time( ), 300 );
跟進 Yc
函式,發現其註冊了兩個curl的回撥 read_header
, read_body
:
curl_setopt( $_obfuscate_joiNh4aIhouViZGQho_JiI4�, CURLOPT_HEADERFUNCTION, "read_header" ); curl_setopt( $_obfuscate_joiNh4aIhouViZGQho_JiI4�, CURLOPT_WRITEFUNCTION, "read_body" );
其中 read_body
函式會將curl到的content寫到本地檔案 kss_tool/_webup.php
file_put_contents( KSSROOTDIR."kss_tool".DIRECTORY_SEPARATOR."_webup.php", $_obfuscate_jJWMiJWJjoyIkYmLjY6VipM�, FILE_APPEND );
想要使用 admin_upload.php
這個點寫shell,需要滿足兩個條件:
- 繞過admin許可權驗證
- 能控制curl部分的回顯
週四當晚並沒有突破。週五早9點比賽環境恢復,我開啟ipython對這個疑似webshell的地址 kss_tool/_webup.php
做了監控。如果這個檔案被其他隊動了,就證明這個思路是可行的。
In [1]: while True: ...:try: ...:r = requests.get('http://guaika.txmeili.com:8888/kss_tool/_webup.php') ...:except Exception,e: ...:print e ...:continue ...:if r.content not in ans: ...:print r.content ...:ans.append(r.content)
結果發現這個檔案的http response在一直變化,看著看著flag就出來了....
然後提交拿了2血,幾秒之後看到De1ta也交了flag,感謝De1ta的WEB大佬們送的火箭!
解題過程
當天下午完整打通了exp過程,分為三個部分:
- 找到注入點,偷資料
- 構造cookie,拿到admin許可權
- 通過更新功能寫shell到本地,讀flag
SQL%E6%B3%A8%E5%85%A5/">SQL注入
CMS對SQL注入的防禦策略
kss_inc/function
ofollow,noindex" target="_blank">github link
實現了多種過濾方案,然後在SQL語句拼接取參時,通過傳入引數指定取參的位置(GET/POST/COOKIE)和過濾方案(sql/sqljs/num等)
外部取參的程式碼示例:
$_obfuscate_iJWMjIiVi5OGjJOViY2Li48� = _obfuscate_i4mIkpOGkomKiouRhoaMh5I�( "out_trade_no", "pg", "sql", "" );
意思是從POST/GET(pg)中取出引數 out_trade_no
的值,然後通過 sql
過濾器的檢查後,賦值到 i48
變數。
先簡單看下過濾器的正則:
case "sql" : if ( preg_match( "/select|insert|update|delete |union|into|load_file|outfile|char|0x[0-9a-f]{6}|\\.\\/|\\/\\*|'/i", $_obfuscate_ipCJlJOSlJSQkYqNlYqKlIs˙ ) ) { ob_clean( ); $_obfuscate_ipCJlJOSlJSQkYqNlYqKlIs˙ = preg_replace( "/(select|insert|update|delete |union|into|load_file|outfile|char|0x[0-9a-f]{6}|\\.\\/|\\*|')/i", "<font color=red>$1</font>", $_obfuscate_ipCJlJOSlJSQkYqNlYqKlIs˙ ); exit( "<p>MySQL injection:".$_obfuscate_lIyOioeNkY6Vj4qPkJGMiJQ˙.",".$_obfuscate_iYyTho_HlJCOh4yRj4ePj4k˙.",".$_obfuscate_ipCJlJOSlJSQkYqNlYqKlIs˙."</p>" );
過濾了 '
,然後匹配到這些危險字元時,會將引數帶到html回顯,使response可控(XSS敏感)。
正則寫的沒啥問題,接下來兩個方向:
'
注入構造
kss_inc/payapi_return2.php
是一個外部支付功能。其中的 chinabank
, e138
兩種支付方式均存在"未使用過濾器"直接傳參的漏洞。
else if ( $_obfuscate_kYyPkY_PkJKVh4qGjJGIio4� == "chinabank" ) { $_obfuscate_kpGPh4mNh46SkZONh4eLlJU� = ""; $_obfuscate_k42NkY2RkoiNjJCKlZSKiIg� = trim( $_POST['v_oid'] ); $_obfuscate_iJWMjIiVi5OGjJOViY2Li48� = $_obfuscate_k42NkY2RkoiNjJCKlZSKiIg�; $_obfuscate_iIuQkYaUioqGlI6IjIuMiI8� = trim( $_POST['v_pstatus'] ); $_obfuscate_jpGJk5SSkJOIk4iQiI_OhpU� = trim( $_POST['v_amount'] ); $_obfuscate_lIuQk5OGjpKVjY6UiI_QjJM� = $_obfuscate_jpGJk5SSkJOIk4iQiI_OhpU�; $_obfuscate_hpCRlJCSjI6Ki5WSipCLkpQ� = trim( $_POST['v_moneytype'] ); $_obfuscate_lJSPjJCOi5CIiJSSkZWNh4Y� = trim( $_POST['remark1'] ); $_obfuscate_iImJjYmQjYyOjIuVkIuMjIs� = trim( $_POST['v_md5str'] ); if ( $_obfuscate_iIuQkYaUioqGlI6IjIuMiI8� == "20" ) { $_obfuscate_i5CMioaGiI6ShomNiIuKjJE� = "TRADE_FINISHED"; } else { $_obfuscate_i5CMioaGiI6ShomNiIuKjJE� = "WAIT_BUYER_PAY"; } } else if ( $_obfuscate_kYyPkY_PkJKVh4qGjJGIio4� == "e138" ) { $_obfuscate_kpGPh4mNh46SkZONh4eLlJU� = ""; $_obfuscate_k42NkY2RkoiNjJCKlZSKiIg� = trim( $_POST['SerialNo'] ); $_obfuscate_iJWMjIiVi5OGjJOViY2Li48� = $_obfuscate_k42NkY2RkoiNjJCKlZSKiIg�; $_obfuscate_iIuQkYaUioqGlI6IjIuMiI8� = trim( $_POST['Status'] ); $_obfuscate_jpGJk5SSkJOIk4iQiI_OhpU� = trim( $_POST['Money'] ); $_obfuscate_lIuQk5OGjpKVjY6UiI_QjJM� = $_obfuscate_jpGJk5SSkJOIk4iQiI_OhpU�; $_obfuscate_iImJjYmQjYyOjIuVkIuMjIs� = trim( $_POST['VerifyString'] ); if ( $_obfuscate_iIuQkYaUioqGlI6IjIuMiI8� == "2" ) { $_obfuscate_i5CMioaGiI6ShomNiIuKjJE� = "TRADE_FINISHED"; } else { $_obfuscate_i5CMioaGiI6ShomNiIuKjJE� = "WAIT_BUYER_PAY"; } }
SQL執行時 GP
被帶入 i48
變數。
$_obfuscate_lZGQj4iOj4mTlZGNjZGUj5E� = $_obfuscate_jIaUiIeSjZWKlIqLkIqOioc�->_obfuscate_iY6OkJCRkY2PjpCPk5CRkJA�( "select * from kss_tb_order where ordernum='".$_obfuscate_iJWMjIiVi5OGjJOViY2Li48�."'" );
這裡因為沒有過濾,可以 '
閉合然後構造一個布林盲注。
Cookie構造
在使用 admin_upload.php
寫shell之前,有許可權校驗,可使用從資料庫中注出的資料,按原始碼的驗證邏輯構造出cookie,拿到admin許可權。
cookie構造邏輯在 kss_inc/db_function.php
line 300。
有兩個k-v需要構造,下圖紅框部分為注入跑出來的資料,藍框部分從原始碼配置檔案裡拿到:
webshell寫入
回到最開始提到的從遠端伺服器更新程式碼的邏輯:
$_obfuscate_koiKkIiPjI6UkYeRlIqNhoc� = _obfuscate_lY6Gk5KMkYmPjIyPhpCOlYc�( "http://api.hphu.com/import/".$_obfuscate_koaSiYqGjIqMiZSLk4uGiZU�.".php?phpver=".PHP_VERSION."&webid=".WEBID."&rid=".time( ), 300 );
URL裡面拼接的變數是外部可控的,我們在這個主站的test目錄下發現了一套Demo的CMS:
http://api.hphu.com/test/kss_admin/index.php
回想之前的SQL注入過濾機制,我們可以觸發這個機制,將php程式碼寫入http回顯,然後 admin_upload.php
通過 curl
讀內容時會將頁面中的php程式碼寫入 _webup.php
,完成webshell植入。
主站回顯構造:
Exp構造(帶上之前構造好的cookie):
最終將可控回顯寫入 _webup.php
然後通過webshell讀到C盤根目錄下的flag檔案。
- 給 @r3kapig 隊友遞茶
- 給提供思路的隊內WEB大佬遞茶 @麥香 @zzm @hear7v @lynahex @n0b0dy
- 給本題做的比我們快的 @Dubhe 和 @De1ta 兩隊WEB大佬遞茶
- 給出題人遞茶 @RicterZ