Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
So this is a static binary. It's got a lot of libc zipped up within itself, removing ret2libc and ret2plt and stuff like that. Disassembling main and ignoring all the extra libc functions, we see it prints the string about it being a large binary and then calls gets on rbp-0x100. So, buffer overflow obviously. But what do we ret to? How to pop a shell?
System, execve etc. all the stuff that may be useful for shell popping is removed from the binary's libc functions. That doesn't matter though.
The libc is big, and it has a lot of purposes. It'll need to do many things including syscalls. Because of this, libc is always a ROP gadget goldmine. Since this binary has most of libc in itself, this binary is also a ROP gadget goldmine.
Due to this, we can build a simple execve ROP chain to execve("/bin/sh",NULL,NULL), then cat flag.txt.
What I just used here is called Return Oriented Programming. ROP works by building chains of return addresses. When a program rets, it'll go to the next value off the stack as a return address. So, if our input is ever at the top of the stack, we can build chains of return addresses, causing it to continue ret-ing into the next location.
The ROP chain i used uses syscalls or system calls, the lowest level, used for special calls to the system, e.g opening files, writing to files, and in this case executing files.
The chain I just used is as follows. It uses the execve syscall since the function is not available. Remember 64-bit arguments are in rdi, then rsi, then rdx...
Our goal is to execute execve("/bin/sh",0,0) as a syscall
pop rdi address - pop next value off the stack into rdi for first argument
/bin/sh address - to be popped into rdi as first argument
pop rsi address - pop next value off the stack into rsi for second argument
0 - to be popped into rsi as second argument
pop rdx address - pop next value off the stack into rdx for third argument
0 - to be popped into rdx as third argument
pop rax address - pop next value off of the stack into rax. RAX stores the syscall number when a syscall is done.
0x3b - to be popped into rax for the syscall number. 0x3b is execve
syscall address - execute the syscall. Will be asking the kernel to execute execve("/bin/sh",0,0)
We get a shell, and get the output in large ascii text. There's a PNG of the flag. Here's my script (stupid probably). I got 1/3 of the image and guessed the rest lol
OH MY GOD I COULD HAVE CHEESED IT
ONLY STDOUT IS ASCII-ARTED
SO
base64 -w0 flag.png 1>&2
FUCKING YEAH DOES IT REEEEEEEEEEEEEEEEEEEEEEEEEE
It takes an address and number in hex, then writes the number to the address. The hex is parsed by strtoull which will be useful later.
After writing, it calls sleep(0xf)
, then prints the address of alarm and calls exit. The address of alarm gives us a libc leak. Partial RELRO, so GOT overwrite is possible. Here's what we do:
Overwrite exit@got with main. When it attempts to exit after printing a libc pointer to us, it'll call main, giving us another write
Overwrite sleep@got with main(we couldn't do this before as alarm is called after sleep but we dont need anymore leaks) so that the alarm doesnt catch us out and our exploit is quick from then on
Overwrite strtoull@got with system so next time it tries to turn our input to hex it calls system on it
Enter /bin/sh as the next address
Shell will be popped, we can cat flag.txt.
So, from the title, we guess it's using OFB encryption.
For those of you that don't know, OFB encryption works like this: Firstly, like any AES mode, there is a key, and then with OFB, you also have an IV. Then:
Encrypt the IV using the key
Sets the new IV to the encrypted IV
Xor the plaintext block with the encrypted IV, and then this is the output
This repeats for each block.
After reading the script, we can work out that:
It takes the flag.txt, and reads this
Pads this data using pkcs7 padding
Splits the data into blocks of 16 bytes
Randomly shuffles these blocks
Encrypts the blocks with the OFB we mentioned above.
Since the IV and key stay constant, we can connect and receive as many data samples as we want, and since there are 3 blocks, there are only 6 possible permutations for the blocks to be in. (doesn't really matter but whatever)
Looking even closer at the script, we see this line:
This means that the length of the flag must be 33, as we have already established that there are three blocks. This also means, because of the way the data is padded, that one of the blocks must be "}\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f"
(we know the last char is "}")
Now, we can simply attempt to XOR each block with this string to get the key for that block, and then XOR that with the other blocks to get the flag. I made this table for convenience.
We then XOR f24f00b65180b6161b2b9da92d2e42ae with 7d0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f to get 8f400fb95e8fb919142492a622214da1, which is what XOR key was used to encrypt the first block.
Then, we simply XOR this key with each of the other blocks to get the rest of the flag.
Just snow steg.
/snow -C /tmp/coldwar.txt
Yeah, it's another one of those nested compression challenges. Inside of the zip is a gzip compressed file, which has a compressed file of a compressed file, etc. etc. etc.
Anyway, all of the archive types it uses are extractable using 7z e
So I just used a script to continuously extract the current archive and then remove it until it could not be extracted anymore. Then, you can simply cat the last file left to get the flag, flag{the_answer_is_1548_licks}
NOTE: My script was called popper.py, you'll have to replace popper.py in the script with whatever you call your script.
Given the name "space FORCE" this was probably meant to be house of force but I felt like that would be more complicated than what I ended up doing so I... didn't do that.
Instead, I went along the path of a more complex tcache poisoning attack.
Let's digest the program first before we look at how we are to attack it.
We have 5 functions:
register account
print account by UID
print all accounts
delete last registered account
launch rocket
.
I'm not gonna talk about launch rocket, since it seems rather useless and I didn't use it at all.
We choose a first name, last name, and can set the expiry if we want. Note the expiry, unlike the comment, is malloc-ed no matter what, it's just not initialised if we don't set the expiry. We can also set the comment, for this we get to specify the size and it gets malloced. Here are some representations of how accounts are stored:
Simply represents the index of the account in the account array, which by the way is stored on the stack and has no cap.
Specify UID(which is used as an index to grab the account) and the UID, first name and last name will be printed. Note that there's no checks at all on the UID and deleted accounts aren't removed from the array, the account counter is just decremented(read after free) but I ended up not using this, as the UID is only printed as an integer and thus not a lot of information can be reaped.
Note that the program keeps an account counter, incrementing when an account is made, decrementing when the last account is deleted.(This is also how it keeps track of the last account). All accounts up to this counter are printed: UID, last name, first name But this time so is the expiry - day, year and month.
Malloc does NOT initialise data. Info leak! Left over pointers from a free chunk newly allocated remain. This allows us to get a heap leak by smartly handling expiries, which I'll explain below.
There is overflow when we make an expiry. 32 bytes are read into the month when only 16 should be read. This gives overflow of 15 bytes after the end of the expiry chunk(1 byte is reserved for null as fgets is used)
So, we've already covered the account and expiry structs.
Accounts are 0x70 size chunks(0x60 allocation)
Expiries are 0x20 size chunks(0x10 allocation)
Comments are malloc-ed to their exact size
Note we can't read to comments, only write.
So, how do we get this info leak?
Let's start off with two accounts, no comment. Heap is as follows:
Let's free them both, and take a look at the tcache
0x20 bin -> account 0 expiry -> account 1 expiry
0x70 bin -> account 0 -> account 1
Let's make another account
, it'll be served
pretty much on top of the old count 0
. Let's again not set an expiry.
Now, remember how I said it just doesnt initialise when we dont set an expiry? This means in place of the day and year fields, will set a heap pointer! We can leak this pointer!:
Printing all accs
Extracting the day and year pointers
Doing twos complement (as they may be displayed negative)
Reconstructing the ints to get the original heap address
This gives us the heap base
> Ok.. ok, so we know the whereabouts of the heap. What about libc?
In hindsight this could've been done SOOO much simpler by forcing an unsorted bin chunk then doing the same sort of protocol as the 0x20 would be served by the unsorted bin but.. here we are. The 15 bytes of overflow
past the expiry chunk
lets us overwrite the size field
of the next chunk
(I didnt do anything here, just set it to what it already was) and then overwrite
the 8 bytes afterwards
. This can be used nicely for tcache poisoning, overwriting the next pointer of a tcache chunk.
What I did was:
allocate an account with a 0xe0 sized comment
freeed it
allocated an account
with no comment
.
This account would be in around the same place, just below the old, freed, tcache comment chunk.
With the expiry overflow
, we can overwrite the next pointer
to execute tcache poisoning
. Given my knowledge of the whereabouts of the heap, I used this to:
go back
overwrite the expiry pointer
of chunk 0
to an existing unsorted bin chunk
(as that would have libc pointers).
So, we can set the next pointer
to the address
of the expiry pointer of chunk 0
. Make a 0xe0 comment allocation
, make one again
- this will be on top of chunk 0's expiry pointer! Let's overwrite the expiry pointer with that of the unsorted bin chunk.
> Now, by printing all accounts, we also print the libc pointer hidden in the expiry(it'll be right at the month field).
> Note the 0xf0 tcache is utterly fried after this so we wont be making any 0xe0 allocations again.
Now, we've got heap AND libc!!! We just need the final piece - an arb write, preferable to write to free hook
.
We can repeat this process again, but things get more complicated. Going back to getting an unsorted chunk
, this is only possible through:
filling up the tcache
(I used 0xf0 tcache)
freeing another one
Problem with this is...
It leaves an unwanted unsorted/small bin chunk we'll need to completely serve out to stop allocations earlier on in the heap
It wastes a lot of space on the heap as all these old tcache chunks get disconnected from tcache. We'll need to refill these phantom freed account blocks before we move on
We can't have too many accounts, it'll overwrite important things on the stack and maybe even the return address to account addresses(since NX is on this isn't at all helpful)
I went smart about this, and in my filling up
of the heap
so that we can get a nice "clean slate" ready for the second poisoning. I used comment sizes
that imitated account creations
.
Eventually, through some trial and error and thinking:
I was able to do a good set of allocations
that efficiently used the list to fill up the entirety of the previous section of the heap
such that all new allocations are top chunk allocations.
Finally, we can:
allocate a chunk
with 0xf0 size
such that the comment chunk will be of size 0x100
it won't be in the previously destroyed tcache.
Then, we free it
Allocate
an account
with no comment
tcache poison
via overflow
Set the next pointer
to free hook-8
Allocate
an account
0xf0 comment size
Do that again
The second one's comment will be at free hook-8
write /bin/sh + system
free the previous account
shell popped!
Free hook is called on the pointer that is about to be freed before it is freed. Overwriting free hook with system means the chunk about to be freed (in this case /bin/sh as we wrote that to free hook -8) will have system called on it Allocating at free hook-8 to get free hook overwrite and also extra space for /bin/sh for system is a nice little trick i learned from a fizzbuzz writeup, which he learned from NotDeGhost's writeups.
Abuse lack of pointer clearing to leak heap pointers using expiries by allocating two, freeing two then allocation one(all without expiry initialisation)
Fill 0xf0 tcache, get an unsorted bin chunk this way(we can allocate chunks of arbitrary sizes via allocating accounts with specific sizes of comments)
Tcache poison to get a chunk at the expiry pointer of account 0
Overwrite expiry pointer to point at unsorted bin chunk(which will have libc pointer)
Leak libc by printing all accounts
Fill up previous section of the heap through a series of specifically comment-sized registrations
Tcache poison again this time in the 0x100 tcache to get a chunk at free hook-8
Write /bin/sh\x00 + system
Free last account
Shell popped
Exploit script can be found here
flag{michael_scott_for_president}
So the encryption takes some data, then base64 encodes it and encrypts it by XORing each byte with the byte after it, wrapping the last byte around to the start. The result is then hexlified. So, we can simply bruteforce the first byte in order to bruteforce what the decryption is. This gives us the base64 of the flag, which can be base64 decoded to get:
So, let's go to one of the pages, Bit for example.
We'll see the url http://jh2i.com:50010/?page=bit
?page= imlies some form of LFI may be possible, as it looks like the specified page is imported into the template. So, let's go ahead and try http://jh2i.com:50010/?page=/etc/passwd
We'll find it gives an error about /etc/passwd.php not existing. This tells us it appends .php to the end of the page parameter and includes it.
Tony told me that a null byte causes the .php to become redundant as the null byte will terminate the string.
http://jh2i.com:50010/?page=/etc/passwd%00 works, displaying /etc/passwd.
So I took a guess and tried /flag.txt%00 and that gave the flag.
If we try to run this, we would eventually get the flag, but it's really slow, so let's try to not do that.
If we take a look at the a() function, we can see that it
Has a loop that goes from 1 to n (for i in range)
Defines b to be 0
Checks if i is a multiple of n
If so, add i to b
If b is equal to n, return True
We can see (and also guessing by the title) that this will only return true on perfect numbers. Knowing this, I just looked up a list of perfect numbers, and then just XORed each number with the corresponding perfect number.
Script below.
Then we just wrap the output with flag and submit it
/robots.txt init
flag{r0b0ts_txt_txt_r0b0ts}
So you're given 2 blocks of text, one is caesar cipher, offset 13 encrypted, and the other is reversed text, than caesar cipher with offset 13.
Put them together alternatively(one from one block,one from the other, read second block from bottom to top) and you get this:
Oh boy! Wow, this warmup challenge sure was a lot of fun to put together! I so definitely absolutely always love trying to think up new and innovative things to do with the very basic, common and classic CTF techniques! The first part of your flag is flag{julius and that is a great start but it is not everything that you will need to solve this challenge. I don't like trying to hide and . separate each part of the flag. The second part of the flag is _in_a but you do need just a little bit more. What exactly should we include here to try and make this filler text look more engaging and worthwhile? Should we add newlines? Should we add spaces and try and make it symmetrical? How many lines is enough to make this filler text look believable? A solid square of letters within a simple, monospace-font text file looks good enough to me. Are we almost at the end? It looks like it! I hope it is good. The third part of your flag is reflection} and at this point you should have everything
Combine flag{julius_
, _in_a_
and reflection}, and you get your flag
With some fuzzing we can figure out that the search is using LDAP. The forgot password message reveals that the password is in the 'description' field. This allows us to char-by-char brute the password: administrator)(description=*
Will return a result if the password matches this pattern
The pw is: very_secure_hacktivity_pass