My Secret
Challenge
- mysecret.zip
- Type: format string, read.
- Difficulty: beginner.
Analysis
We can easily figure out this is a classic format string bug from these 2 lines:
1 | fgets(answer, sizeof(answer), stdin); |
The flag is read from flag.txt
into a buf in heap, and the address of this buf is stored in char* secret
. So we need to search for the location of secret
on stack, then by using %s
format specifier, we can read a string at an address value and print it out to terminal.
1 | gef➤ b *main+301 |
So secret
is at the 8th parameter, and all we need to input is just %8$s
.
Exploit
Do it yourself.
Check Sum
Challenge
- checksum.zip
- Type: format string, read + write.
- Difficulty: easy.
Analysis
1 | long long *ptr = # |
1 | Arch: amd64-64-little |
The program stores 2 random numbers on stack, read 30 bytes into name
, then receives input as a long long decimal and stores in num
, prints out greeting
, which also includes the string the we inputted. Finally, it checks if a+b+3108
equals to num
before giving the shell.
The main point of this challenge is how to read the number of a
and b
, but when we read them out, there is no way we can write it sum to the num
, since the program reads input only once.
Let’s review the structure of a format string used in printf()
:
%[flags][width][.precision][length]specifier
According to this document, we can take advantage of the *
of width parameter to set width for printing out an arbitary char a value of a
and b
, since they are both declared on main()
. The strategy is we gonna print 2 char, with each char is set width of value that a
and b
store, then pads some more chars so that the total print out equals to a+b+3108
. Noted that the string that is printed out by printf
is not only contains our input but also has 'hi '
, so there are 3 chars already there.
Exploit
1 | from pwn import * |
This payload get the value at 8th para (value of a
) and set it as a width for the next para to be printed out as a char, apply the same for 9th para (value of b
) and %3105s
so that the eventually string that is printed out will has the desired length. That’s how we can by pass the condition and get the shell.
Out Of Pwn 2
Challenge
- outofpwn2.zip
- Type: out of bound, go to right, asynchronous growth, overwrite the value of pointers that can be read-write.
- Difficulty: medium.
Analysis
This challenge is 90% based on this CTF writeup, all I did is just rewrited the source code in C and patched the out of bound bug on the left side. Therefore, we cannot read, write, delete notes at negative index, uncreated notes or freed notes.
So what is the vulnerability? Let’s have a look at source code:
1 | int INDEX = 0; |
1 | Arch: amd64-64-little |
And locations of NOTES
and SIZES
in the binary file:
Each NOTES
element is 8 bytes, whereas SIZES
takes only 4 bytes. Everytime we create a note, INDEX
increases one. However, the program doesn’t stop us from creating more than 8 notes, then until a specific number of notes is created, the value to be stored in SIZES[i]
with overlapse on NOTES[j]
, since SIZES
only increase 4 bytes each time.
Therefore, we can find out the note index i
to create that when we input the length for it, this value will be stored in SIZES[i]
and also overwrites NOTES[j]
. And since PIE is disabled, we can overwrite by the value of a GOT address. Then, since we can read and edit on a created note, we can then read the libc address of that function, calculate system address and edit it to that note. If we choose to edit at the GOT address of free()
and create a note with content "/bin/sh"
, when we call free on that note, we call system('/bin/sh')
and finally get the shell we want.
Exploit
1 | from pwn import * |
We need to call delete(1)
so that the libc address of the free()
can be resolved and stored in GOT section.
That’s all, happy hacking! :P