The following post by 0xb0b is licensed under CC BY 4.0
L1 Keycard
We find the keycard for the first Side Quest hidden in the task of the first day of the TryHackMe Advent Of Cyber. It asks the question What's with all these GitHub repos? Could they hide something else?
Day 1: Maybe SOC-mas music, he thought, doesn't come from a store?
So let's take a look at the repositories we have available. In the scenario of the task, the following script is downloaded and executed. It is from the profile MM-WarevilleTHM.
The C2-Server is a Flask app that provides a login with session-based authentication. It allows an admin to log in with hardcoded credentials (ADMIN_USERNAME and ADMIN_PASSWORD) and access a dashboard and data page. Users who are not logged in will be redirected to the login page. With a valid session, we are able to reach /dashboard. The secret_key is used for secure session management, and the app runs on host="0.0.0.0" and port=8000. Since we have access to the secret_key we are able to craft our own session cookie to access the dashboard.
On the machine for the first task of Advent Of Cyber, port 8000 is also available in addition to the YouTube to MP3 converter. Here we see a login to Bloatware-C2. This might be the C2-server of Bloatware-WarevilleTHM. We have a login in front of us.
We give it a try and craft a session cookie using flask-unsing, since we know the secret used. In this case, we need to use the --legacy tag to get a working cookie. This might depend on different versions of flask-unsing.
We add that cookie like the following with the web development tools in our browser.
After that, we are able to access the /dashboard and find the first keycard which contains the password for the zip file of the first side quest.
What is the password the attacker used to register on the site?
We unpack the ZIP with the password of the first keycard and find a PCAP file. We find a port scan, traffic on port 22, and a lot of traffic on port 80.
Since the first question is about passwords, we search for this keyword in the packet details. We click on find again and again and find on packet 1532 the first credentials used for registering the user frostyfox.
What is the password that the attacker captured?
We continue with our search and on packet no. 1535 we find the credentials of mcskidy.
The first two questions could also be answered very quickly by using grep to search for the entries with password in the PCAP.
What is the password of the zip file transferred by the attacker?
The next question asks for the password of a transferred zip file. But we cannot find it in the HTTP Object via File -> Export Objects -> HTTP.
Nevertheless, there are interesting files that could be of interest to us later on; it is the file ff.
We continue and first filter out the traffic on port 22 and port 80 and also ignore the Nmap traffic. We find traffic on ports 9001 and 9002. The traffic on port 9001 seems just to be gibberish; it might be encrypted.
Extract The Zip
This step is not crucial to answering the current question but the next.
A zip file starts with the magic bytes PK, so we have to look for that. First, we inspect the traffic on port 9002.
Here we see some sort of SQL file, which is mentioned in the last question.
Directly From Traffic Found
We can extract the ZIP file by following the TCP Stream of the traffic on port 9002.
The next thing we do is to save the raw data to a file.
It is indeed a zip file containing the elves.sql file. But we are not able to crack the password used.
From Raw PCAP
This is another approach to extracting the zip. From the PCAP file we can see there is some data with the magic bytes PK in it and also the elves.sql file.
The idea is now to extract all the data fields and convert the hexadecimal data into output.hex back into its raw binary form and saves the result in a file named hex. We then are able to retrieve the zip via binwalk -e hex using binwalk it analyzes the binary file hex and attempts to extract any embedded files, data, or compressed archives.
tshark -r traffic.pcap -Y 'http or tcp' -T fields -e data > output.hex
xxd -r -p output.hex hex
binwalk -e hex
We were able to retrieve the zip file:
Decrypt The Traffic
Since we were not able to crack the password for the zip, we have to look deeper into the traffic. We may be able to find out how the zip was created. We remember the traffic on port 9001.
As already mentioned, this is encrypted.
We look at the binaries that were transmitted in the course of the communication. Including the file ff.
As reverse engineering of this specific binary is somewhat overwhelming, we decide to check to scan for any malware using Virustotal. For this, we calculate the MD5 checksum of that file.
Next, we use the hash to search for known entries at Virustotal...
How this encryption works or is used can be found in pel.c.
It implements a Packet Encryption Layer (PEL) for a Tiny Shell application, utilizing AES-CBC-128 encryption and HMAC-SHA1 for integrity verification. Here's a concise summary:
Encryption Mode:
Uses AES in CBC mode with a 128-bit key.
IVs (Initialization Vectors) are generated using SHA-1 and process-specific data.
Message Authentication:
Integrity is ensured using HMAC-SHA1.
A packet counter (p_cntr) is included to prevent replay attacks.
Session Initialization:
Client Side: Generates two IVs, sets up encryption contexts, and performs a challenge-response handshake with the server.
Server Side: Receives IVs from the client, sets up encryption contexts, and validates the client's handshake challenge.
Message Transmission:
Encrypts plaintext in 16-byte blocks (AES block size).
Adds padding for alignment and appends an HMAC for integrity.
Sends the combined encrypted message and HMAC to the receiver.
Message Reception:
Verifies the HMAC to ensure integrity.
Decrypts ciphertext block by block using AES-CBC
pel.c
/* * Packet Encryption Layer for Tiny SHell, * by Christophe Devine <devine@cr0.net>; * this program is licensed under the GPL. */#include<sys/types.h>#include<sys/socket.h>#include<sys/time.h>#include<unistd.h>#include<string.h>#include"pel.h"#include"aes.h"#include"sha1.h"/* global data */int pel_errno;struct pel_context{ /* AES-CBC-128 variables */struct aes_context SK; /* Rijndael session key */unsignedchar LCT[16]; /* last ciphertext block */ /* HMAC-SHA1 variables */unsignedchar k_ipad[64]; /* inner padding */unsignedchar k_opad[64]; /* outer padding */unsignedlongint p_cntr; /* packet counter */};struct pel_context send_ctx; /* to encrypt outgoing data */struct pel_context recv_ctx; /* to decrypt incoming data */unsignedchar challenge[16] = /* version-specific */"\x58\x90\xAE\x86\xF1\xB9\x1C\xF6" \"\x29\x83\x95\x71\x1D\xDE\x58\x0D";unsignedchar buffer[BUFSIZE +16+20];/* function declaration */voidpel_setup_context( struct pel_context *pel_ctx,char*key,unsignedchar IV[20] );intpel_send_all( int s,void*buf,size_t len,int flags );intpel_recv_all( int s,void*buf,size_t len,int flags );/* session setup - client side */intpel_client_init( int server,char*key ){int ret, len, pid;struct timeval tv;struct sha1_context sha1_ctx;unsignedchar IV1[20], IV2[20]; /* generate both initialization vectors */ pid =getpid();if( gettimeofday( &tv,NULL )<0 ) { pel_errno = PEL_SYSTEM_ERROR;return( PEL_FAILURE ); }sha1_starts( &sha1_ctx );sha1_update( &sha1_ctx, (uint8 *) &tv,sizeof( tv ) );sha1_update( &sha1_ctx, (uint8 *) &pid,sizeof( pid ) );sha1_finish( &sha1_ctx,&buffer[ 0] );memcpy( IV1,&buffer[ 0],20 ); pid++;if( gettimeofday( &tv,NULL )<0 ) { pel_errno = PEL_SYSTEM_ERROR;return( PEL_FAILURE ); }sha1_starts( &sha1_ctx );sha1_update( &sha1_ctx, (uint8 *) &tv,sizeof( tv ) );sha1_update( &sha1_ctx, (uint8 *) &pid,sizeof( pid ) );sha1_finish( &sha1_ctx,&buffer[20] );memcpy( IV2,&buffer[20],20 ); /* and pass them to the server */ ret =pel_send_all( server, buffer,40,0 );if( ret != PEL_SUCCESS ) return( PEL_FAILURE ); /* setup the session keys */pel_setup_context( &send_ctx, key, IV1 );pel_setup_context( &recv_ctx, key, IV2 ); /* handshake - encrypt and send the client's challenge */ ret =pel_send_msg( server, challenge,16 );if( ret != PEL_SUCCESS ) return( PEL_FAILURE ); /* handshake - decrypt and verify the server's challenge */ ret =pel_recv_msg( server, buffer,&len );if( ret != PEL_SUCCESS ) return( PEL_FAILURE );if( len !=16||memcmp( buffer, challenge,16 )!=0 ) { pel_errno = PEL_WRONG_CHALLENGE;return( PEL_FAILURE ); } pel_errno = PEL_UNDEFINED_ERROR;return( PEL_SUCCESS );} /* session setup - server side */intpel_server_init( int client,char*key ){int ret, len;unsignedchar IV1[20], IV2[20]; /* get the IVs from the client */ ret =pel_recv_all( client, buffer,40,0 );if( ret != PEL_SUCCESS ) return( PEL_FAILURE );memcpy( IV2,&buffer[ 0],20 );memcpy( IV1,&buffer[20],20 ); /* setup the session keys */pel_setup_context( &send_ctx, key, IV1 );pel_setup_context( &recv_ctx, key, IV2 ); /* handshake - decrypt and verify the client's challenge */ ret =pel_recv_msg( client, buffer,&len );if( ret != PEL_SUCCESS ) return( PEL_FAILURE );if( len !=16||memcmp( buffer, challenge,16 )!=0 ) { pel_errno = PEL_WRONG_CHALLENGE;return( PEL_FAILURE ); } /* handshake - encrypt and send the server's challenge */ ret =pel_send_msg( client, challenge,16 );if( ret != PEL_SUCCESS ) return( PEL_FAILURE ); pel_errno = PEL_UNDEFINED_ERROR;return( PEL_SUCCESS );}/* this routine computes the AES & HMAC session keys */voidpel_setup_context( struct pel_context *pel_ctx,char*key,unsignedchar IV[20] ){int i;struct sha1_context sha1_ctx;sha1_starts( &sha1_ctx );sha1_update( &sha1_ctx, (uint8 *) key, strlen( key ) );sha1_update( &sha1_ctx, IV,20 );sha1_finish( &sha1_ctx, buffer );aes_set_key( &pel_ctx->SK, buffer,128 );memcpy( pel_ctx->LCT, IV,16 );memset( pel_ctx->k_ipad,0x36,64 );memset( pel_ctx->k_opad,0x5C,64 );for( i =0; i <20; i++ ) {pel_ctx->k_ipad[i] ^= buffer[i];pel_ctx->k_opad[i] ^= buffer[i]; }pel_ctx->p_cntr =0;}/* encrypt and transmit a message */intpel_send_msg( int sockfd,unsignedchar*msg,int length ){unsignedchar digest[20];struct sha1_context sha1_ctx;int i, j, ret, blk_len; /* verify the message length */if( length <=0|| length > BUFSIZE ) { pel_errno = PEL_BAD_MSG_LENGTH;return( PEL_FAILURE ); } /* write the message length at start of buffer */ buffer[0] = ( length >>8 ) &0xFF; buffer[1] = ( length ) &0xFF; /* append the message content */memcpy( buffer +2, msg, length ); /* round up to AES block length (16 bytes) */ blk_len =2+ length;if( ( blk_len &0x0F ) !=0 ) { blk_len +=16- ( blk_len &0x0F ); } /* encrypt the buffer with AES-CBC-128 */for( i =0; i < blk_len; i +=16 ) {for( j =0; j <16; j++ ) { buffer[i + j] ^=send_ctx.LCT[j]; }aes_encrypt( &send_ctx.SK,&buffer[i] );memcpy( send_ctx.LCT,&buffer[i],16 ); } /* compute the HMAC-SHA1 of the ciphertext */ buffer[blk_len ] = ( send_ctx.p_cntr <<24 ) &0xFF; buffer[blk_len +1] = ( send_ctx.p_cntr <<16 ) &0xFF; buffer[blk_len +2] = ( send_ctx.p_cntr <<8 ) &0xFF; buffer[blk_len +3] = ( send_ctx.p_cntr ) &0xFF;sha1_starts( &sha1_ctx );sha1_update( &sha1_ctx,send_ctx.k_ipad,64 );sha1_update( &sha1_ctx, buffer, blk_len +4 );sha1_finish( &sha1_ctx, digest );sha1_starts( &sha1_ctx );sha1_update( &sha1_ctx,send_ctx.k_opad,64 );sha1_update( &sha1_ctx, digest,20 );sha1_finish( &sha1_ctx,&buffer[blk_len] ); /* increment the packet counter */send_ctx.p_cntr++; /* transmit ciphertext and message authentication code */ ret =pel_send_all( sockfd, buffer, blk_len +20,0 );if( ret != PEL_SUCCESS ) return( PEL_FAILURE ); pel_errno = PEL_UNDEFINED_ERROR;return( PEL_SUCCESS );}/* receive and decrypt a message */intpel_recv_msg( int sockfd,unsignedchar*msg,int*length ){unsignedchar temp[16];unsignedchar hmac[20];unsignedchar digest[20];struct sha1_context sha1_ctx;int i, j, ret, blk_len; /* receive the first encrypted block */ ret =pel_recv_all( sockfd, buffer,16,0 );if( ret != PEL_SUCCESS ) return( PEL_FAILURE ); /* decrypt this block and extract the message length */memcpy( temp, buffer,16 );aes_decrypt( &recv_ctx.SK, buffer );for( j =0; j <16; j++ ) { buffer[j] ^=recv_ctx.LCT[j]; }*length = ( ((int) buffer[0]) <<8 ) + (int) buffer[1]; /* restore the ciphertext */memcpy( buffer, temp,16 ); /* verify the message length */if( *length <=0||*length > BUFSIZE ) { pel_errno = PEL_BAD_MSG_LENGTH;return( PEL_FAILURE ); } /* round up to AES block length (16 bytes) */ blk_len =2+*length;if( ( blk_len &0x0F ) !=0 ) { blk_len +=16- ( blk_len &0x0F ); } /* receive the remaining ciphertext and the mac */ ret =pel_recv_all( sockfd,&buffer[16], blk_len -16+20,0 );if( ret != PEL_SUCCESS ) return( PEL_FAILURE );memcpy( hmac,&buffer[blk_len],20 ); /* verify the ciphertext integrity */ buffer[blk_len ] = ( recv_ctx.p_cntr <<24 ) &0xFF; buffer[blk_len +1] = ( recv_ctx.p_cntr <<16 ) &0xFF; buffer[blk_len +2] = ( recv_ctx.p_cntr <<8 ) &0xFF; buffer[blk_len +3] = ( recv_ctx.p_cntr ) &0xFF;sha1_starts( &sha1_ctx );sha1_update( &sha1_ctx,recv_ctx.k_ipad,64 );sha1_update( &sha1_ctx, buffer, blk_len +4 );sha1_finish( &sha1_ctx, digest );sha1_starts( &sha1_ctx );sha1_update( &sha1_ctx,recv_ctx.k_opad,64 );sha1_update( &sha1_ctx, digest,20 );sha1_finish( &sha1_ctx, digest );if( memcmp( hmac, digest,20 )!=0 ) { pel_errno = PEL_CORRUPTED_DATA;return( PEL_FAILURE ); } /* increment the packet counter */recv_ctx.p_cntr++; /* finally, decrypt and copy the message */for( i =0; i < blk_len; i +=16 ) {memcpy( temp,&buffer[i],16 );aes_decrypt( &recv_ctx.SK,&buffer[i] );for( j =0; j <16; j++ ) { buffer[i + j] ^=recv_ctx.LCT[j]; }memcpy( recv_ctx.LCT, temp,16 ); }memcpy( msg,&buffer[2],*length ); pel_errno = PEL_UNDEFINED_ERROR;return( PEL_SUCCESS );}/* send/recv wrappers to handle fragmented TCP packets */intpel_send_all( int s,void*buf,size_t len,int flags ){int n;size_t sum =0;char*offset = buf;while( sum < len ) { n =send( s, (void*) offset, len - sum, flags );if( n <0 ) { pel_errno = PEL_SYSTEM_ERROR;return( PEL_FAILURE ); } sum += n; offset += n; } pel_errno = PEL_UNDEFINED_ERROR;return( PEL_SUCCESS );}intpel_recv_all( int s,void*buf,size_t len,int flags ){int n;size_t sum =0;char*offset = buf;while( sum < len ) { n =recv( s, (void*) offset, len - sum, flags );if( n ==0 ) { pel_errno = PEL_CONN_CLOSED;return( PEL_FAILURE ); }if( n <0 ) { pel_errno = PEL_SYSTEM_ERROR;return( PEL_FAILURE ); } sum += n; offset += n; } pel_errno = PEL_UNDEFINED_ERROR;return( PEL_SUCCESS );}
What we are missing now is the key and the IVs. We can see that the key has been defined in the header file tsh.h. As we are in possession of the ff file, we can extract this.
Using BinaryNinja, we look at the .data segment and find the key directly there.
Now, we need to extract the data that has been send to the target machine, to see which commands might have been executed. For this we use tshark. The first package sent are the two IVs