Are you tired of hacking?, take some rest here.

Just help me out with my small experiment regarding memcpy performance. 

after that, flag is yours.


http://pwnable.kr/bin/memcpy.c


ssh memcpy@pwnable.kr -p2222 (pw:guest)



해킹이 아니라고? 쉬울것 같은 예감이 든다 ^~^--> 아니었다 ㅎㅎ



// 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;

}



memcpy@ubuntu:~$ cat readme

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

execute the binary by connecting to daemon(nc 0 9022).




오 그냥 실행만 하면 되는군 하고

실행시켜서 입력값을 대충 넣어봣더니



중간에 짤려버린다 ㅂㄷ..




이번엔 5번째 까지 가다가 끊겼다


뭐지..

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");

        }


저기서 오류가 나서 끊기는게 아닌가 싶다.

이코드만 무사히 통과하면 flag를 볼 수 있을 것 같다.




(푸는중)


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;

}


slow_memcpy는 그냥 복사하듯이 값을 하나씩 옮긴다

fast_memcpy는 일단 어셈으로 짜여있다 한번 자세히 보도록하자


// 64-byte block fast copy


??64byte블록이란느 말이있어서 일단 염두에 두었다


if(len) slow_memcpy(dest, src, len);


len이 0이아니면 slow_memcpy를 한번 더 돌아서 64비트로 나눈 나머지를 cpy해줘야한다


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;

                }

movdqa와 movntps가 뭔지 찾아보았따


SSE병렬 프로그래밍이라는게 있는데

128bit씩 한꺼번에 연산을 한다고 한다

속도가 엄청 빨라지는 연산이다


여기서 movdqa move alinged double quard word의 줄임말으로써

128bit 레지스터나 메모리에 정렬된 값을 옮길때 쓰는 명령어다(정렬되지 않았을 떄는 movdqu)

(정렬된 메모리의 연산이 훨씬 빠르기 떄문에 연산 전에 정렬을 해준다고 한다)

http://www.slideshare.net/KooKyeongWon/0204-sse




MOVNTPS - Non-temporal store of four packed single-precision floatingpoint values from an XMM register into memory

MOVNTPS--Move Aligned Four Packed Single-FP Non Temporal

Opcode

Instruction

Description

0F 2B /r

MOVNTPS m128xmm

Move packed single-precision floating-point values from xmm to m128, minimizing pollution in the cache hierarchy.

Description

Moves the double quadword in the source operand (second operand) to the destination operand (first operand) using a non-temporal hint to minimize cache pollution during the write to memory. The source operand is an XMM register, which is assumed to contain four packed single-precision floating-point values. The destination operand is a 128-bit memory location.

The non-temporal hint is implemented by using a write combining (WC) memory type protocol when writing the data to memory. Using this protocol, the processor does not write the data into the cache hierarchy, nor does it fetch the corresponding cache line from memory into the cache hierarchy. The memory type of the region being written to can override the non-temporal hint, if the memory address specified for the non-temporal store is in an uncacheable (UC) or write protected (WP) memory region.

Because the WC protocol uses a weakly-ordered memory consistency model, a fencing operation such as SFENCE should be used in conjunction with MOVNTDQ instructions if multiple processors might use different memory types to read/write the memory location.

http://www.jaist.ac.jp/iscenter-new/mpc/altix/altixdata/opt/intel/vtune/doc/users_guide/mergedProjects/analyzer_ec/mergedProjects/reference_olh/mergedProjects/instructions/instruct32_hh/vc197.htm


그니까 movadq에서 데이터를 레지스터로 옮기고 movntdq에서 레지스터에서 메모리로 데이터를 옮기는 것 같다

이 연산을 16bit 4블록을 동시에 옮기는데 그래서 64비트 단위 인것 같다.


그리고 movadq에서 <정렬된 데이터>라는 의미는 주솟값이 64의 배수라는 뜻 인것 같다



즉 정렬되지 않았을 떄는 에러를 일으킨다

src가 정렬되 있는 데이터이기 때문에 des가 정렬되어 있지 않으면 데이터가 오버플러우 되거나 세그먼트 폴트가 나타날 것이다

복사해서 한번 돌려보자

gcc로 컴파일 할떄 -lm을 붙여줘야한다

pow함수 때문


이럴땐, gcc source.c -lm

 

-lm을 링크해야 하는 이유

: man 페이지에서 해당 함수를 위해 사용해야 할 별도의 라이브러리가 있는지 확인해 합니다.

 


libm.a 라이브러리를 지정해줘야 math.h 에 있는 pow() 를 쓸 수 있다.
 (참고) libc.a 는 gcc 에서 기본적으로 include 된다.

