Skip to content

Instantly share code, notes, and snippets.

@Jinmo

Jinmo/README.md Secret

Created December 25, 2016 06:05
Show Gist options
  • Save Jinmo/bbcf852059873c277f1272c61420c69a to your computer and use it in GitHub Desktop.
Save Jinmo/bbcf852059873c277f1272c61420c69a to your computer and use it in GitHub Desktop.
Christmas CTF 2016 with noname cat

It's RPG game on server. They gave the server's binary on me. The pseudo code is..

main {
  butler: HP 1000 ATK 74 LUK 5 DEF 50
  Well512 random with timestamp seed
  A = 0
  B = 0
  if input is n {
    A++
  }
  if input is m {
    prints timestamp and B++
  }
  if input is y {
    I = input between 0 ~ 22. if input not on the range, exit.
    cat: HP timestamp%300 ATK Well512(timestamp%1000,A)%60 LUK Well512(timestamp%1000,A)%10 DEFWell512(timestamp%1000,B)%76
    Fight
    if cat wins {
      gives I-th letter of flag.
    }
  }
}
Well512(seed, round) {
  returns different random value if seed or round is different.
}
Fight {
  Attack cat player
  Attack player cat
}

Attack(A, B) {
  if A.ATK > B.DEF and Well512(new timestamp, 1) % 100 > LUK {
    B.HP -= A.ATK - B.DEF
  }
  if B.HP < 0 {
    A win
  }
}

There are Well512 PRNG which determines the cat's PRNG. I've just ported it to python and tested the solution locally, and it worked.

But on server, it didn't work. So I just brute forced every letter of flag.

python withnonamecat.py | tee|grep hereisflag &
# executed about 8 times
#include <stdint.h>
unsigned int well512(int ts, unsigned int a2)
{
int v2; // ST1C_4@5
int v3; // ST20_4@5
unsigned int v4; // ST1C_4@5
int v5; // ST1C_4@5
unsigned int v6; // ST24_4@5
int v8; // [sp+10h] [bp-58h]@1
unsigned int i; // [sp+14h] [bp-54h]@1
unsigned int j; // [sp+14h] [bp-54h]@4
int state[16]; // [sp+28h] [bp-40h]@2
v8 = 0;
for ( i = 0; i <= 0xF; ++i )
state[(uint64_t)i] = i + ts;
for ( j = 0; j < a2 % 0x2710; ++j )
{
v2 = state[(uint64_t)(((uint8_t)v8 + 13) & 0xF)];
v3 = (state[(uint64_t)(unsigned int)v8] << 16) ^ v2 ^ state[(uint64_t)(unsigned int)v8] ^ (v2 << 15);
v4 = state[(uint64_t)(((uint8_t)v8 + 9) & 0xF)];
v5 = (v4 >> 11) ^ v4;
state[(uint64_t)(unsigned int)v8] = v5 ^ v3;
v6 = state[(uint64_t)(unsigned int)v8] ^ 32 * state[(uint64_t)(unsigned int)v8] & 0xDA442D20;
v8 = ((uint8_t)v8 + 15) & 0xF;
state[(uint64_t)(unsigned int)v8] ^= (v5 << 28) ^ (v3 << 18) ^ v6 ^ v3 ^ 4
* state[(uint64_t)(unsigned int)v8];
}
return (unsigned int)state[(uint64_t)(unsigned int)v8];
}
#coding: utf8
from pwn import *
import datetime
import time
import ctypes
import random
lib = ctypes.CDLL('./withnonamecat.so')
FLAG_LEN = 22
HP, LUK, ATK, DEF = 1000, 5, 74, 50
flag = ''
HOST, PORT = '0.0.0.0', 9000
HOST, PORT = 'devslave.com', 9001
u = lambda x: x & 0xffffffff
timer = None
while timer is None or timer in [0, 1, 2, 3, 4, 5, 7, 8, 9, 10, 11, 12, 15, 16, 17, 18, 19, 20, 21]:
timer = random.randint(0, FLAG_LEN)
while timer < FLAG_LEN:
r = remote(HOST, PORT)
r.recvuntil('체크해본다.')
r.sendline('m')
years = int(r.recvuntil('년', drop=True))
months = int(r.recvuntil('월', drop=True))
days = int(r.recvuntil('일', drop=True))
hours = int(r.recvuntil('시', drop=True))
minutes = int(r.recvuntil('분', drop=True))
seconds = int(r.recvuntil('초', drop=True))
d = datetime.datetime(years, months, days, hours, minutes, seconds)
ts = int(time.mktime(d.timetuple()))
# print ts, time.time()
forward = 1
checktime = 2
catHP = ts % 0x12C
found = False
while True:
catLUK = u(lib.well512(ts % 0x3e8, forward)) % 0xA
catATK = u(lib.well512(ts % 0x3e8, forward)) % 0x3C
catDEF = u(lib.well512(ts % 0x3e8, checktime)) % 0x4C
if (catATK > DEF and catDEF > ATK) and (catATK - DEF) * 180 > 1000 and catLUK > 5:
found = True
break
if forward == 200:
forward = 0
checktime += 1
if checktime > 200:
found = False
break
forward += 1
if found == False and 0:
time.sleep(1)
r.close()
continue
forward = random.randint(0, 30)
checktime = random.randint(1, 30)
for i in xrange(checktime - 1):
r.recvuntil('체크해본다.')
r.sendline('m')
for i in xrange(forward):
r.recvuntil('체크해본다.')
r.sendline('n')
r.recvuntil('체크해본다.')
r.sendline('y')
r.recvuntil('...')
r.sendline(str(timer + 1))
#print r.recvuntil('==', drop=True)
#r.interactive()
#print 1337, hex(u(lib.well512(ts % 0x3e8, forward)))
#print 1337 ,hex(u(lib.well512(ts % 0x3e8, checktime)))
#print 1337, catHP, catATK, catLUK, catDEF
#print 1337, forward, checktime
data = r.recvall()
if '{' not in data:
r.close()
continue
# print `data`
data = data.split('\xed\x94\x8c\xeb\x9e\x98\xea\xb7\xb8 {')[1]
c = data[0]
flag += c
print 'hereisflag', `flag`, timer
timer += 1
@KSHMK
Copy link

KSHMK commented Jan 2, 2017

I think that replacing this code with line 22 in withnonamecat.py might be a better way to improve readability.
int(time.mktime(time.strptime(P, '\n%Y년 %m월 %d일 %H시 %M분 %S초'))) - time.timezone

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment