pwn.college: Debugging Refresher

gdb, gdb!
还记得大二在B站看jyy的操作系统课的时候,他用gdb浇灭了我的黑客梦。

Debugging Refresher

level1

直接run一下,再continue一下就好了,gdb的简单使用

level2

1
2
3
4
5
6
7
8
9
(gdb) p/x $r12
$2 = 0x18215a7f83c98b8e
(gdb) c
Continuing.
Random value: 0x18215a7f83c98b8e
You input: 18215a7f83c98b8e
The correct answer is: 18215a7f83c98b8e
You win! Here is your flag:
pwn.college{ETe5riLiwE1X6xz1M0yCPK4-4WH.0VN0IDLxYTN1YzW}

level3

通过在read处打断点,然后执行到read函数时,查看寄存器信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
(gdb) info registers 
rax 0x7fff17b9e998 140733591447960
rbx 0x5f31bbc71d10 104667208424720
rcx 0x3 3
rdx 0x8 8
rsi 0x7fff17b9e998 140733591447960
rdi 0x3 3
rbp 0x7fff17b9e9b0 0x7fff17b9e9b0
rsp 0x7fff17b9e968 0x7fff17b9e968
r8 0x3d 61
r9 0x2c 44
r10 0x0 0
r11 0x246 582
r12 0x5f31bbc712a0 104667208422048
r13 0x7fff17b9eaa0 140733591448224
r14 0x0 0
r15 0x0 0
rip 0x5f31bbc71210 0x5f31bbc71210 <read@plt>
eflags 0x202 [ IF ]

rsi作为read系统调用的buf参数,所以在随机数被set后,读取这个地址的值即可。(我发现不能断点给到read函数,因为从main中执行到read的时候,有其他函数会调用read,然后就会导致它read一直失败报错)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
(gdb) c
Continuing.
The random value has been set!


Program received signal SIGTRAP, Trace/breakpoint trap.
0x00005f31bbc71c64 in main ()
(gdb) x /8x 0x7fff17b9e998
0x7fff17b9e998: 0xd81c082e 0xc0e7d841 0x17b9eaa0 0x00007fff
0x7fff17b9e9a8: 0x34e94700 0xa2fdab81 0x00000000 0x00000000
(gdb) c
Continuing.
Random value: c0e7d841d81c082e
You input: c0e7d841d81c082e
The correct answer is: c0e7d841d81c082e
You win! Here is your flag:

Breakpoint 1, 0x00005f31bbc71210 in read@plt ()
(gdb) c
Continuing.
pwn.college{s102MI2GzaSB-30x2SjuMIlWzDy.0lN0IDLxYTN1YzW}


[Inferior 1 (process 4714) exited normally]

没毛病!至于为什么这个随机值是反着的,存储的是小端序,手动改成大端序才行。而且输出是以4字节为单位的,所以只需要将八字节反向一下即可。类似地,以一个字节为单位输出,也可以手动转换

1
2
3
(gdb) x /8x 0x7ffccd2aa938
0x7ffccd2aa938: 0x30 0xe5 0x4d 0x5b 0xce 0xe0 0x9a 0x51
(gdb) 519ae0ce5b4de530

**PS:**在后续的学习中,发现可以使用x/gx 以8字节为单位输出

level4

我也不知道是不是我第三关的方法用错了,第四关就是使用第三关的方法,一直重复,一直读取read系统调用时rsi寄存器的那个地址,并且在scanf系统调用前打个断点,这样方便我们读取这个随机值。重复5次还是几次就能拿到flag了。

level5

使用gdb脚本

1
2
3
4
5
6
7
8
9
10
11
start
break *main+704
commands
set $rsi_buf = $rsi
printf "buf_addr value: %llx\n", $rsi_buf
end
break *main+752
commands
set $local_variable = *(unsigned long long*)($rsi_buf)
printf "Random value: %llx\n", $local_variable
end

呃,这个脚本不是完全的自动化,但是手动复制输入给程序,重复几次后就能获得到flag了。但是实际上应该是可以做到自动化输入的,或许可以借助python脚本来写,或者直接改内存。

level6

改寄存器的值做不到哇,scanf函数是通过调用多次read系统调用,每次都只写入1个字节

我麻了,尝试那么多次。实际上应该跳过scanf,而不是重定向标准输入。只需要获取scanf所读取的内存地址,在这个地址将那个随机值放进去,然后控制rip寄存器,跳过scanf就好了。用一下exp会出现一个问题:

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
run
break *main+572
commands
silent
set $rsi_buf = $rsi
printf "buf_addr value: %llx\n", $rsi_buf
continue
end
break *main+606
commands
silent
set $local_variable = *(uint64_t*)($rsi_buf)
printf "Random value: %llx\n", $local_variable
set $rbp
set $rip=$rip + 0x18
continue
end
break *main+637
commands
ptype $rsi
ptype $local_variable
set $rsi=$local_variable
info registers
continue
end

我仅仅修改了printf的内存,导致它输出的内容是一致的,但是后续的cmp指令所比较的,还是取的rbp-0x18地址的内容,这个内容我并没有修改,所以认定为不匹配。我直接修改rbp-0x18地址的内容为这个随机值即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
run
break *main+572
commands
silent
set $rsi_buf = $rsi
continue
end
break *main+606
commands
silent
set $local_variable = *(uint64_t*)($rsi_buf)
set $rbp
set $rip=$rip + 0x18
continue
end
break *main+630
commands
silent
set *(uint64_t*)($rbp-0x10) = $local_variable
continue
end

OK,自动化成功!

level7

1
2
3
(gdb) call (void)win()
You win! Here is your flag:
pwn.college{kS6uX4HwFALgoHPYfILQokDmQwX.0FM1IDLxYTN1YzW}

gdb直接可以调用函数,其实在前面的关卡,我看到有个win函数,就修改过rip寄存器跳到win函数,直接能够获取flag。不过还是一步步做收获大些。毕竟这flag啥也不是

level8

因为程序是被损坏了,但是我认为open,read等系统调用应该都挺重要的,所以直接jump过去执行,单步执行来到write后就拿到flag了。

入口是从win+47位置开始的。


pwn.college: Debugging Refresher
https://loboq1ng.github.io/2024/08/13/pwn-college-Debugging-Refresher/
作者
Lobo Q1ng
发布于
2024年8月13日
许可协议