http://blog.daum.net/_blog/BlogTypeView.do?blogid=0SsSt&articleno=210


근데 컴파일이 에러하나 없이 잘되더라..


윗줄에 

// compiled with : gcc -o memcpy memcpy.c -m32 -lm

라는 주석이 있었는데 이렇게 해야 에러가 뜨는가보다.. 삽질..



0x080486f0에서 에러가 난다

따라갔다


여기서 에러가 난댄다



...



답답해서 고민하다가

memcpy.c에 des주소를 출력하는 소스를 추가해봤따

des가 0x8071058로 시작되서

정렬이 안되있어서 에러가 났다

끝이 8이기 때문ㅇ 0으로 만들어주려면 8이 더필요했다

32에다 8을 덯나 40을 넣어줬다

조금씩 수정하면서 계속해봤따32


오 한칸 더갔다


계속해보자

계속하다보니 8씩 더해서 넣어주면 되는것 같다


플래그 땄다!!!

이제 nc 0 9022로 해보자

길어서 짤리는데

암튼 플래그땄다 

값은 8 16 40 72 136 264 520 1032 2056 5004

넣었다


ㅠㅠ감격ㄱ 이제 rookiss풀러가야지~






'war game > pwnable.kr' 카테고리의 다른 글

pwnable.kr tiny_easy 30pt  (0) 2016.10.18
pwnable.kr simple login 50pt  (0) 2016.10.18
pwnable.kr codemap 10pt  (0) 2016.10.12
pwnable.kr uaf 8pt  (0) 2016.10.12
pwnable.kr cmd2 9pt  (0) 2016.10.12

I have a binary that has a lot information inside heap.

How fast can you reverse-engineer this?

(hint: see the information inside EAX,EBX when 0x403E65 is executed)


download: http://pwnable.kr/bin/codemap.exe



ssh codemap@pwnable.kr -p2222 (pw:guest)



이제 Toddler's Bottle도 2문제 남았다!

좀만 더 힘내보잡!


일단 .ssh로 접속했는데 permission denied로 codemap.c를 볼 수가 없었다. 

readme를 읽어보니


codemap@ubuntu:~$ cat readme

reverse engineer the 'codemap.exe' binary, then connect to codemap daemon(nc 0 9021),

the daemon will ask you some question, provide the correct answer to get flag.



보니까 .exe파일이어서 일단 다운로드링크로 다운을 받았다.

이 exe파일을 리버싱해서 codemap daemon에 접속했을 때 


codemap@ubuntu:~$ nc 0 9021

What is the string inside 2nd biggest chunk? :


라는 물음의 답을 찾아야 한다.



codemap을 돌리면 다음화면까지 뜨고 꺼져버린다

힌트인 hint: see the information inside EAX,EBX when 0x403E65 is executed

를 생각하면서 올리디버거에 올려보자


     


...



올리디버거로 돌리니 entry point가 00100000대 여서 도무지 어떻게 풀어야 할지 감이 안왔다 ㅠㅠ

아이다로 넣어보니 00400000대로 들어왔으나 bp 설정후 디버깅하니 다시 100000대로 들어왔다

왜 그런지 아직도 모르겠다 ㅠㅠ

왜그럴까 혹시 아는사람잇으면 댓글이라도 달아줫으면 좋겟답 ㅠㅠㅠ 답답


결국 구글링했는데 codemap이라는 게 


Codemap is a binary analysis tool for "run-trace visualization" provided as IDA plugin.


이란다 

Codemap 을 쓰고 돌리면 DB에 bp를 기준으로 데이터가 저장된다는데

이를 읽어 오기만 하면 답이 나온다는 것..


일단 정품아이다가 생기면 다시 풀어봐야겠다


http://codemap.kr/

codemap 다운로드 사이트

'war game > pwnable.kr' 카테고리의 다른 글

pwnable.kr simple login 50pt  (0) 2016.10.18
pwnable.kr memcpy 10pt  (1) 2016.10.12
pwnable.kr uaf 8pt  (0) 2016.10.12
pwnable.kr cmd2 9pt  (0) 2016.10.12
pwnable.kr cmd1 1pt  (0) 2016.10.11

Mommy, what is Use After Free bug?


ssh uaf@pwnable.kr -p2222 (pw:guest)








use after free문제이다


use after free는

 file:///C:/Users/Home/Downloads/USE_AFTER_FREE.pdf

cd80님의 문서를 보고 공부했다.

cd80님의 문서는 읽을때마다 정말 좋은것 같다


