[문제]


[풀이]

memcpy.pptx
18.54MB

 

 

→ 발표자료 만들다가 가져옴

readme

binary of "memcpy.c" source code (with real flag) will be executed under memcpy_pwn privilege if you connect to port 9022.

  • 실제 flag가 포함된 바이너리는 9022에서 memcpy_pwn 권한으로 실행되고 있다고 함

  • local에서는 flag 부분까지 잘출력되는데, 서버에서는 중간에 멈춰버림
  • 음.. 뭔가.. 로컬에서는 대부분 slow_memcpy가 시간이 매우 많이 걸리는데, 서버꺼는 별 차이가 없거나 오히려 fast_memcpy가 오래 걸리는 경우가 다수 보임

 

소스코드 분석 - memcpy.c

// compiled with : gcc -o memcpy memcpy.c -m32 -lm
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <sys/mman.h>
#include <math.h>

unsigned long long rdtsc(){
        asm("rdtsc");
}

char* slow_memcpy(char* dest, const char* src, size_t len){
	int i;
	for (i=0; i<len; i++) {
		dest[i] = src[i];
	}
	return dest;
}

char* fast_memcpy(char* dest, const char* src, size_t len){
	size_t i;
	// 64-byte block fast copy
	if(len >= 64){
		i = len / 64;
		len &= (64-1);
		while(i-- > 0){
			__asm__ __volatile__ (
			"movdqa (%0), %%xmm0\\n"
			"movdqa 16(%0), %%xmm1\\n"
			"movdqa 32(%0), %%xmm2\\n"
			"movdqa 48(%0), %%xmm3\\n"
			"movntps %%xmm0, (%1)\\n"
			"movntps %%xmm1, 16(%1)\\n"
			"movntps %%xmm2, 32(%1)\\n"
			"movntps %%xmm3, 48(%1)\\n"
			::"r"(src),"r"(dest):"memory");
			dest += 64;
			src += 64;
		}
	}

	// byte-to-byte slow copy
	if(len) slow_memcpy(dest, src, len);
	return dest;
}

