Jump to content

router telnet access


haicen
 Share

Recommended Posts

Apologies for the duplicate post. I originally posted this under hacks and mods, but realized that wasn't the right place.



I have a Belkin N150 router, which has a few known vulnerabilities. https://www.exploit-...exploits/38840/



Based on the vulnerabilities listed, the best option seems to be the root telnet access. The method works, and a root shell is obtained. I am able to view directories and execute commands. The router itself runs a version of busybox. I understand everything up to this point, but I'm not sure where to go from here. I would like to be able to either obtain the admin page password or be able to reset the password to the default. I am at an utter loss as to how to accomplish this task. The admin web app relies heavily on javascript and a cgi-bin script. I think the cgi-bin script handles all of the authentication through a JSON string.




My attempts to recover the password so far have been attempting to decompile the cgi-bin script using recstudio, but i can only get what looks like assembly code, which i can't read. I have also tried using hydra to brute force the password, but I can't seem to get the parameters correct. I don't know if hydra will even work on this web page since the http-get parameters are encoded in base64 and sent directly to the cgi script. I ran the cgi binary through strings, but didn't find anything that jumped out at me.



I don't see any shell scripts or commands that could be used to reset the password via telnet.



Any help or suggestions are very appreciated.



I can provide a copy of the firmware if that is helpful.


Link to comment
Share on other sites

press the little red reset button on the machine...

As for a brute force. Start up wireshark and attempt a login, copy the POST request and paste here... you can also record the same login attempt for telnet...root:root

there is alot more to a brute force then just starting up hydra...

Those exploits don't seem useful in your situation

Edited by i8igmac
Link to comment
Share on other sites

Pretty amazing yea haicen? I mean you have root access, but yet you still feel somewhat powerless in terms of resetting or grabbing the password for the administration web interface! l8igmac will help you with what he said, and I'll see about reverse engineering that C cgi-bin binary. That is if it is a cgi-bin binary written in C. cgi-bin's can also be scripts like in perl (.pl files) but in this case it does appear to be a C binary instead as you've mentioned.

You're going to have to figure out how the password is being stored and retrieved to check against. Could it be stored in an sql database for example and the binary grabs it from there or maybe a hashed version of the password. So are there actual web page files or does the router itself actually just return html back from requests it gets?

You say it does an http POST/GET(you didn't clarify but I assume it's a POST) with the password passed to a cgi-bin C binary. In a C compiled version of a cgi-bin there are two ways that it uses to get the POST or GET data. In a GET request received a "getenv("QUERY_STRING");" (function of stdlib) retrieves the passed string and in the case of a POST request received, getenv("CONTENT_LENGTH"); retrieves the length of the posted data, and stdin (standard input) will get the posted data!

So in either case looking for the function "getenv" being called in the disassembled code is a good starting point of seeing where it's initially taking the posted data and following what it does with it. (since it has to base64 decode the input and then most likely hash the passed in password and get the stored hashed admin password to compare it to). If and since it does seem to be an older router it might have the password stored in plaintext even.

It may even be possible to make the admin control panel accept any password at all by patching only 1 single byte of the binary, but that might be a little dangerous to play with, but as long as you back up the original binary you should always still be able to telnet back over the original and everything will be back to normal.

You said you can upload the firmware if that's helpful sure that indeed might be, but I'd be more interested in the cgi-bin binary and having a look at that if you would upload that. (since I have an idea of where to start looking) :smile:

EDIT: Also I just thought of another way you should be able to determine where the password for the admin cp is being stored. -> Do a CRC or MD5 of every file you have access to (since you're root pretty much everything) then quickly change or reset the password (just changing it is probably better unless you start with a resetted and only password changed device, since resetting is also going to change other things back to default) then CRC or MD5 everything again and check for which files have changed and what is still the same. Probably less will be going on if it isn't connected to the internet and routing packets as well so that would probably be the better time to try this. You'll get some files that happened to change but you should also be able to determine which file contains the password!

What kind of architecture is the router? x86 or ARM or etc? I did a brief search but couldn't determine what your router's architecture actually is. For the above mentioned you'll need either a bash script or perl script or compiled binary for that architecture with a way to CRC or MD5 complete files of all files in "/" and enough space to hold those CRC's temporarily to compare against the new CRCs once the password has been changed.

It shouldn't take up much space for that, but I know routers like yours can be very limited in memory and available space and might already be packed pretty tightly. In which case it would benefit to not store the hashes on the router but send them over the telnet connection or another port to be stored and compared against on your end (basically like a DIFF)

Edited by AlfAlfa
Link to comment
Share on other sites

Since I'm a new user, I can only make one more post until later today, so I will have to lump everything in this reply.

Here is a link to my dropbox folder with the cgi binary, embedded file system (not exact copy, but same version from mfg), and some output and http requests. https://www.dropbox.com/sh/lso259hi7nmhkp9/AAAWyxx5hKOwUOLQjx9GGcTIa?dl=0

press the little red reset button on the machine...

As for a brute force. Start up wireshark and attempt a login, copy the POST request and paste here... you can also record the same login attempt for telnet...root:root

there is alot more to a brute force then just starting up hydra...

Those exploits don't seem useful in your situation

While I could reset it that way, I am attempting to simulate a scenario where I have gained access to the router and would like to make changes to the router's configuration. Enable guest network, connect to a transparent proxy, etc. Its more for my own education than anything else.

I've included the http-post request with the correct password below. The password is "secret". As you can see, the password is encoded in base64 by the client before it is sent. Maybe there's an option to do that in hydra, but I didn't find one.

POST /cgi-bin/webproc?getpage=html/page/loginajax.js&var:page=*&timestamp=1455458360087 HTTP/1.1 (application/x-www-form-urlencoded)

Frame 148: 917 bytes on wire (7336 bits), 917 bytes captured (7336 bits) on interface 0
Ethernet II, Src: AskeyCom_23:5c:02 (00:21:63:23:5c:02), Dst: BelkinIn_ed:88:98 (94:10:3e:ed:88:98)
Internet Protocol Version 4, Src: 192.168.2.9 (192.168.2.9), Dst: 192.168.2.1 (192.168.2.1)
Transmission Control Protocol, Src Port: 54611 (54611), Dst Port: 80 (80), Seq: 1, Ack: 1, Len: 851
Hypertext Transfer Protocol
HTML Form URL Encoded: application/x-www-form-urlencoded
Form item: "var:login" = "true"
Form item: "obj-action" = "auth"
Form item: ":username" = "admin"
Form item: ":password" = "c2VjcmV0"
Form item: ":hostname" = "a2FsaQ=="
Form item: ":action" = "login"
Form item: ":ip" = "192.168.2.9"
Form item: ":sessionid" = "54452c2d"

Pretty amazing yea haicen? I mean you have root access, but yet you still feel somewhat powerless in terms of resetting or grabbing the password for the administration web interface! l8igmac will help you with what he said, and I'll see about reverse engineering that C cgi-bin binary. That is if it is a cgi-bin binary written in C. cgi-bin's can also be scripts like in perl (.pl files) but in this case it does appear to be a C binary instead as you've mentioned.

You're going to have to figure out how the password is being stored and retrieved to check against. Could it be stored in an sql database for example and the binary grabs it from there or maybe a hashed version of the password. So are there actual web page files or does the router itself actually just return html back from requests it gets?

You say it does an http POST/GET(you didn't clarify but I assume it's a POST) with the password passed to a cgi-bin C binary. In a C compiled version of a cgi-bin there are two ways that it uses to get the POST or GET data. In a GET request received a "getenv("QUERY_STRING");" (function of stdlib) retrieves the passed string and in the case of a POST request received, getenv("CONTENT_LENGTH"); retrieves the length of the posted data, and stdin (standard input) will get the posted data!

So in either case looking for the function "getenv" being called in the disassembled code is a good starting point of seeing where it's initially taking the posted data and following what it does with it. (since it has to base64 decode the input and then most likely hash the passed in password and get the stored hashed admin password to compare it to). If and since it does seem to be an older router it might have the password stored in plaintext even.

It may even be possible to make the admin control panel accept any password at all by patching only 1 single byte of the binary, but that might be a little dangerous to play with, but as long as you back up the original binary you should always still be able to telnet back over the original and everything will be back to normal.

You said you can upload the firmware if that's helpful sure that indeed might be, but I'd be more interested in the cgi-bin binary and having a look at that if you would upload that. (since I have an idea of where to start looking) :smile:

EDIT: Also I just thought of another way you should be able to determine where the password for the admin cp is being stored. -> Do a CRC or MD5 of every file you have access to (since you're root pretty much everything) then quickly change or reset the password (just changing it is probably better unless you start with a resetted and only password changed device, since resetting is also going to change other things back to default) then CRC or MD5 everything again and check for which files have changed and what is still the same. Probably less will be going on if it isn't connected to the internet and routing packets as well so that would probably be the better time to try this. You'll get some files that happened to change but you should also be able to determine which file contains the password!

What kind of architecture is the router? x86 or ARM or etc? I did a brief search but couldn't determine what your router's architecture actually is. For the above mentioned you'll need either a bash script or perl script or compiled binary for that architecture with a way to CRC or MD5 complete files of all files in "/" and enough space to hold those CRC's temporarily to compare against the new CRCs once the password has been changed.

It shouldn't take up much space for that, but I know routers like yours can be very limited in memory and available space and might already be packed pretty tightly. In which case it would benefit to not store the hashes on the router but send them over the telnet connection or another port to be stored and compared against on your end (basically like a DIFF)

This has been very frustrating. I have the keys to the kingdom so to speak, but there is nothing here. Most of the file system is write protected except a few directories. I may try the md5 checksum idea manually after I determine which ones are writable.

I agree that it is most likely a C binary. I found a file called "My_getenv" in the disassembly from RecStudio. Unfortunately I don't understand what the file does. I see some if/else statements, an infinite while loop with breaks and goto's, but none of it means anything to me. My programming knowledge is heavy on python and ultralight on C. I don't see any references to sql in the file system. I uploaded the binary. It's in the dropbox folder and the file is called "webproc"

I have no idea what sort of architecture it has, nor really any idea on how to figure that out. I don't think perl is installed on the system. I didn't see perl in /sbin or /bin, so I think it would have to be a compiled binary (which I don't have the knowledge to do currently).

Is it just the challenge of getting the password for you or are you trying to do something else once you have it?

It is the challenge of getting the password. I don't use the router for anything, and Belkin tends to be lacking in terms of security. Other than the unsecured telnet interface, it appears that this router is somewhat secure.

Link to comment
Share on other sites

can I ask you to post only the raw data. Exclude all the junk wireshark includes...

the session id can be a little tricky, it may need to be revalidated after every attempt...

how does the server respond when a successful login happens? 302 ok?

how does the server respond when I fail login happens?403 forbidden?

how does the web server respond when you fail to produce a proper post requests?400 bad request?

there may also be firewall rules! when so many failed login attempts happen, the web server may change how it responds to successful login! Meaning that even though you have supplied the correct creds, the server responds with a 403..

I have never seen a router have any kind of firewall rules to protect any of its services, at least none that exist in peoples homes...

I had a project that I scrapped all the default router user:passwords combinat into a word list... I'm not sure where I placed it...

list all the ports, telnet maybe easier to brute..

Edited by i8igmac
Link to comment
Share on other sites

I have uploaded the wireshark packet capture to the same dropbox folder as router.pcapng. https://www.dropbox.com/s/kcq65ita76a7oo8/RequestPassword.txt?dl=0

Frame 138 is a sending a wrong password. Number 146 is what I assume to be the response, which is 200-ok.

Frame 189 is is a the correct password.

I have wordlists, thats not an issue. I don't need to brute force telnet either. I already have access to that since it is by default set to root:root. The problem is that I don't understand what I can do now that I have that access to be able to determine the password.

Link to comment
Share on other sites

On another note I figured out what the architecture is :smile:

Alf@UNKNOWN:~/Downloads/PenTest$ readelf -a -W webproc
ELF Header:
  Magic:   7f 45 4c 46 01 02 01 00 00 00 00 00 00 00 00 00
  Class:                             ELF32
  Data:                              2's complement, big endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           MIPS R3000
  Version:                           0x1
  Entry point address:               0x401b70
  Start of program headers:          52 (bytes into file)
  Start of section headers:          0 (bytes into file)
  Flags:                             0x1007, noreorder, pic, cpic, o32, mips1
  Size of this header:               52 (bytes)
  Size of program headers:           32 (bytes)
  Number of program headers:         7
  Size of section headers:           0 (bytes)
  Number of section headers:         0
  Section header string table index: 0

A MIPS R3000 big endian and an ELF32 binary! MIPS I haven't reverse engineered before, but this should be fun! Of course it wasn't going to be x86 or ARM but I now at least know how I need to look at it as! I also determined that your disassembled output was just of one function not the whole executable and it looks more like it tried to decompile it rather than disassemble so it's more like pseudo code. I'm at least looking at the actual MIPS code now!

Link to comment
Share on other sites

On another note I figured out what the architecture is :smile:

Alf@UNKNOWN:~/Downloads/PenTest$ readelf -a -W webproc
ELF Header:
  Magic:   7f 45 4c 46 01 02 01 00 00 00 00 00 00 00 00 00
  Class:                             ELF32
  Data:                              2's complement, big endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           MIPS R3000
  Version:                           0x1
  Entry point address:               0x401b70
  Start of program headers:          52 (bytes into file)
  Start of section headers:          0 (bytes into file)
  Flags:                             0x1007, noreorder, pic, cpic, o32, mips1
  Size of this header:               52 (bytes)
  Size of program headers:           32 (bytes)
  Number of program headers:         7
  Size of section headers:           0 (bytes)
  Number of section headers:         0
  Section header string table index: 0

A MIPS R3000 big endian and an ELF32 binary! MIPS I haven't reverse engineered before, but this should be fun! Of course it wasn't going to be x86 or ARM but I now at least know how I need to look at it as! I also determined that your disassembled output was just of one function not the whole executable and it looks more like it tried to decompile it rather than disassemble so it's more like pseudo code. I'm at least looking at the actual MIPS code now!

What tool did you use to look at the code? Is it the same readelf command? I'd be interested to see what it looks like.

Link to comment
Share on other sites

What tool did you use to look at the code? Is it the same readelf command? I'd be interested to see what it looks like.

Not exactly... That helped me figure out the architecture but it doesn't disassemble (I don't think, at least not mips) and objdump will disassemble but it doesn't support mips either.

First I found this online tool similar to what you used call Retargetable Decompiler: https://retdec.com/decompilation/it disassembles as well as trying to provide a C or Python source code which isn't perfect but does help to find your way around in the disassembled mips code, and even gives you nice graphs of the code flow.

I did see where it does in fact call the getenv function with the CONTENT_LENGTH string (which is contained inside it like I thought it would be) Then I found what I think is what determines if you typed the correct password or not. It's called "CheckAuth" :)

However with only static analysis and not being able to debug, I had to do my best to create a couple "best shot at it" patches:

I'll get to that in a minute, I should say that I think I found where the router stores the administration panel password and it appears to be hashed, along with where the wpa passphrase, wep key (if used) and WPS pin are also stored along with all the other router settings. It's in /etc/config.xml and /etc/config_full.xml the two settings / configuration files!

Alf@UNKNOWN:~/Downloads/PenTest/FileSystem$ cat etc/config_full.xml | grep KeyPassphrase
                                    <KeyPassphrase t="s"></KeyPassphrase>
                    <X_TWSZ-COM_PSKExpression t="s" >KeyPassphrase</X_TWSZ-COM_PSKExpression>
                    <KeyPassphrase t="s"></KeyPassphrase>
                            <KeyPassphrase t="s">wpatestpass</KeyPassphrase>

Alf@UNKNOWN:~/Downloads/PenTest/FileSystem$ cat etc/config.xml | grep Password
                        <DevicePassword t="s">12345670</DevicePassword>
                        <X_TWSZ-COM_PeerPassword t="s"  Stat="1"></X_TWSZ-COM_PeerPassword>
                            <WEPKey    t="s" Password="2">1111111111</WEPKey>
                            <WEPKey    t="s" Password="2">2222222222</WEPKey>
                            <WEPKey    t="s" Password="2">3333333333</WEPKey>
                            <WEPKey    t="s" Password="2">4444444444</WEPKey>
                            <PreSharedKey t="s"    Password="2"></PreSharedKey>
                            <KeyPassphrase t="s" Password="2"></KeyPassphrase>
                <WEPKey    t="s" Password="2"></WEPKey>
                <Passphrase t="s" Password="2"></Passphrase>
                <Password t="s"     >$1$TW$W4EX5n8uMLE15bpd62uqD.</Password>
                <Password t="s"     >$1$TW$3q69ksdrX7zaaLg54vFxN0</Password>
        <SmtpPassword t="s"  ></SmtpPassword>
                <Password t="s">Pass</Password>
        <Password t="s"></Password>
Alf@UNKNOWN:~/Downloads/PenTest/FileSystem$

wpatestpass was your wpa test passphrase wasn't it?

DevicePassword is in both configs and is the WPS pin, so both would probably have to be changed if editing them manually.

Finally after the WEP keys the Password fields containing $1$TW$ are the 'admin' and 'user' passwords, did you know there was even a login for 'user'? seems like a backdoor perhaps but maybe with not as many permissions as the admin login. The first one is for admin, the second for user.

$1$TW$ seems to be a constant, probably because it's called a TWSZ-COM so that's where I seee the TW coming from. It seems to be a Chinese device as in some areas in the filesystem there's plenty of Chinese.

I also realized you could've probably done the CRCs/MD5s not on the actual device itself, but pulling the filesystem running the CRCs, then changing the administration password, pulling it again and CRCing again! That would've made that easier than trying to do it on the device, but if this is where the passwords are stored as it appears so, that won't be necessary anymore!

I'm not sure how that's encoded / encrypted though and you might want to change the password and check that admin password again and see if it indeed changes like I think it will.

Now let's check out some mips code, the C pseudo code is nice to look at to get some bearings, but we'll have to actually patch the mips machine code in order to change it's behavior. My goal was to try my best using just static analysis to figure out where to patch in order to make it so you can enter any admin password and it will be accepted. I take no responsibility if you end up bricking your router, however I think the webproc process isn't vital to the routers functioning and you should be able to just telnet back over the unpatched / original binary if anything goes wrong and you're not able to access the admin panel anymore. (that should be the only side effect, if even that) I also found a place where other services could be enabled like ftp but they were commented out and maybe not truly available on the device. The telnet probably they forgot to remove it as they were still using it when last working on it, unlike the other ones.

Some C pseudo code of the main function where the CheckAuth function is called:

/ Address range: 0x401d90 - 0x4024ab
int main(int argc, char ** argv) {
    <-- snip
    -->
    mallopt(-3, 0x1000);
    MSG_RegModule(769);
    unknown_401de0();
    int32_t v59; // 0x454ce0
    int32_t v60 = v59;
    MSG_AllModStartOK(1);
    int32_t v61 = v59;
    srand(time(NULL)); // <-- seeding the pseudo random number generator I see with the time
    rand();
    rand();
    int32_t v62;
    int32_t v63; // 0x454e18
    int32_t v64;
    OM_ValSet((int32_t *)v63, v61 - 0x2df4, My_getenv((char *)(v60 - 0x2e00)), v62, v64, 0); <-- where it calls their custom version of getenv (your decompilation attempt only did that function)
    int32_t v65 = v59 - 0x2dcc; // 0x401ec4
    OM_ValSet((int32_t *)v63, v59 - 0x2de0, v65, v62, v64, 0);
    int32_t v66 = WEB_GetEnv(); // 0x401edc
    int32_t v67 = v66;
    int32_t v68 = -1;
    int32_t v69; // 0x402438
    int32_t v70; // 0x402458
    int32_t v71; // 0x454eb4
    if (v66 == -1) {
        // 0x402438
        ((int32_t (*)())v69)();
        ((int32_t (*)(struct struct__IO_FILE *))v71)((struct struct__IO_FILE *)v70);
        // branch -> 0x402464
    } else {
        // 0x401ef4
        int32_t v72; // 0x454e60
        OM_ValGet((int32_t *)v72, v59 - 0x2db4, v65);
        int32_t v73;
        int32_t * v74 = (int32_t *)(v73 + 0x52f50); // 0x401f64_0
        int32_t * v75 = (int32_t *)(v73 + 0x53088); // 0x401f6c_0
        int32_t v76; // 0x401f68
        ((int32_t (*)())v76)();
        int32_t v77;
        OM_ValGet((int32_t *)v63, v61 - 0x2df4, v77);
        int32_t v78; // 0x454ed8
        OM_ValFind((struct struct_15 *)v78, v59 - 0x2d8c, v77);
        int32_t v79 = v68; // 0x401fc0
        v77 = v79;
        CheckAuth((char *)0, (char *)v60, v79, v62); // <-- This looks interesting

So after perusing that pseudo C code generated by retdec, and the actual disassembled mips code provided by it as well. I also was looking for an offline disassembler for mips, and it turns out kali already comes with one called radare2 / rasm2!

I use it like this: (-e for big endian, -b 32 for 32 bit code, -a mips for mips architecture and then the binary file to work with)

radare2 -e -b 32 -a mips webproc

Then you can use pd [length] (optional length) to print out the disassembled code in a nice color coded format that even shows the relative jumps.

or 's [address]' ex. 's 0x401fcc' where 0x401fcc is the place I believe might be what I've tried to accomplish.

ECrsxDe.png

You can also see one of the nice graphs that shows how the main function links to the others, and most things seem to branch out from the CheckAuth function so it's definitely important.

This is my first patch:

04ozjj5.png

This is right after the CheckAuth function is called which is at 0x401fc0... Just after returning from that function is makes a branch decision based on whether the return value is zero (which is usually success, and I believe that to be the case as it seemed to return -1 or a higher than 0 value in most returns from CheckAuth and only returned 0 in one place I believe [which should mean a successful authentication])

The instruction I patched: beqz v0, 0x402068 (branch if equal to zero [if v0 is equal to zero])

So here was the plan, currently it checks if v0 (the return value from CheckAuth) is 0 and if it is zero and only zero it follows the branch and goes to what I think is the successful login code. What i've done with my patch is to reverse / inverse the branch and made it:

bnez v0, 0x402068 (branch if not equal to zero [if v0 is anything else but zero])

In theory if this works, any admin password that IS NOT the correct password should be a successful login. Funnily enough if it works, the actual correct password would be the only thing that would give you an unsuccessful login! lol hows that for a reversal.

If I had the router for example, I would keep making patches and test until I got the result I was looking for. Also I would try to get a debugger going, so I could actually see how the program is behaving while it's executing which would help tremendously at finding the results I'm after!

I used a simple hex editor once I knew what I wanted to patch, to find the bytes within the file to patch and then modifying them with the new instruction(s). I used rasm2 and online assemblers in conjunction along with a MIPS instruction set reference guide in order to what bytes to replace the original bytes with to create the valid instructions I wanted. Most machine code is relatively simple, move / load instructions, branch instructions / jump instructions, and stack manipulating instructions.

This next patch is a bit more of a gamble, instead of changing that branch instruction after CheckAuth is called, I instead went into the CheckAuth function and made it immediately before doing anything setting the return value (v0) to 0 and then just return. This is a bit more risky, because the things that happen in the CheckAuth function might be more important then just returning successful or not, and it may set some other things up but I thought it might still be worth a shot since I'm just using static analysis and can't debug.

The first one almost for sure won't have any real bad side effects, it'll either do what I hope it will or just seem like nothing's different or have a hidden side effect that'll be unnoticeable. This second one might have a more noticeable side effect if it doesn't work. However I don't think either of them should crash per say since I did use valid instructions that replaced the original instructions!

So I recommend trying the first one, branch inversed first if you're going to try these! Also when you do make sure you type an incorrect password and see if it accepts it, then type the correct password and see if it accepts it. With the second patch: set return value and just return, I may have it backwards and it might make all passwords not be accepted. The first patch: branch inverse, however should just do something complete opposite of normally, like successful logging in with an incorrect password and failing to login with a correct password.

i4MZg4t.png

tWAAXPf.png

Here's the disassembled and pseudo decompiled output I got from retdec, and the two patched versions if you're willing to try them and remember I recommend the branch invert one first if you are, you'll have to rename them from 'webproc-inversed-branch' and 'webproc-patched-justreturnzero' to 'webproc' the original and actual name! Also you can only try one at a time just to be clear as it will replace the current original webproc binary.

In the inverted branch patch it ends up being like I said a SINGLE BYTE PATCH :) funny how I knew that in my post earlier and the the second just return zero patch is an 8 byte patch, two instructions overwriting the beginning of the CheckAuth function!

Link: http://www.speedyshare.com/H7UYA/webproc-disassembled-and-patched-versions.zip

Link to comment
Share on other sites

Wow. That was quite a read. I don't think I understand half of it, but I will have to re-read another day.

As for the passphrase and hashes, the firmware I uploaded was not the actual firmware loaded on the device but was a fresh copy from the manufacturer. So that may in fact be the default password, but I think it is just a test value since the as-shipped version came up without a password and after a system reset reverts to an open network.

On to the hashes: my memory is a bit fuzzy since I last looked at those a few days ago, but I was able to decrypt them. Unfortunately they weren't the correct ones. I also found those hashes lurking in the devices /etc/shadow file. I also remember them not changing after I modified the admin panel password. Also, the user those hashes are associated is "tw" and is commented out in /etc/passwd. My knowledge of how linux handles password hashing is fairly limited, but as I understand it, tw is the salt for these particular hashes. Again, there may also be some discrepancies since we aren't physically looking at the exact same binary since mine is on the device and the one I uploaded was from the manufacturer. If I have time tomorrow to poke around in the firmware some more, I will attempt to confirm the hash issue.

I kinda have a handle on what is going on here, and I will definitely be trying to reproduce what you've done. I will let you know if I get it to work. Honestly I am blown away that you were able to figure all that out.

Please accept this I.O.U redeemable for one (1) alcoholic beverage of your choice on the off chance we should ever meet.

Link to comment
Share on other sites

I got the code to open in radare2, I'm at a loss as to how I would go about editing the binary myself. Could you explain how that process works?

I tried running rasm2 -f webproc -b 32 -a mips -D

No errors occur, but nothing happens, it just returns to the next console line.

Back to the hashes mentioned: I plugged the router back in so I could check out the hashes again. I used johnny and set to work on cracking the hashes (which took all of 2 seconds). My hashes are different than the ones included in the device firmware. I wasn't able to crack the admin password on the manufacturer release, but I don't know why that one failed to crack. Maybe it is a slightly later version of the firmware and that got changed. I'll have to do some more investigating there. The user level account password was "user" so I find it unlikely that they would have changed one password but not the other. Or something may alter that once the firmware upgrade is applied.

Link to comment
Share on other sites

I got the code to open in radare2, I'm at a loss as to how I would go about editing the binary myself. Could you explain how that process works?

I tried running rasm2 -f webproc -b 32 -a mips -D

No errors occur, but nothing happens, it just returns to the next console line.

Back to the hashes mentioned: I plugged the router back in so I could check out the hashes again. I used johnny and set to work on cracking the hashes (which took all of 2 seconds). My hashes are different than the ones included in the device firmware. I wasn't able to crack the admin password on the manufacturer release, but I don't know why that one failed to crack. Maybe it is a slightly later version of the firmware and that got changed. I'll have to do some more investigating there. The user level account password was "user" so I find it unlikely that they would have changed one password but not the other. Or something may alter that once the firmware upgrade is applied.

I was actually using just any old hex editor (in my case 'Bless') to do the patching, however yes you can actually use radare2 for that as well.

Here's an example of using it to do that branch instruction patch. The first byte from 0x10 to 0x14 is what flips a beqz to a bnez

For moving to addresses with 's' you have to add the 0x in front for hexadecimal, but when patching with wx [sequence of bytes] you don't put the 0x in front is what I've figured out...

You also need to re-open the file as read-write as it opens it as read only at first, type "oo+" to do that.

Alf@UNKNOWN:~/Downloads/PenTest$ cp webproc webproc-mypatch1
Alf@UNKNOWN:~/Downloads/PenTest$ radare2 -e -b 32 -a mips webproc-mypatch1
Warning: read (strtab) at 0x20
Warning: Cannot initialize strings table
[0x00401b70]> oo+
File webproc-mypatch1 reopened in read-write mode
[0x00401b70]> s 0x401fcc
[0x00401fcc]> pd 1
        ,=< 0x00401fcc    10400026     beqz v0, 0x00402068
[0x00401fcc]> wx 14400026
[0x00401fcc]> pd 1
        ,=< 0x00401fcc    14400026     bnez v0, 0x00402068
[0x00401fcc]> wx 10
[0x00401fcc]> pd 1
        ,=< 0x00401fcc    10400026     beqz v0, 0x00402068
[0x00401fcc]> wx 14
[0x00401fcc]> pd 1
        ,=< 0x00401fcc    14400026     bnez v0, 0x00402068
[0x00401fcc]> exit

As for assembling to actually know what the bytes are to write using 'wx' with the above method you can use rasm2 like this... I think it's important to differentiate between radare2 and rasm2 though, rasm2 assembles and radare2 disassembles.

Alf@UNKNOWN:~/Downloads/PenTest$ rasm2 -e -b 32 -a mips "addiu v0, zero, zero"
24020000
Alf@UNKNOWN:~/Downloads/PenTest$ rasm2 -e -b 32 -a mips "jr ra"
03e00008

The first one 'addiu v0, zero, zero' is really the same as 'li v0, 0'

This is because radare2 and most disassemblers use a pseudo syntax (as described here: http://www.howardhuang.us/teaching/cs232/03-More-MIPS-instructions.pdf)for cleaner looking code, but it really is the same as what the actual code is. If you instead put bytes into rasm2 instead of an instruction in quotes it'll do the reverse: (so I guess technically it does disassemble too, but a byte sequence rather than an entire binary file)

Alf@UNKNOWN:~/Downloads/PenTest$ rasm2 -e -b 32 -a mips -d 24020000
li v0, 0
Alf@UNKNOWN:~/Downloads/PenTest$ rasm2 -e -b 32 -a mips -d 03e00008
jr ra
Alf@UNKNOWN:~/Downloads/PenTest$ rasm2 -e -b 32 -a mips -d 2402000003e00008
li v0, 0
jr ra

I did however have trouble getting rasm2 to assemble any branch instructions with the instruction in quotes form:

Alf@UNKNOWN:~/Downloads/PenTest$ rasm2 -e -b 32 -a mips -o 0x401fcc "beq v0, zero, 0x402068"
Cannot assemble 'beq v0, zero, 0x402068' at line 3
invalid
Alf@UNKNOWN:~/Downloads/PenTest$ rasm2 -e -b 32 -a mips -o 0x401fcc "bne v0, zero, 0x402068"
Cannot assemble 'bne v0, zero, 0x402068' at line 3
invalid
Alf@UNKNOWN:~/Downloads/PenTest$ rasm2 -e -b 32 -a mips -o 0x401fcc "bne v0, zero, 0x26"
Cannot assemble 'bne v0, zero, 0x26' at line 3
invalid
Alf@UNKNOWN:~/Downloads/PenTest$ rasm2 -e -b 32 -a mips -o 0x401fcc "bne v0, zero, +0x26"
Cannot assemble 'bne v0, zero, +0x26' at line 3
invalid
Alf@UNKNOWN:~/Downloads/PenTest$ rasm2 -e -b 32 -a mips -o 0x401fcc "bne v0, zero, 26"
Cannot assemble 'bne v0, zero, 26' at line 3
invalid
Alf@UNKNOWN:~/Downloads/PenTest$ rasm2 -e -b 32 -a mips -o 0x401fcc "bne v0, zero, 402068"
Cannot assemble 'bne v0, zero, 402068' at line 3
invalid
Alf@UNKNOWN:~/Downloads/PenTest$ rasm2 -e -b 32 -a mips -o 0x401fcc "bne v0, 0, 402068"
Cannot assemble 'bne v0, 0, 402068' at line 3
invalid

There should be and probably is a way to get them to assemble properly, I'm just not sure how it wants me to type it for it to like it.

You can however like described above do it the other way, give it bytes and it'll show you what it will produce :) So I used an online mips assembler here to know what bytes I should use: http://alanhogan.com/asu/assembler.php

You can enter some test code like this: and then you generate the assembled code at the bottom, then on the second similar page you copy the blue colored output into it, and select verbose/debug + submit and you can see what it would do

########################################################
# Resource for assembling branch instructions on mips
########################################################

.text
.globl   main

# main:   Testing branching instructions

VZeroIsOne:
j GoBack1

VOneIsOne:
j GoBack2

VZeroIsNotZero:
VZeroIsZero:
j GoBack3

VOneIsNotZero:
j CallCheckAuthFunction


CheckAuth:
li $v0, -1 #set v0 to zero
jr $ra    #jump register: return address register

main:   
   addi   $sp, $sp, -16   # make space on stack.
   sw   $ra, 0($sp)   # preserve return address.
   sw   $s0, 4($sp)   # preserve registers s0 through s2
   sw   $s1, 8($sp) # as we may clobber it in main
   sw   $s2, 12($sp)

   #effectively the same two instructions here
   #except operating on two different registers, v0 and v1
   
   #load v0 register with value: 1
   li $v0, 1
   
   #add value: 1 to value: 0 and copy to v1 register
   addiu $v1, $zero, 1

   li $a0, 1 #load a0 register with value: 1

   beq $v0, $a0, VZeroIsOne #branch if v0 is equal to a0
   
GoBack1:
   beq $v1, $a0, VOneIsOne #branch if v1 is equal to a0

GoBack2:
   li $v0, 0

   #same as bnez
   bne $v0, $zero, VZeroIsNotZero #branch if v0 does not equal 0
   #same as beqz
   beq $v0, $zero, VZeroIsZero    #branch if v0 is 0
   

GoBack3:
   #same as bnez
   bne $v1, $zero, VOneIsNotZero #branch if v1 does not equal 0
   

CallCheckAuthFunction:
   #calling a function places the return address in $ra register
   jal CheckAuth
   #here is where it will return to

   beq $v0, $zero, SuccessfulLogin

InvalidCreds:
   li $v0, -1       # return value for main
   j ExitMainFunction

SuccessfulLogin:
   li   $v0, 0      # return value for main

ExitMainFunction:
   lw   $ra, 0($sp)   # restore return address
   lw   $s0, 4($sp)   # restore registers s0 through s3
   lw   $s1, 8($sp) # before exiting main
   lw   $s2, 12($sp)
   addi   $sp, $sp, 16   # restore stack pointer

   #jump to register, ra register = return address register
   jr   $ra     # return to Operating System


.data

As you can see from part of the verbose output:

Loading 0x00400044 : 1440fff0  ; <input:52> bne $v0, $zero, VZeroIsNotZero #branch if v0 does not equal 0
   NOT branching [bne], because 0 does equal 0

Loading 0x00400048 : 1040ffef  ; <input:54> beq $v0, $zero, VZeroIsZero #branch if v0 is 0
   Branching [beq], because 0 does equal 0
 

1440fff0 -> 14 40 ff f0

1040ffef -> 10 40 ff ef

The difference in the first two bytes is only the first one, from 0x14 (bnez) to 0x10 (beqz)

The second byte I believe is the register, so if v0 was v1 or a2 or whatever that byte would be different.

The last two bytes are the relative offset where the branch is going to actually jump to!

So knowing that we can assemble our new branch instruction:

Alf@UNKNOWN:~/Downloads/PenTest$ rasm2 -e -b 32 -a mips -o 0x401fcc -d 10400026
beqz v0, 0x00402068
Alf@UNKNOWN:~/Downloads/PenTest$ rasm2 -e -b 32 -a mips -o 0x401fcc -d 14400026
bnez v0, 0x00402068

Notice I add the -o parameter with the address the branch is being taken from, this is important at showing the right result, as like I said above branching is relative so based on the address you branch from + the offset is where it will go to.

So for example if you didn't provide the address with the offset, it'll be the same as using offset 0:

Alf@UNKNOWN:~/Downloads/PenTest$ rasm2 -e -b 32 -a mips -d 14400026
bnez v0, 0x0000009c
Alf@UNKNOWN:~/Downloads/PenTest$ rasm2 -e -b 32 -a mips -o 0 -d 14400026
bnez v0, 0x0000009c

It'll still be the right code at the proper location that you place it (since branching is relative to that location), but using the offset here will show you that it's going to be correct!

There we are, that should do it for now. I'm going to see if any of my old routers can be OpenWrt'd or at least if there's a telnet available or way to access the filesystem like yours so I could experiment with my own router as well, though last time I checked for OpenWrt support the site said it was possible but limited space and memory made it difficult, I wonder if there's been any progress made!

As for the hashes, ah I see you used johnny to help you with them! So is it the right place or not? So what kind of hash is it? Just curious about that.

~Alf

Edited by AlfAlfa
Link to comment
Share on other sites

I still don't understand writing to the binary. I installed bless, but it looks nothing like the disassembled output, and the addresses are different. I'm trying to understand this part:

Alf@UNKNOWN:~/Downloads/PenTest$ cp webproc webproc-mypatch1
Alf@UNKNOWN:~/Downloads/PenTest$ radare2 -e -b 32 -a mips webproc-mypatch1
Warning: read (strtab) at 0x20
Warning: Cannot initialize strings table
[0x00401b70]> oo+
File webproc-mypatch1 reopened in read-write mode
[0x00401b70]> s 0x401fcc
[0x00401fcc]> pd 1
        ,=< 0x00401fcc    10400026     beqz v0, 0x00402068
[0x00401fcc]> wx 14400026
[0x00401fcc]> pd 1
        ,=< 0x00401fcc    14400026     bnez v0, 0x00402068
[0x00401fcc]> wx 10
[0x00401fcc]> pd 1
        ,=< 0x00401fcc    10400026     beqz v0, 0x00402068
[0x00401fcc]> wx 14
[0x00401fcc]> pd 1
        ,=< 0x00401fcc    14400026     bnez v0, 0x00402068
[0x00401fcc]> exit

I understand that wx 14400026 writes 14400026 to address 0x00401fcc. What does wx 14 do? it doesn't write 14 to 0x00401fcc, so I'm not understanding what that operation does. I've worked with asm instructions before, but it was on a terrible spartan fpga, and was much simpler. Never disassembled anything.

The hashes have nothing to do with the admin console unfortunately. They are static across password changes. What I know about the hash itself is that it is MD5, salted with "TW". I can't get johnny to crack them. The only output I get is "Loaded 1 hash" followed by "No password hashes left to crack". I don't know if I've put the file in the wrong format or what. It is still a work in progress.

Link to comment
Share on other sites

It doesn't for you? In my example above it did, I only did that to show that only that one byte out of the 4 bytes actually are changed. Typing two bytes would change the first 2 bytes of the four byte (32 bit) instruction, typing 3 changes the first 3 bytes, and typing 4 changes all four bytes of the one instruction. I think it'll do that no matter how many bytes you type it'll overwrite starting from the address you're at with however many bytes you typed after wx.

If you look closely it goes from 10 40 00 26 to 14 40 00 26 -> 10 40 00 26 -> 14 40 00 26

All instructions for this architecture are 32-bit (4 bytes) in length, so it only really make sense to edit multiples of 4 bytes at a time unless the last instruction in the sequence of instructions you're modifying is just the first byte or just the first two or just the first three. Like in this case, the first instruction to be modified is also the last and only the first byte needs to be changed.

EDIT: As for the hex editor method of patching, yes it isn't disassembling it's just showing the raw bytes of the file. The addresses also aren't different they are just based from 0 rather than 0x400000. In this image here you can see the ELF magic number header at offset 0 in the file, but at virtual address 0x400000 in radare2:

LGxPWGi.png

And at offset 0x1fcc in the hex editor is virtual address 0x401fcc in radare2: (it's hard to see where the cursor is, but on the line with offset 0x1fb8 the cursor is grey before an 0x14 byte and the next line offset 0x1fd4 can be seen as 0x401fd4 immediately following the two lines highlighted in radare2)

gzbEVBO.png

When looking at a file everything starts from 0 since that makes sense for files, the hex editor doesn't know it's a binary file or what kind of file it is at all it's just showing what the file is made of. In radare2 it knows it's a binary file so it also knows what the image base is (usually 0x400000) and uses that as the where to base everything from, since if you were looking at the memory of the application while it was running that is what addresses you would use and be looking at! The image base can be configured to be something else besides 0x400000, or it can even be randomized. (something like ASLR [address space layout randomization]) Most of the time though it is the default of 0x400000.

That should clear it up...

The hashes may not have to do with the admin console, but you said some places or files on the filesystem weren't accessible from root, maybe the tw user does have access to them, and that might be what they're using to store the admin control panel password which makes it inaccessible to the 'root' user.

It could be possible that root isn't actually root, and tw is actually root or at least higher privileged than the 'root' user. If you 'ls -lah' what kind of files are owned by 'tw' and which are owned by 'root'

Edited by AlfAlfa
Link to comment
Share on other sites

No, I have access to everything as root. I'm 99% sure that the tw user was actually commented out in the passwd and shadow files. I believe them to be correct since I am able to log in as root. Modifying the firmware wasn't exactly the direction I wanted to go with since that is not a practical method for gaining admin access to the router. I was hoping it would uncover clues as to where the password was stored. I assume the admin password cannot be stored inside the webproc file itself. Reverse engineering is cool, but it just isn't part of my toolbox.

Link to comment
Share on other sites

I haven't quite given up yet. I have been comparing the contents of the folders before and after changing the password. The process I have been using is:

  1. tar the directory, output it to a writeable directory IE tar -c -f /var/etc1.tar etc
  2. change the password
  3. tar directory again IE tar -c -f /var/etc2.tar etc
  4. calculate md5 sum for both hashes

Strangely, the two directories I expected to change /etc/ and /usr/www have not been modified. Can a minor change like a password be insignificant after being compressed to a tar file?

Another interesting inconsistency is that the "WPS" default pin isn't really the default pin. The config.xml says it is 12345670, but it is something completely different. I don't even know how that could happen unless the developers never configured that option.

Link to comment
Share on other sites

I've only skimmed through this but it doesn't seem like anyone has noticed that these two lines are MD5 crypt hashes:

<Password t="s" >$1$TW$W4EX5n8uMLE15bpd62uqD.</Password>
<Password t="s" >$1$TW$3q69ksdrX7zaaLg54vFxN0</Password>


http://usenix.org/legacy/publications/library/proceedings/usenix99/full_papers/provos/provos_html/node10.html

From the question above, if anything changes, even one bit, an MD5 of any file will change, so yes, even a minor change will change the hash, that is the point of hashes.

If the config.xml file isn't changing after you change the root password then you are looking in the wrong place. These two files could just be templates which are used to create the actual config files from. Search the disk for the phrase $1$ or other snippets from the config file.

If you are logging in through ssh then the password will probably be in /etc/shadow, if not, look in the PAM config and see how it is doing its authentication.

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...