class Human에 give_shell()이라는 함수가 있는데


m,w를 free(delete)->reuse(data덮어쓰기)->introduce주소를 give_shell()주소로 덮어쓰기->introduce호출


하면 될 것 같다.


일단 틀은 잡았는데 어떻게 메모리를 들여다 볼지 막막했다.

gdb를 돌ㄹ보아야겠다


(푸는중)




스위치문을 찾았다


1번 옵션 -> introduce()를 콜하는 case 1이 있는 곳(0x400fcd~0x401000(2번시작주소))을 읽어보면


[rbp-38]에 m

[rbp-0x30]에 w가 있을 것 같다.

여기 주소를 한번 따라가 봐야겠다.


m의 주소는 0x01494040

w의 주소는 0x01494090

이라고 생각하고 들어가봤다



두 구조체를 발견했다

나이와 이름을 발견했다.

그렇다면 덮어써야할 함수는 어디있을까?


위의 case1에서 어셈블리를 해석해보면

call rdx라는 함수를 호출하는데 

rdx는 00401570+0x08안의 값이 들어간다


따라가니 <_ZTV3MAN+24>가 있었고 0x004015f0이 있었다.

이게 introduce()의 주소인 것 같다.


그렇다면 getshell()의 주솟값은 어디있을까?ㅠㅠ


일단 3번으로 구조체를 delete한후 2번 옵션으로 AAAA라는 값이 있는 파일을 read해서 data에 넣은후

다시 저 메모리를 방문해 보았따.



아까 introduce()주소와 관련된 곳에 AAAA가 덮어써졋다

user after free 취약점에 의해 발생한 현상이다

(그 밑에 AAAA는 바로 전에 실수로 delete를 안하고 read했더니 들어갔던 값이다... 실수 ㅎㅎ)


그렇다면 우리가 해야할 일은

getshell()로 보내는 값을 찾아서 그 주소값을 파일 내용에 넣은다음

돌리면 getshell()을 실행시킬 수 있다.


아 getshell()어떻게 찾지하다가 



human woman man과 관련된 함수 테이블을 찾아봤따


case1에서 호출하는 것은 0x0040117a의 주소였다

따라가보니



get_shell()은 human의 private성분이었음으로 

아무튼

zt15human의 주솟값을 하나씩 넣어봤다

오 진짜 introduce가 있었다


0x0040117a엔 뭐가있나 해서 들어가보니


shell함수다


ㅠㅠㅠㅠㅠ


 human에 있는 40117a의 주소에서 0x08을 뺀 주소를 보면

0x00401588을 넣어보자



ㅠㅠㅠ


풀이가 너무 정신 없었던 것 같지만 잘 봐주면 좋겠다


풀이를 쓰는건 힘들지만 풀이를 쓰고 나면 머리속에 더 정리가 더 잘 되는 느낌이라

기분이 좋은것 같당



'war game > pwnable.kr' 카테고리의 다른 글

pwnable.kr memcpy 10pt  (1) 2016.10.12
pwnable.kr codemap 10pt  (0) 2016.10.12
pwnable.kr cmd2 9pt  (0) 2016.10.12
pwnable.kr cmd1 1pt  (0) 2016.10.11
pwnable.kr lotto 2pt  (0) 2016.10.11

Daddy bought me a system command shell.

but he put some filters to prevent me from playing with it without his permission...

but I wanna play anytime I want!


ssh cmd2@pwnable.kr -p2222 (pw:flag of cmd1)


지금까지의 문제중 가장 점수 높은 문제다

접속해보자@_@


아 비번이 cmd1의 flag다 



mommy now I get what PATH environment is for :)

저번 포스팅으로 가서 가져왔다







