CISCN2024 华南分区赛 awd


不好评价,支持绿盟

赛前

第一次awd,还想着怎么打呢。晚上讨论的时候是想着肯定是要先fix的,然后队长给了个更好用的python脚本批量打全场。我猜测第二天的比赛是一个php一个java,所以赛前连夜学了一下怎么修java的靶机,希望能派上用场。加之赛前给一队写了个勉强能用的摆烂php日志包,用于生成类似burp的访问记录的

然后连夜装了个burp拓展,这还挺好用,一键复制到python就能够用。总之就是使用burpsuitecopy as python requests的插件自动化提取payload

另外俩队友在看怎么修pwn+怎么上pwn的流量监控

misc队友起了个聊天室+tulip流量分析,聊天室docker compose一下就能搭。

但是流量分析还得看有无tcpdump,如果有就皆大欢喜

当天

开始抽象起来了,靶机和我想得一样,一个php一个java

ssh靶机全是弱密码

1
2
username: ctf
password: ctf@123

所以当务之急就是立刻连上ssh然后快速改密码

还好有bitwarden,在比赛开始之前就生成了十个强密码发到聊天室,因为我们想着哪怕密码够强,也有可能会每个队伍的密码都是相同的

结果不仅是相同的,而且还是这么弱的,一下就能批量改ssh密码

php重置的很顺利,java一不小心重置密码的密码忘了,给我整懵逼了。还好能够重置ssh并且不扣分。第二次就改好了,也算是个小插曲。

php

php是一个极致cms,存在有sql数据库+redis:

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
//Conf config.php
<?php return array (
'db' =>
array (
'host' => '127.0.0.1',
'dbname' => 'jizhicms6903',
'username' => 'root',
'password' => 'admin123!',
'prefix' => 'jz_',
'port' => '3306',
),
'redis' =>
array (
'SAVE_HANDLE' => 'Redis',
'HOST' => '127.0.0.1',
'PORT' => 6379,
'AUTH' => NULL,
'TIMEOUT' => 0,
'RESERVED' => NULL,
'RETRY_INTERVAL' => 100,
'RECONNECT' => false,
'EXPIRE' => 1800,
),
'APP_DEBUG' => true,
); ?>

可以看到mysql也是个弱密码,但是靶机没有提供mysql,给我干的有点小脑萎缩,就没仔细去看,也没想到好像可以用物理机连上去

登上去改个密码就好

后台,在readme.md里有写:

1
演示:[demo.jizhicms.cn](http://demo.jizhicms.cn)   后台:[http://demo.jizhicms.cn/admin.php](http://demo.jizhicms.cn/admin.php) [账户:test,密码:123456]

登录以后把密码改了即可

D盾,扫到Home/template/default/adminpanel.php是个后门:

直接注释掉即可

其他地方就没有扫到什么了

赛后查到可以在后台传shell:

反正把后台密码+mysql密码直接先防就行了,实在不放心就看这个文章里的办法,在CommonController.php内加黑名单

其实原本就有php的限制,所以照着php这里加就好了

批量,先打adminpanelcontroller,burp直接复制下来就能打

后台传shell的洞需要多几步操作,大同小异,都是burp抓包操作一下,然后复制到python里批量就好,然后场上事故太多了,就懒得去打web了,纯纯靠pwn拿分了,what can i say

web1

java靶机,和ciscn初赛的靶机一模一样,jdbc打sqlite,唯一不同的地方就是从com.example换成了org.example.ezawd,没绷住

都知道原理了,修起来还是很简单的

  • 把load_extension关了。但是这个方法没啥用,可以通过下面这条payload进行加载:jdbc:sqlite:file:/tmp/sqlite-jdbc-tmp-hashcode.db?enable_load_extension=true。那这样岂不是跟mysql一样随便打了吗
  • SqliteDatasourceConnector.java里加个黑名单:
1
2
3
if(url.contains("resource") || url.contains("file")){
throw new UnsupportedOperationException("Don't hack me");
}

打包jar的时候要注意SqliteDataSourceConnectorDataSourceConnector要一起打包,还有就是SqliteDataSourceConnector反编译出来会有一些不明意义的符号,所以得自己看一下那些var表示的啥

还好留着国赛初赛的附件,那个时候反编译的文件是正常的,我直接复制粘贴来用了,修完的时候大概是这样的(可以看到注释我都没改):

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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
package org.example.ezawd.utils.sql;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import org.sqlite.SQLiteConfig;

/* loaded from: app.jar:BOOT-INF/classes/com/example/jdbctest/utils/sql/SqliteDatasourceConnector.class */
public class SqliteDatasourceConnector implements DatasourceConnector {
private Connection connection;

public SqliteDatasourceConnector(String url) throws ClassNotFoundException, SQLException {
if(url.contains("resource") || url.contains("file")){
throw new UnsupportedOperationException("Don't hack me");
}
Class.forName("org.sqlite.JDBC");
SQLiteConfig config = new SQLiteConfig();
config.enableLoadExtension(false);
this.connection = DriverManager.getConnection(url, config.toProperties());
this.connection.setAutoCommit(true);
}

/* JADX WARN: Finally extract failed */
@Override // com.example.jdbctest.utils.sql.DatasourceConnector
public String[] getTables() {
List<String> tableNames = new ArrayList<>();
try {
Statement statement = this.connection.createStatement();
ResultSet resultSet = statement.executeQuery("SELECT name FROM sqlite_master WHERE type= 'table'");
Throwable th = null;
while (resultSet.next()) {
try {
try {
tableNames.add(resultSet.getString("name"));
} finally {
}
} catch (Throwable th2) {
if (resultSet != null) {
if (th != null) {
try {
resultSet.close();
} catch (Throwable th3) {
th.addSuppressed(th3);
}
} else {
resultSet.close();
}
}
throw th2;
}
}
if (resultSet != null) {
if (0 != 0) {
try {
resultSet.close();
} catch (Throwable th4) {
th.addSuppressed(th4);
}
} else {
resultSet.close();
}
}
if (statement != null) {
if (0 != 0) {
statement.close();
} else {
statement.close();
}
}
} catch (SQLException e) {
e.printStackTrace();
}
return (String[]) tableNames.toArray(new String[0]);
}

/* JADX WARN: Finally extract failed */
@Override // com.example.jdbctest.utils.sql.DatasourceConnector
public String[] getTableContent(String tableName) {
String sql = "select * from " + tableName;
try {
Statement statement = this.connection.createStatement();
ResultSet resultSet = statement.executeQuery(sql);
Throwable th = null;
while (resultSet.next()) {
try {
try {
} finally {
}
} catch (Throwable th2) {
if (resultSet != null) {
if (th != null) {
try {
resultSet.close();
} catch (Throwable th3) {
th.addSuppressed(th3);
}
} else {
resultSet.close();
}
}
throw th2;
}
}
if (resultSet != null) {
if (0 != 0) {
try {
resultSet.close();
} catch (Throwable th4) {
th.addSuppressed(th4);
}
} else {
resultSet.close();
}
}
if (statement != null) {
if (0 != 0) {
statement.close();
} else {
statement.close();
}
}
} catch (SQLException e) {
e.printStackTrace();
}
return new String[0];
}
}

修复前:

修复后:

批量:

批量不了吧这玩意,本地能反弹shell成功,但是远程环境无法反弹shell。通过debug得到的hashcode和tmp下的hashcode完全不一样,加载不了恶意so,抽象。

总之就是打别人没测成功,打自己完全ok。忘记看自己反弹shell过来是啥权限了(为什么要看后续会说),草,想起来就想抽自己两巴掌怎么啥都没干。

正片

讲完怎么修和打,终于正片开始了。

比赛开始狂fix,fix到一半就发现不对劲了,php还算正常,刚开始没权限下源码,后续得进到html文件夹才能够复制源码下来。java下下来了,反编译一看这不是原题吗,想着赶紧修好赶紧attack,刚好博客传了ciscn的web题解,然后直接起了个本地服务(hexo魅力时刻)照着打。attack传上去的db就不对劲了,怎么和debug的完全不一样,我靠只能一个个对着找,找到对应的so了,第一次弹shell没弹成,发现没bash,改成sh以后就行了,但是随便找了个ip照着刚刚的步骤打完全弹不了shell。

attack是后话了,想着重启一下java服务然后赶紧修复,发现他用的root权限起的java服务,我一个ctf用户甚至杀不掉它的进程,妈的服了。然后工作人员一直在说检查权限问题,甚至重置了两次环境,逆天,然后每次重置还得重新修php的机子。最后都到饭点了,还是加固环节。

最后直接说修不好java和pwn的权限,只能打不能修,没绷住。

然后接下来就是更加抽象的过程,后面ssh服务直接烂了,靶机直接失联,php只能够勉强的断断续续连上,然后就又断,又连又断,java的ssh直接全场崩溃。然后web手只能纯坐牢,看着这个连不上的ssh服务发呆。

php、pwn、java的服务全是root起的,被人rce完了能直接吧全场的ssh都关了+改ssh密码

一队php还在加固环节就被人种马了,别的师傅甚至到了后面连php都修不上,到了最后一轮都没修好,这不是靶机的问题是啥问题,比赛开始之前群里就有师傅发了被攻击的截图,主办方也不管

用户的权限更是低的一坨,连tmp文件夹下的东西都删不掉

总结就是你以为这是awd,其实这是ctf pwn,web php就那几个洞,修完了就没得打,java不太清楚反弹不了shell怎么打,听说要打内存马,但是不太懂so怎么能加载java的内存马。但是就是有人打出来了,T_T。所以这两个基本上纯坐牢,只能靠pwn来打,只要能批量就是200分起步,因为修不了pwn的机子,所以直接白给

所以这里就是自己的问题了,自己应该要反弹shell以后直接把java服务关了再重启修复的,按道理反弹shell之后我是root权限,当时忘记了。应该是觉得很激动,我能打全场了导致的,反正就是没有想到那么多,唉。

自己问题还挺多的,但是到后面都被恶心坏了,不想打了,索性直接玩杂交版去了,只能说傻逼东西。