Cracking Static Stack Canaries

Challenge source code and binary is available here. Below is the source code for reference.
1 |
|
Understanding the source code completely is the first step to solving a pwn challenge. Key things to note here is that user can input the number of bytes they want to write.
In vuln()
function, buf
variable is assigned buffer of 64 bytes but there is no check in place to check the user input because read()
does not perform bounds checking, which is why fgets()
is preferred to read user input.
π‘ Array bound checking refers to determining whether all array references in a program are within their declared ranges
User can input more than 64 bytes and potentially perform a buffer overflowβ¦ until they hit a wall β the stack canary was in place.
What are Stack Canaries?
Stack Canaries are a secret random or static value placed between the local variables and the saved return address to protect against buffer overflow attacks. They are placed just before the return address and if the value is overwritten (attacker trying to overwrite return address), program immediately crashes but if they values are same, program assumes that no stack smashing was attempted.

There are different types of canaries that can be implemented π
Type | Example | Protection |
---|---|---|
Null canary | 0x00000000 | 0x00 |
Terminator canary | 0x00000aff | 0x00, 0x0a, 0xff |
Random canary | <any 4-byte value> | Usually starts with 0x00 |
Random XOR canary | Usually starts with 0x00 | |
64-bit canary | <8 bytes> | |
Custom canary |
More information on stack canaries can be found on SANS article.
Brute-forcing Canary
Knowing the basics of stack canaries, we can proceed to brute-force the static canary value. Since our program reads the canary from canary.txt
, this means it is not a dynamic value that changes with every run but static and remains same for every run.
After few minutes of source code analysis, testing and playing with the binary, we note the following:
read()
does not append NULL terminator at the end of string- Input after 64 characters overwrites canary value
If we tell binary to write 65 bytes, we can overwrite 1 byte of canary value (64 bytes buffer + 1 byte that we can overwrite) and rest of canary value stays intact because we do not overwrite it as weβre only consuming 1 extra byte after allocated buffer of 64 bytes.

Modified compiled binary for better visualization
In this way, we can find the canary value.
65th β First value of canary
66th β Second value of canary
67th β Third value of canary
68th β Fourth value of canary
Running the below pwntools
script gives us the canary value to be BiRd
.
1 | from pwn import * |
Now, we can easily overwrite the return address by finding the offset (cyclic pattern) and overwriting EIP (Revise here).
Overwriting Return Address (EIP)
We have to find the offset for overwriting the return address. Generate the cyclic pattern of 100 characters using cyclic 100
and place it after canary value.
1 | $ python2 -c 'print("A"*64 + "BiRd" + "aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaa")' |
Run the program in GDB, enter input bytes as 100 (as we have to find the offset now) and enter the above generated payload.
Programs crashes because EIP was overwritten, note the value inside EIP and execute cyclic -l eaaa
in GDB to find the offset value which comes out to be 16.
Itβs straightforward now and I wonβt go into much explaining because similar approach is explained here: (https://stillhacks.xyz/cryptocat-pwn-challenges/#4οΈβ£-Ret2Win-Challenge).
This is the final payload to solve the challenge.
1 | $ python2 -c 'print("A" * 64 + "BiRd" + "A" * 16 + "\x36\x93\x04\x08")' > payload |
Should you have any questions, type them below. π