int main(void){

	setvbuf(stdout, 0, _IONBF, 0);
	setvbuf(stdin, 0, _IOLBF, 0);

	printf("Hey, I have a boring assignment for CS class.. :(\\n");
	printf("The assignment is simple.\\n");

	printf("-----------------------------------------------------\\n");
	printf("- What is the best implementation of memcpy?        -\\n");
	printf("- 1. implement your own slow/fast version of memcpy -\\n");
	printf("- 2. compare them with various size of data         -\\n");
	printf("- 3. conclude your experiment and submit report     -\\n");
	printf("-----------------------------------------------------\\n");

	printf("This time, just help me out with my experiment and get flag\\n");
	printf("No fancy hacking, I promise :D\\n");

	unsigned long long t1, t2;
	int e;
	char* src;
	char* dest;
	unsigned int low, high;
	unsigned int size;
	// allocate memory
	char* cache1 = mmap(0, 0x4000, 7, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
	char* cache2 = mmap(0, 0x4000, 7, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
	src = mmap(0, 0x2000, 7, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);

	size_t sizes[10];
	int i=0;

	// setup experiment parameters
	for(e=4; e<14; e++){	// 2^13 = 8K
		low = pow(2,e-1);
		high = pow(2,e);
		printf("specify the memcpy amount between %d ~ %d : ", low, high);
		scanf("%d", &size);
		if( size < low || size > high ){
			printf("don't mess with the experiment.\\n");
			exit(0);
		}
		sizes[i++] = size;
	}

	sleep(1);
	printf("ok, lets run the experiment with your configuration\\n");
	sleep(1);

	// run experiment
	for(i=0; i<10; i++){
		size = sizes[i];
		printf("experiment %d : memcpy with buffer size %d\\n", i+1, size);
		dest = malloc( size );

		memcpy(cache1, cache2, 0x4000);		// to eliminate cache effect
		t1 = rdtsc();
		slow_memcpy(dest, src, size);		// byte-to-byte memcpy
		t2 = rdtsc();
		printf("ellapsed CPU cycles for slow_memcpy : %llu\\n", t2-t1);

		memcpy(cache1, cache2, 0x4000);		// to eliminate cache effect
		t1 = rdtsc();
		fast_memcpy(dest, src, size);		// block-to-block memcpy
		t2 = rdtsc();
		printf("ellapsed CPU cycles for fast_memcpy : %llu\\n", t2-t1);
		printf("\\n");
	}

	printf("thanks for helping my experiment!\\n");
	printf("flag : ----- erased in this source code -----\\n");
	return 0;
}

하나씩 뜯어보자

rdtsc 함수

unsigned long long rdtsc(){
        asm("rdtsc");
}

 

main 함수-1

int main(void){

	setvbuf(stdout, 0, _IONBF, 0);
	setvbuf(stdin, 0, _IOLBF, 0);

	printf("Hey, I have a boring assignment for CS class.. :(\\n");
	printf("The assignment is simple.\\n");

	printf("-----------------------------------------------------\\n");
	printf("- What is the best implementation of memcpy?        -\\n");
	printf("- 1. implement your own slow/fast version of memcpy -\\n");
	printf("- 2. compare them with various size of data         -\\n");
	printf("- 3. conclude your experiment and submit report     -\\n");
	printf("-----------------------------------------------------\\n");

	printf("This time, just help me out with my experiment and get flag\\n");
	printf("No fancy hacking, I promise :D\\n");

	unsigned long long t1, t2;
	int e;
	char* src;
	char* dest;
	unsigned int low, high;
	unsigned int size;
	// allocate memory
	char* cache1 = mmap(0, 0x4000, 7, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
	char* cache2 = mmap(0, 0x4000, 7, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
	src = mmap(0, 0x2000, 7, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);

	size_t sizes[10];
	int i=0;

mmap의 사용

  • cache1,2
    • 0x4000만큼 다른 프로세스와 대응 영역을 공유하지 않고, 읽고 쓰고 실행이 모두 가능한 것으로 보임.
    • MAP_ANONYMOUS는 어떠한 파일하고도 연결되지 않고 0으로 초기화된 영역으로 fd는 무시되지만 어떤 경우에는 -1이 요구된다고 함
    • offset는 0으로 고정한다고 함!
    • http://jake.dothome.co.kr/user-virtual-maps-mmap2/
  • src는 cache와 같은 방법으로 0x2000만큼 매핑

 

main 함수-2

	size_t sizes[10];
	int i=0;

	// setup experiment parameters
	for(e=4; e<14; e++){	// 2^13 = 8K
		low = pow(2,e-1);
		high = pow(2,e);
		printf("specify the memcpy amount between %d ~ %d : ", low, high);
		scanf("%d", &size);
		if( size < low || size > high ){
			printf("don't mess with the experiment.\\n");
			exit(0);
		}
		sizes[i++] = size;
	}

	sleep(1);
	printf("ok, lets run the experiment with your configuration\\n");
	sleep(1);
  • 2^4~2^13의 범위에서 memcpy amount를 사용자로부터 입력 받음
  • size가 low와 high 사이에 없으면 바이너리가 종료됨
  • 10번 수행하고 끝내는 것으로 보임

 

main 함수-3

	// run experiment
	for(i=0; i<10; i++){
		size = sizes[i];
		printf("experiment %d : memcpy with buffer size %d\\n", i+1, size);
		dest = malloc( size );

		memcpy(cache1, cache2, 0x4000);		// to eliminate cache effect
		t1 = rdtsc();
		slow_memcpy(dest, src, size);		// byte-to-byte memcpy
		t2 = rdtsc();
		printf("ellapsed CPU cycles for slow_memcpy : %llu\\n", t2-t1);

		memcpy(cache1, cache2, 0x4000);		// to eliminate cache effect
		t1 = rdtsc();
		fast_memcpy(dest, src, size);		// block-to-block memcpy
		t2 = rdtsc();
		printf("ellapsed CPU cycles for fast_memcpy : %llu\\n", t2-t1);
		printf("\\n");
	}

	printf("thanks for helping my experiment!\\n");
	printf("flag : ----- erased in this source code -----\\n");
	return 0;
}
  • 입력한 size만큼 malloc으로 할당하여 dest에 저장함
  • slow_memcpy함수를 실행하기 전 시간과, 실행 후 시간을 측정해 slow_memcpy cycle이 도는 시간을 반환해줌
    • fast_memcpy도 마찬가지

 

slow_memcpy

char* slow_memcpy(c]har* dest, const char* src, size_t len){
	int i;
	for (i=0; i<len; i++) {
		dest[i] = src[i];
	}
	return dest;
}
  • 1바이트씩 직접 매핑하고 있음

 

fast_memcpy

char* fast_memcpy(char* dest, const char* src, size_t len){
	size_t i;
	// 64-byte block fast copy
	if(len >= 64){
		i = len / 64;
		len &= (64-1);
		while(i-- > 0){
			__asm__ __volatile__ ( //프로그래머가 코딩한 순서대로 assemble 됨
			"movdqa (%0), %%xmm0\\n"
			"movdqa 16(%0), %%xmm1\\n"
			"movdqa 32(%0), %%xmm2\\n"
			"movdqa 48(%0), %%xmm3\\n"
			"movntps %%xmm0, (%1)\\n"
			"movntps %%xmm1, 16(%1)\\n"
			"movntps %%xmm2, 32(%1)\\n"
			"movntps %%xmm3, 48(%1)\\n"
			::"r"(src),"r"(dest):"memory");
			dest += 64;
			src += 64;
		}
	}

	// byte-to-byte slow copy
	if(len) slow_memcpy(dest, src, len);
	return dest;
}

 

문제 해결

💡 xmm 레지스터들에 의해 메모리에 저장되는데, 해당 주소가 16byte 단위로 저장되는 지 확인해볼 것
  • 레지스터의 값들이 저장되는 곳은 dest임
    • dest 주소를 확인하는 과정이 필요함 → gdb / printf로 출력
  • local에서 컴파일 했을 때에는 멀쩡했음(해당 취약점은 ubuntu 16.04까지에서만 통함. 이후 버전의 로컬 환경에서 컴파일하면 memory를 자동적으로 align해줘서 flag까지 출력됨)

  • 서버에서 확인한 결과 experiment 3와 5에서 이상함을 감지함
    • 원래라면 16byte 단위이기 때문에 0x10씩 늘어나야 함
    • 즉, experiment 1의 시작이 0으로 끝났다면 모두 0으로 끝나야 한다는 소리임
    • experiment2와 3을 살펴보면 0x8889420 → 0x8889438임
    • 16byte만큼 추가됐는데, 총 늘어난 크기는 24byte임
    • 8byte가 더 추가된 것
    • 내가 넣은 값에 대해서 8byte씩 추가하고 있으므로 입력 값 자체를 8byte씩 더 입력해주면 됨

  • 오! 64를 입력하는 부분부터 8byte씩 추가로 입력해줬더니 flag 부분이 출력됨

 

Exploit Code - Pwntools

from pwn import *

p = remote('pwnable.kr', 9022)

p.recvuntil("8 ~ 16 : ")
p.sendline("8")

p.recvuntil(" : ")
p.sendline("16")

p.recvuntil(" : ")
p.sendline("32")

p.recvuntil(" : ")
p.sendline("72")

p.recvuntil(" : ")
p.sendline("136")

p.recvuntil(" : ")
p.sendline("264")

p.recvuntil(" : ")
p.sendline("520")

p.recvuntil(" : ")
p.sendline("1032")

p.recvuntil(" : ")
p.sendline("2056")

p.recvuntil(" : ")
p.sendline("4104")

p.recvuntil("flag : ")
print p.recv(1000)
p.interactive()


flag

🍒 1_w4nn4_br34K_th3_m3m0ry_4lignm3nt

 

'Wargame > Pwnable.kr' 카테고리의 다른 글

[Pwnable.kr] blukat  (0) 2023.02.27
[Pwnable.kr] asm  (0) 2023.02.26
[Pwnable.kr] uaf  (0) 2022.10.15
[Pwnable.kr] cmd2  (0) 2022.10.15
[Pwnable.kr] cmd1  (0) 2022.10.15

+ Recent posts