cmd에 =, PATH, export / ` flag를 우회하고 있다


일단 cmd1에서 썻던 방법은 다 필터링 되고 있다ㅠㅠ



처음엔 이렇게도 해봤는데

'가 필터링이었다.. python 스크립트에서 '를 안쓰는 방법을 생각해보았다

는 없었고

bash쉘의 $ 특징을 이용했다


오오 된다된다




'war game > pwnable.kr' 카테고리의 다른 글

pwnable.kr codemap 10pt  (0) 2016.10.12
pwnable.kr uaf 8pt  (0) 2016.10.12
pwnable.kr cmd1 1pt  (0) 2016.10.11
pwnable.kr lotto 2pt  (0) 2016.10.11
pwnable.kr blackjack 1pt  (0) 2016.10.11


#include <stdio.h>

#include <string.h>


int filter(char* cmd){

        int r=0;

        r += strstr(cmd, "flag")!=0;            //flag, sh, tmp문자열 필터링

        r += strstr(cmd, "sh")!=0;

        r += strstr(cmd, "tmp")!=0;

        return r;

}

int main(int argc, char* argv[], char** envp){

        putenv("PATH=/fuckyouverymuch");        //path환경변수 초기화

        if(filter(argv[1])) return 0;

        system( argv[1] );

        return 0;

}



필터링 우회하는데는 정규식만한게 없는것 같다.








argv에 export를 넣어줘서 path를 다시 넣어주는 방법도 있다.

'war game > pwnable.kr' 카테고리의 다른 글

pwnable.kr uaf 8pt  (0) 2016.10.12
pwnable.kr cmd2 9pt  (0) 2016.10.12
pwnable.kr lotto 2pt  (0) 2016.10.11
pwnable.kr blackjack 1pt  (0) 2016.10.11
pwnable.kr coin1 6pt  (0) 2016.10.11

Mommy! I made a lotto program for my homework.

do you want to play?



ssh lotto@pwnable.kr -p2222 (pw:guest)



#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <fcntl.h>


unsigned char submit[6];


void play(){


        int i;

        printf("Submit your 6 lotto bytes : ");

        fflush(stdout);


        int r;

        r = read(0, submit, 6);


        printf("Lotto Start!\n");

        //sleep(1);


        // generate lotto numbers

        int fd = open("/dev/urandom", O_RDONLY);

        if(fd==-1){

                printf("error. tell admin\n");

                exit(-1);

        }

        unsigned char lotto[6];

        if(read(fd, lotto, 6) != 6){

                printf("error2. tell admin\n");

                exit(-1);

        }

        for(i=0; i<6; i++){

                lotto[i] = (lotto[i] % 45) + 1;         // 1 ~ 45

        }

        close(fd);


        // calculate lotto score

        int match = 0, j = 0;

        for(i=0; i<6; i++){

                for(j=0; j<6; j++){

                        if(lotto[i] == submit[j]){

                                match++;

                        }

                }

        }


        // win!

        if(match == 6){

                system("/bin/cat flag");

        }

        else{

                printf("bad luck...\n");

        }


}


void help(){

        printf("- nLotto Rule -\n");

        printf("nlotto is consisted with 6 random natural numbers less than 46\n");

        printf("your goal is to match lotto numbers as many as you can\n");

        printf("if you win lottery for *1st place*, you will get reward\n");

        printf("for more details, follow the link below\n");

        printf("http://www.nlotto.co.kr/counsel.do?method=playerGuide#buying_guide01\n\n");

        printf("mathematical chance to win this game is known to be 1/8145060.\n");

}


int main(int argc, char* argv[]){


        // menu

        unsigned int menu;


        while(1){


                printf("- Select Menu -\n");

                printf("1. Play Lotto\n");

                printf("2. Help\n");

                printf("3. Exit\n");


                scanf("%d", &menu);


                switch(menu){

                        case 1:

                                play();

                                break;

                        case 2:

                                help();

                                break;

                        case 3:

                                printf("bye\n");

                                return 0;

                        default:

                                printf("invalid menu\n");

                                break;

                }

        }

        return 0;

}



 for(i=0; i<6; i++){

                for(j=0; j<6; j++){

                        if(lotto[i] == submit[j]){

                                match++;

                        }

                }

        }

이코드를 잘보면 lotto에 들어있는 6가지 수 중에서 submit한 문자가 하나가 잇으면 match가 6이 된다.

1글자만 맞추면 된다는 것이다.


그래서 111111만 20번은 넣어본것 같다.

for(i=0; i<6; i++){

                lotto[i] = (lotto[i] % 45) + 1;         // 1 ~ 45

        }

33부터 우리가 char형으로 입력할 수 있는 값이다. '!'=33 """=34 '#'=35... '-'=45 .... '0'=48->4 '1'=49->5...


만약 submit으로  1 1 1 1 1 1 을 넣었다면 1=49 이므로 lotto와 맞을 수가 없다

따라서 아스키가 45보다 작은 문자를 넣어줘야한다

######을 넣어봤다.

성공!!



'war game > pwnable.kr' 카테고리의 다른 글

pwnable.kr cmd2 9pt  (0) 2016.10.12
pwnable.kr cmd1 1pt  (0) 2016.10.11
pwnable.kr blackjack 1pt  (0) 2016.10.11
pwnable.kr coin1 6pt  (0) 2016.10.11
pwnable.kr shellshock 1pt  (0) 2016.10.11

+ Recent posts