web1 webshell上传
前端绕过。绕了半天才想起来是前端绕过。。burp改个后缀就好了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 代码 function checkFile() { var file = document.getElementsByName('upload_file')[0].value; if (file == null || file == "") { alert("请选择要上传的文件!"); return false; } //定义允许上传的文件类型 var allow_ext = ".jpg|.png|.gif"; //提取上传文件的类型 var ext_name = file.substring(file.lastIndexOf(".")); //判断上传文件类型是否允许上传 if (allow_ext.indexOf(ext_name + "|") == -1) { var errMsg = "该文件不允许上传,请上传" + allow_ext + "类型的文件,当前文件类型为:" + ext_name; alert(errMsg); return false; } }
拿到shell 蚁剑上传 cat /flag
web3 http://172.20.2.4:9003/index.php?txt=../../../flag 任意文件读取,秒了
web7 知识点:SSTI,SSTI读配置文件,/proc目录的作用,flask session伪造
首先,尝试登录172.20.2.3:9006,登录失败,访问了一下robots.txt,发现了有报错信息,观察URL,考虑可能存在XSS,
image-20210511155722690.png 这里看了一眼中间件分析结果,发现了flask。。flask人狂喜(。
直接4
image-20210511155809022.png 懂得都懂,铁ssti了,尝试一步步rce,检测发现没开debug模式,所以只能走常规的rce利用了。
当尝试到
1 ''.__class__.__mro__[-1].__subclasses__()
时发现被ban了,单独测试了下小括号,发现是ban了小括号。既然ban了小括号,那么常规的ssti rce基本就走不通了。只能考虑读取一些敏感文件,比如config,使用payload 读取config文件,发现了 secret_key
image-20210511160014837.png
secret_key的作用是生成session,具体步骤我忘了,反正是根据secret_key可以还原和生成对应的session。
github有对应的工具,flask session manager。
接下来看了看源码, image-20210511160246157.png
一个简单的前端验证,burp抓返回包把0改成1就行,这样就顺利进入后台了。但是进入后台后提示我不是admin(2333
image-20210511160330889.png
好吧,看起来是要伪造admin的session了。现在secret_key有了,用工具先解密当前session,把用户名改成admin后再生成session。如下图所示
image-20210511160731351.png
那么当前生成的admin的session就是eyJ1c2VybmFtZSI6ImFkbWluIn0.YJo7Og.7WfYqubO5M7KZ-8IXXKIp6kMf5o,edit this cookie 换一下(我这chrome有bug,用的firefox)
image-20210511160841881.png
已经给了hint,考虑任意文件读取,首先尝试读取/etc/passwd,成功。
image-20210511161046827.png
接下来尝试读flag。直接/flag不行,关键字给过滤了。卡了好几个小时
用了挺多绕过方法 /fla? /f* 等等 但是可能中间有转义之类的,最终传到Linux层并不能识别通配符,所以卡了很长时间
最后的思路是之前比赛中读flag,过滤了很多,师傅告诉我利用/proc/目录读文件,proc目录代表了与当前运行进程有关的信息并以文件的形式展示(学习链接:https://blog.csdn.net/qq_45521281/article/details/114594402),而/proc/self目录代表了当前进程的有关信息并以文件的形式展示,需要注意的有fd、cmdline、cwd、envrion等。
/proc/self/cmdline 代表当前的命令行输入内容,我读取了之后是这个
image-20210511161330178.png
好家伙 绝对路径有了,直接读文件
/app/app_a384gh1.py
程序代码如下,基本修复了ssti->rce,然后看到了import ffffffff111llllag 文件,那直接读当前/app目录下的ffffffff111llllag.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 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 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 from flask import Flask,request,render_template_string,redirect,render_template,sessionimport randomimport stringimport base64import ffffffff111llllagapp = Flask(import_name=__name__,template_folder='templates' ,static_folder='static' ,static_url_path='/static' ) app.config['SECRET_KEY' ] ='' .join(random.sample(string.ascii_letters + string.digits, 8 )) @app.before_request def before_request (): if '/admin/' in request.path: sess_name='guest' print (session) if 'username' in session: sess_name=session['username' ] if sess_name!='admin' : return 'Your current account is ' +sess_name+' not admin' @app.after_request def makeheader (response ): response.headers["X-Powered-By" ] = "PHP/7.2.10" response.headers["Hint" ] = "Wake up Neo, the Matrix has you" response.headers['Server' ]='Apache/2.4.35 (Win64) PHP/7.2.10' return response @app.route('/' ) def redirect_2_index (): if 'username' not in session: print ('not in session!' ) session['username' ]='guest' return redirect("./index.php" , code=302 ) @app.route("/err.php" ) def err (): errorinfo=request.args.get("errorinfo" ) blacklist=["(" ,")" ] for black in blacklist: if black in errorinfo: return "You're just a dirty hacker,aren't you?" return render_template_string("Oh no,there is an Error! Error info:<p> %s" % errorinfo) @app.route("/index.php" ) def index (): return render_template("index.html" ) @app.route("/login.php" ,methods=['POST' ] ) def login (): username=request.form['username' ] password=request.form['password' ] if "'" in username or "'" in password: return "You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '''" return '0' @app.route("/admin/backendmanage.php" ) def backendmanage (): img=request.args.get("img" ) if not img: img='1.png' if "flag" in img: return "You're just a dirty hacker,aren't you?" content = '' with open (img, 'rb' ) as img_f: content = img_f.read() content = base64.b64encode(content) content='' .join([chr (i) for i in content]) return '<h1>Current Image:{img}</h1><!-- ?img=1.png --><img src="data:;base64,{content}">' .format (img=img,content=content) @app.errorhandler(Exception ) def all_exception_handler (e ): e=str (e) return redirect("/err.php?errorinfo=" +e, code=302 ) if __name__ == '__main__' : app.run(host="0.0.0.0" ,port=80 )
读取flag文件后,拿到flag。
image-20210511161628308.png
这题有几个坑点,首先是HTTP头里面的PHP HEADER 容易迷惑人,其次是登录那的sql注入容易迷惑人,最后是FLASK-SSTI的利用方式需要掌握全面:
1.直接RCE
2.开DEBUG时,SSTI 读文件->构造PIN码->RCE
3.伪造session登录
misc1 流量包没看出来啥,直接分离http对象发现了flag.php,压缩包被破坏了,修复下头
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 JPEG (jpg), 文件头:FFD8FF 文件尾:FF D9 PNG (png), 文件头:89504E47 文件尾:AE 42 60 82 GIF (gif), 文件头:47494638 文件尾:00 3B ZIP Archive (zip), 文件头:504B0304 文件尾:50 4B TIFF (tif), 文件头:49492A00 文件尾: Windows Bitmap (bmp), 文件头:424D 文件尾: CAD (dwg), 文件头:41433130 文件尾: Adobe Photoshop (psd), 文件头:38425053 文件尾: Rich Text Format (rtf), 文件头:7B5C727466 文件尾: XML (xml), 文件头:3C3F786D6C 文件尾: HTML (html), 文件头:68746D6C3E Email [thorough only] (eml), 文件头:44656C69766572792D646174653A Outlook Express (dbx), 文件头:CFAD12FEC5FD746F Outlook (pst), 文件头:2142444E MS Word/Excel (xls.or.doc), 文件头:D0CF11E0 MS Access (mdb), 文件头:5374616E64617264204A WordPerfect (wpd), 文件头:FF575043 Adobe Acrobat (pdf), 文件头:255044462D312E Quicken (qdf), 文件头:AC9EBD8F Windows Password (pwl), 文件头:E3828596 RAR Archive (rar), 文件头:52617221 Wave (wav), 文件头:57415645 AVI (avi), 文件头:41564920 Real Audio (ram), 文件头:2E7261FD Real Media (rm), 文件头:2E524D46 MPEG (mpg), 文件头:000001BA MPEG (mpg), 文件头:000001B3 Quicktime (mov), 文件头:6D6F6F76 Windows Media (asf), 文件头:3026B2758E66CF11 MIDI (mid), 文件头:4D546864
image-20210512132636816.png
提取rar
image-20210512132807911.png
十六进制转ascii
flag{My_Name_is_AoBai}
misc2 zip文件头修复
得到密文5a6e4665536e506248206579666b7b39733930733833742d393637312d3433626a2d616f69302d3235663176393138707030377d
十六进制转ascii
ZnFeSnPbH eyfk{9s90s83t-9671-43bj-aoi0-25f1v918pp07}
维吉尼亚解密(captfencoder1.x版本 维吉尼亚解密有bug)
flag{9a90f83e-9671-43ac-bbd0-25b1d918ca07}
misc 3 压缩包头部修复,修改错了的1位
然后需要密码,直接binwalk分离出来一个Linux可执行文件,到这里卡住了。。然后后面看了看文件头应该是个png,zhiweilai加上png的为文件头后发现图片宽高有问题,Linux打不开,用脚本爆破宽高修改即可。
脚本如下
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 import zlibimport structimport sysimport binasciifilename = sys.argv[1 ] with open (filename, 'rb' ) as f: all_b = f.read() crc32key = int (all_b[29 :33 ].hex (),16 ) data = bytearray (all_b[12 :29 ]) 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 = zlib.crc32(data) if crc32result == crc32key: print ("宽为:" ,end="" ) width1=binascii.b2a_hex(width) width1=width1.decode('utf-8' ) width1=int (width1,16 ) print (width,width1) print ("高为:" ,end="" ) height1=binascii.b2a_hex(height) height1=height1.decode('utf-8' ) height1=int (height1,16 ) print (height,height1) exit(0 )
在对应的位置修改图片宽高的十六进制值即可。
image-20210512175308222.png