Difference between revisions of "Firmware password"
m (10 revision(s)) |
m |
||
Line 1: | Line 1: | ||
+ | {{Template:Articles|Terastation}} | ||
+ | |||
''How to find the password for the encrypted zip containing the Firmware?'' | ''How to find the password for the encrypted zip containing the Firmware?'' | ||
Revision as of 00:30, 11 November 2007
How to find the password for the encrypted zip containing the Firmware?
Contents
Get started
- First, find out that ls_servd is responsible for decrypting.
How did i find out? Well bg told me :-)
ice:~/tera>strings ls_servd|grep unzip /bin/unzip -P %s %s/%s -d %s > /dev/null 2>&1
I had some hope that the password is actually contained in this binary somehow.
However, a brief interlude with a zipcracker and the full result of the "strings" output doesn't find a match, but maybe we just need to dig a little bit deeper...
The %s indicates this is a call to sprintf(3), so we need to check the third parameter of this sprintf call.
How about disassembling the thing?
- We need a disassembler.
"objdump" usually does the trick...
ice:~/tera>objdump -d ls_servd /usr/libexec/elf/objdump: ls_servd: File format not recognized
Huh? Why this?
ice:~/tera>file ls_servd ls_servd: ELF 32-bit MSB executable, PowerPC or cisco 4500, version 1 (SYSV), \ for GNU/Linux 2.4.17, dynamically linked (uses shared libs), stripped
Oh, of course. Its a PPC binary, not an x86 binary.
- We need an PowerPC disassembler.
Some googling later, I now have an (linux) ppc-cross-objdump binary.
ice:~/tera>./ppc-linux-objdump -s -d ls_servd >DUMP ice:~/tera>vim DUMP
Good. We have the code, and an hexdump in there. But now we have two more problems
Understanding the code.
- I have no PPC assembler knowledge.
Well, I know 6510 asm and some x86 asm, which helps with the general mindset, but the details of PPC are a mystery to me.
Nothing, a quick google couldn't fix. A PPC asm reference can be found here:
http://www-aix.informatik.uni-tuebingen.de/doc_link/en_US/a_doc_lib/aixassem/alangref/toc.htm
If you want to look up some mnemonics, be sure to also check "Chapter 6: Extended mnemonics" if you don't find your mnemonic.
- The file is big.
We now have about 8k lines assembler code. Where to start?
The only "plaintext" part of the listing are the system calls.
10002fec: 48 01 a2 59 bl 1001d244 <wait3@plt>
Objdump nicely annotated them for us. Hey, thanks :)
Parameter passing.
From strings, we know that "/usr/bin/unzip" is being called, that is usually done by using the system(3) system call. Lets check for that. ls_servd uses a lot of system(3) calls, this is the first one:
10005dec: 3c 60 10 01 lis r3,4097 10005df0: 38 63 b6 44 addi r3,r3,-18876 10005df4: 48 01 72 01 bl 1001cff4 <system@plt>
system(3) only takes one argument, so we can assume that r3 is that argument. Lets see what it is:
The first line fills the upper 16 bits of r3 with 4097 (hex 1001), resulting in r3=0x10010000 - the second line substracts 18876, resulting in r4=0x1000B644
We can now go and look that up in the hexdump part of the objdump output:
1000b638 613d3078 25303258 0d0a0000 2f736269 a=0x%02X..../sbi 1000b648 6e2f7265 626f6f74 00000000 6c69626c n/reboot....libl
Which makes the whole thing a call to /sbin/reboot. Yay!
Finding the right place
We could now go, and look up all parameters to system, to find the correct one, but we can also do it the other way round:
1000c478 25303878 0a000000 2f62696e 2f756e7a %08x..../bin/unz 1000c488 6970202d 50202573 2025732f 2573202d ip -P %s %s/%s - 1000c498 64202573 203e202f 6465762f 6e756c6c d %s > /dev/null
okay, the string starts at 0x1000C480 which makes the difference to 0x10010000 = 15232
Only two places refer to that string. The fist one looks like this:
10008c30: 48 00 01 89 bl 10008db8 <_init+0x5f70> 10008c34: 7f 48 d3 78 mr r8,r26 10008c38: 7c 65 1b 78 mr r5,r3 10008c3c: 38 9e c4 80 addi r4,r30,-15232 # unzip -p %s %s/%s -d %s 10008c40: 7f 46 d3 78 mr r6,r26 10008c44: 38 f8 c3 c4 addi r7,r24,-15420 # image.zip 10008c48: 38 61 00 08 addi r3,r1,8 10008c4c: 4c c6 31 82 crclr 4*cr1+eq 10008c50: 48 01 46 4d bl 1001d29c <sprintf@plt> 10008c54: 38 61 00 08 addi r3,r1,8 10008c58: 3b ff 00 01 addi r31,r31,1 10008c5c: 48 01 43 99 bl 1001cff4 <system@plt>
Yay, an sprintf(3) followoed by an system(). The system call parameters are numbered from r3 upwards, so the third parameter to the sprintf is r5, which is copied from r3 a few lines above. It looks like r3 might be the return value of the omnious subroutine at 0x10008db8.
Well the sub starts off a bit lengthy....
# Gen ZipPW in r3 10008db8: 94 21 ff 10 stwu r1,-240(r1) 10008dbc: 3c 80 10 01 lis r4,4097 10008dc0: 93 61 00 dc stw r27,220(r1) 10008dc4: 7c 08 02 a6 mflr r0 10008dc8: 3b 61 00 08 addi r27,r1,8 10008dcc: 93 e1 00 ec stw r31,236(r1) 10008dd0: 38 84 c6 24 addi r4,r4,-14812 # Binstring1 (41b) 10008dd4: 90 01 00 f4 stw r0,244(r1) 10008dd8: 7c 7f 1b 78 mr r31,r3 10008ddc: 93 81 00 e0 stw r28,224(r1) 10008de0: 38 a0 00 2a li r5,42 10008de4: 93 a1 00 e4 stw r29,228(r1) 10008de8: 7f 63 db 78 mr r3,r27 10008dec: 93 c1 00 e8 stw r30,232(r1) 10008df0: 3b 81 00 38 addi r28,r1,56 10008df4: 48 01 43 d1 bl 1001d1c4 <memcpy@plt> 10008df8: 3c 80 10 01 lis r4,4097 10008dfc: 38 84 c6 4e addi r4,r4,-14770 # Binstring2 (41b) 10008e00: 38 a0 00 2a li r5,42 10008e04: 7f 83 e3 78 mr r3,r28 10008e08: 48 01 43 bd bl 1001d1c4 <memcpy@plt> 10008e0c: 3b a1 00 68 addi r29,r1,104 10008e10: 3c 80 10 01 lis r4,4097 10008e14: 38 84 c6 78 addi r4,r4,-14728 # Binstring3 (41b) 10008e18: 38 a0 00 2a li r5,42 10008e1c: 7f a3 eb 78 mr r3,r29 10008e20: 48 01 43 a5 bl 1001d1c4 <memcpy@plt> 10008e24: 3b c1 00 98 addi r30,r1,152 10008e28: 3c 80 10 01 lis r4,4097 10008e2c: 38 84 c6 a2 addi r4,r4,-14686 # Binstring4 (41b) 10008e30: 7f c3 f3 78 mr r3,r30 10008e34: 38 a0 00 2a li r5,42 10008e38: 48 01 43 8d bl 1001d1c4 <memcpy@plt>
which calls memcpy on four different 41 byte long nul terminated binary strings:
a0bdb8d5cea1e8c4bedbc1b3dfc8c9c4c5bde0d1dec1dfbcb1dec9e6c3a3bfe9dec4e5bebfc4dfa5db00 d0b0d7e5dbbca0c8dfa6cea1c5c2dca5b1d7d6dadcc3bee1b2bda0b9e8b49fb2a4c0a5d2b1a2deb1b100 c8e5c2b8ddb8c0dedfd4d8dfe7a5a5e3ceb3b2d3d5b4e5d5bfa3a6e0d4c5bfd7bdd7b0e4c2c8dcb0a300 b8d4c8a7dedcb9e6b6dbb6dab8d1b9dca1b5b7cebcc5a3d5bbe2c7b4a7d8d4e49fd6bdc8e6b4a5c3e800
Well. This surely had me hooked. I had already suspected this "binary blob" to play some role :)
Then follows some less interesting code.
10008e3c: 2c 1f 00 01 cmpwi r31,1 10008e40: 41 82 00 ac beq- 10008eec <_init+0x60a4> # core(r28) 10008e44: 41 81 00 88 bgt- 10008ecc <_init+0x6084> # core(decide) 10008e48: 2c 1f 00 00 cmpwi r31,0 # core(r27) 10008e4c: 7f 63 db 78 mr r3,r27 10008e50: 41 82 00 30 beq- 10008e80 <_init+0x6038> # Cryptocore # 10008e54: 3d 20 10 02 lis r9,4098 10008e58: 38 69 cc a8 addi r3,r9,-13144 # 0x1001CCA8: nul bytes: "result_area" # return_after_cleanup 10008e5c: 80 01 00 f4 lwz r0,244(r1) 10008e60: 83 61 00 dc lwz r27,220(r1) 10008e64: 83 81 00 e0 lwz r28,224(r1) 10008e68: 7c 08 03 a6 mtlr r0 10008e6c: 83 a1 00 e4 lwz r29,228(r1) 10008e70: 83 c1 00 e8 lwz r30,232(r1) 10008e74: 83 e1 00 ec lwz r31,236(r1) 10008e78: 38 21 00 f0 addi r1,r1,240 10008e7c: 4e 80 00 20 blr
which deals with deciding which of the 4 binstrings is actually used, and some cleanup on return. Then follows the part we have been hunting for:
# Crypto-core, r3 is pointer to binstring 10008e80: 88 03 00 00 lbz r0,0(r3) # r0=r3[0] 10008e84: 39 40 00 00 li r10,0 # r10=0 10008e88: 3d 20 10 02 lis r9,4098 # r9=0x10020000 10008e8c: 2c 00 00 00 cmpwi r0,0 # if r0==0 10008e90: 41 82 00 28 beq- 10008eb8 <_init+0x6070> # Towards "return_after_cleanup" 10008e94: 7c 0b 03 78 mr r11,r0 # r11=r0 10008e98: 39 09 cc a8 addi r8,r9,-13144 # r8=0x1001CCA8: result_area # repeat 10008e9c: 38 0b ff 91 addi r0,r11,-111 # r0=r11-111; decrypt 10008ea0: 7c 08 51 ae stbx r0,r8,r10 # r8[r10]=r0; store 10008ea4: 39 4a 00 01 addi r10,r10,1 # r10++ 10008ea8: 7c 03 50 ae lbzx r0,r3,r10 # r0=r3[r10]; get next 10008eac: 2c 00 00 00 cmpwi r0,0 # if not nul byte 10008eb0: 7c 0b 03 78 mr r11,r0 # r11=r0 10008eb4: 40 82 ff e8 bne+ 10008e9c <_init+0x6054> # To repeat # 10008eb8: 39 29 cc a8 addi r9,r9,-13144 # r9= result_area 10008ebc: 38 00 00 00 li r0,0 10008ec0: 7c 09 51 ae stbx r0,r9,r10 # r9[r10]=0; nul terminate 10008ec4: 7d 23 4b 78 mr r3,r9 # RETURN STRING!!!! 10008ec8: 4b ff ff 94 b 10008e5c <_init+0x6014> # return_after_cleanup
Well well. how disappointing. The whole decryption is a bytewise decrement by 111.
The Passwords
- Decrypt them
We have all ingerdients, now we need to decrypt the contained passwords. Time for a little perl...
#!/usr/local/bin/perl @a=qw( a0bdb8d5cea1e8c4bedbc1b3dfc8c9c4c5bde0d1dec1dfbcb1dec9e6c3a3bfe9dec4e5bebfc4dfa5db00 d0b0d7e5dbbca0c8dfa6cea1c5c2dca5b1d7d6dadcc3bee1b2bda0b9e8b49fb2a4c0a5d2b1a2deb1b100 c8e5c2b8ddb8c0dedfd4d8dfe7a5a5e3ceb3b2d3d5b4e5d5bfa3a6e0d4c5bfd7bdd7b0e4c2c8dcb0a300 b8d4c8a7dedcb9e6b6dbb6dab8d1b9dca1b5b7cebcc5a3d5bbe2c7b4a7d8d4e49fd6bdc8e6b4a5c3e800 ); for (@a){ @b=unpack("C*",pack("H*",$_)); print map { $_?chr($_-111):"" } @b; print "\n"; };
Start it
ice:~tera>./crpt 1NIf_2yUOlRDpYZUVNqboRpMBoZwT4PzoUvOPUp6l aAhvlM1Yp7_2VSm6BhgkmTOrCN1JyE0C5Q6cB3oBB YvSInIQopeipx66t_DCdfEvfP47qeVPhNhAuSYmA4 IeY8omJwGlGkIbJm2FH_MV4fLsXE8ieu0gNYwE6Ty
Voila! A simple test shows, that the first password is the one actually used. The other ones might come in handy later :)
See also Firmware Update.
-- Sec