mirror of https://github.com/a0rtega/pafish
Add reverse turing tests
Add reverse Turing tests, which mimick checks found in real world samples like the UpClicker trojan, leaked source code of Ursnif/Gozi, a "DarkRiver" dropper, the MyWeb-backdoor and XLM 4.0-macros used as droppers.
This commit is contained in:
parent
62dad68149
commit
ebb47f35ef
|
@ -5,7 +5,7 @@ WINDRES = i686-w64-mingw32-windres
|
|||
OBJ = Objects/MingW/main.o Objects/MingW/common.o Objects/MingW/utils.o Objects/MingW/debuggers.o Objects/MingW/sandboxie.o \
|
||||
Objects/MingW/vbox.o Objects/MingW/gensandbox.o Objects/MingW/wine.o Objects/MingW/vmware.o \
|
||||
Objects/MingW/qemu.o Objects/MingW/hooks.o Objects/MingW/cpu.o Objects/MingW/cuckoo.o Objects/MingW/bochs.o \
|
||||
Objects/MingW/pafish_private.res
|
||||
Objects/MingW/rtt.o Objects/MingW/pafish_private.res
|
||||
LINKOBJ = $(OBJ)
|
||||
LIBS = -lwsock32 -liphlpapi -lsetupapi -lmpr -lole32 -lwbemuuid -loleaut32 -lws2_32 -s
|
||||
INCS =
|
||||
|
@ -64,5 +64,8 @@ Objects/MingW/cuckoo.o: $(GLOBALDEPS) cuckoo.c
|
|||
Objects/MingW/bochs.o: $(GLOBALDEPS) bochs.c
|
||||
$(CC) -c bochs.c -o Objects/MingW/bochs.o $(CFLAGS)
|
||||
|
||||
Objects/MingW/rtt.o: $(GLOBALDEPS) rtt.c
|
||||
$(CC) -c rtt.c -o Objects/MingW/rtt.o $(CFLAGS)
|
||||
|
||||
Objects/MingW/pafish_private.res: Objects/MingW/pafish_private.rc
|
||||
$(WINDRES) Objects/MingW/pafish_private.rc --input-format=rc -o Objects/MingW/pafish_private.res -O coff
|
||||
|
|
|
@ -5,7 +5,7 @@ WINDRES = windres.exe
|
|||
OBJ = Objects/MingW/main.o Objects/MingW/common.o Objects/MingW/utils.o Objects/MingW/debuggers.o Objects/MingW/sandboxie.o \
|
||||
Objects/MingW/vbox.o Objects/MingW/gensandbox.o Objects/MingW/wine.o Objects/MingW/vmware.o \
|
||||
Objects/MingW/qemu.o Objects/MingW/hooks.o Objects/MingW/cpu.o Objects/MingW/cuckoo.o Objects/MingW/bochs.o \
|
||||
Objects/MingW/pafish_private.res
|
||||
Objects/MingW/rtt.o Objects/MingW/pafish_private.res
|
||||
LINKOBJ = $(OBJ)
|
||||
LIBS = -lwsock32 -liphlpapi -lsetupapi -lmpr -lole32 -lwbemuuid -loleaut32 -lws2_32 -s
|
||||
INCS =
|
||||
|
@ -64,5 +64,8 @@ Objects/MingW/cuckoo.o: $(GLOBALDEPS) cuckoo.c
|
|||
Objects/MingW/bochs.o: $(GLOBALDEPS) bochs.c
|
||||
$(CC) -c bochs.c -o Objects/MingW/bochs.o $(CFLAGS)
|
||||
|
||||
Objects/MingW/rtt.o: $(GLOBALDEPS) rtt.c
|
||||
$(CC) -c rtt.c -o Objects/MingW/rtt.o $(CFLAGS)
|
||||
|
||||
Objects/MingW/pafish_private.res: Objects/MingW/pafish_private.rc
|
||||
$(WINDRES) Objects/MingW/pafish_private.rc --input-format=rc -o Objects/MingW/pafish_private.res -O coff
|
||||
|
|
|
@ -17,20 +17,6 @@
|
|||
*/
|
||||
typedef BOOL (WINAPI * IsNativeVhdBoot) (BOOL *);
|
||||
|
||||
int gensandbox_mouse_act() {
|
||||
POINT position1, position2;
|
||||
GetCursorPos(&position1);
|
||||
Sleep(2000); /* Sleep time */
|
||||
GetCursorPos(&position2);
|
||||
if ((position1.x == position2.x) && (position1.y == position2.y)) {
|
||||
/* No mouse activity during the sleep */
|
||||
return TRUE;
|
||||
}
|
||||
else {
|
||||
/* Mouse activity during the sleep */
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
int gensandbox_username() {
|
||||
char username[200];
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
#ifndef GENSAND_H
|
||||
#define GENSAND_H
|
||||
|
||||
int gensandbox_mouse_act();
|
||||
|
||||
int gensandbox_username();
|
||||
|
||||
int gensandbox_path();
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
#include "cpu.h"
|
||||
#include "cuckoo.h"
|
||||
#include "bochs.h"
|
||||
|
||||
#include "rtt.h"
|
||||
/*
|
||||
Pafish (Paranoid fish)
|
||||
|
||||
|
@ -112,11 +112,32 @@ int main(void)
|
|||
"CPU VM traced by checking cpuid hypervisor vendor for known VM vendors",
|
||||
"hi_CPU_VM_hv_vendor_name");
|
||||
|
||||
/* Generic reverse turing tests */
|
||||
print_check_group("Generic reverse turing tests");
|
||||
exec_check("Checking mouse presence", &rtt_mouse_presence,
|
||||
"Sandbox traced by absence of mouse device",
|
||||
"hi_sandbox_mouse_presence");
|
||||
exec_check("Checking mouse movement", &rtt_mouse_move,
|
||||
"Sandbox traced by missing mouse movement",
|
||||
"hi_sandbox_rtt_mouse_movement");
|
||||
exec_check("Checking mouse speed", &rtt_mouse_speed_limit,
|
||||
"Sandbox traced by missing mouse movement or supernatural speed",
|
||||
"hi_sandbox_rtt_mouse_speed_limit");
|
||||
exec_check("Checking mouse click activity", &rtt_mouse_click,
|
||||
"Sandbox traced by missing mouse click activity",
|
||||
"hi_sandbox_rtt_mouse_click");
|
||||
exec_check("Checking mouse double click activity", &rtt_mouse_double_click,
|
||||
"Sandbox traced by missing double click activity",
|
||||
"hi_sandbox_rtt_mouse_double_click");
|
||||
exec_check("Checking dialog confirmation", &rtt_confirm_dialog,
|
||||
"Sandbox traced by missing dialog confirmation",
|
||||
"hi_sandbox_rtt_confirm_dialog");
|
||||
exec_check("Checking plausible dialog confirmation", &rtt_plausible_confirm_dialog,
|
||||
"Sandbox traced by missing or implausible dialog confirmation",
|
||||
"hi_sandbox_rtt_implausible_confirm_dialog");
|
||||
|
||||
/* Generic sandbox detection tricks */
|
||||
print_check_group("Generic sandbox detection");
|
||||
exec_check("Using mouse activity", &gensandbox_mouse_act,
|
||||
"Sandbox traced using mouse activity",
|
||||
"hi_sandbox_mouse_act");
|
||||
exec_check("Checking username", &gensandbox_username,
|
||||
"Sandbox traced by checking username",
|
||||
"hi_sandbox_username");
|
||||
|
|
|
@ -0,0 +1,375 @@
|
|||
#include <windows.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "types.h"
|
||||
#include "rtt.h"
|
||||
|
||||
/* Dialog's constants */
|
||||
#define ID_OK 1
|
||||
#define ID_QUIT 2
|
||||
#define TEXT_OFF 20
|
||||
|
||||
/* Custom message indicating timer's end */
|
||||
#define WM_CUSTOM (WM_USER + 0x0001)
|
||||
|
||||
/* Duration of each check */
|
||||
#define MAX_DURATION 3000
|
||||
|
||||
/*
|
||||
* Checks, for the presence of a mouse device.
|
||||
*
|
||||
* Found in similar form in maldoc used by ColdRiver
|
||||
* MD5: 48320f502811645fa1f2f614bd8a385a
|
||||
*/
|
||||
int rtt_mouse_presence() {
|
||||
int res;
|
||||
res = GetSystemMetrics(SM_MOUSEPRESENT);
|
||||
return res > 0 ? FALSE : TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks, if the mouse cursor moves.
|
||||
*
|
||||
* Found in similar form in IFSB/Gozi in version 2.13.24.1 leaked by vx-undeground.org
|
||||
* See: https://github.com/vxunderground/MalwareSourceCode/blob/main/Leaks/Win32/Win32.Gozi.rar
|
||||
*/
|
||||
int rtt_mouse_move() {
|
||||
POINT cur = { 0 }, prev = { 0 };
|
||||
u_int movement = 0;
|
||||
int interval = 100;
|
||||
int period = MAX_DURATION;
|
||||
|
||||
do {
|
||||
GetCursorPos(&cur);
|
||||
|
||||
if (prev.x && prev.y)
|
||||
movement = abs(cur.x - prev.x) + (abs(cur.y - prev.y) << 16);
|
||||
|
||||
/* Cursor moved */
|
||||
if (movement) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
prev = cur;
|
||||
Sleep(interval);
|
||||
period -= interval;
|
||||
} while (period);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks, for supernatural cursor speeds.
|
||||
*
|
||||
* Referenced in Fireeye Blogpost from 2014
|
||||
* https://www.fireeye.com/blog/threat-research/2014/06/turing-test-in-reverse-new-sandbox-evasion-techniques-seek-human-interaction.html
|
||||
*/
|
||||
int rtt_mouse_speed_limit() {
|
||||
POINT prev, cur;
|
||||
int mx, my, dx, dy;
|
||||
|
||||
int period = MAX_DURATION;
|
||||
int interval = 10;
|
||||
int cursor_updates = 0;
|
||||
|
||||
mx = GetSystemMetrics(SM_CXFULLSCREEN) / 5;
|
||||
my = GetSystemMetrics(SM_CYFULLSCREEN) / 5;
|
||||
|
||||
GetCursorPos(&prev);
|
||||
|
||||
while (period) {
|
||||
Sleep(interval); /* Sleep time */
|
||||
|
||||
GetCursorPos(&cur);
|
||||
dx = prev.x - cur.x;
|
||||
dy = prev.y - cur.y;
|
||||
|
||||
if ((dx != 0) || (dy != 0)) {
|
||||
/* Cursor moved */
|
||||
cursor_updates++;
|
||||
|
||||
if (abs(dx) > mx && abs(dy) > my) {
|
||||
/* Cursor moved too fast */
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
prev = cur;
|
||||
period -= interval;
|
||||
}
|
||||
|
||||
/* Mouse actually moved, but never moved too fast, therefore pass check */
|
||||
if (cursor_updates) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* No mouse activity, fail check as well */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Timer callback, which sends a custom message to the message loop, in order
|
||||
* to signal that the time is up.
|
||||
*/
|
||||
VOID CALLBACK timer_proc() {
|
||||
PostMessageA(NULL, WM_CUSTOM, 0, 0);
|
||||
}
|
||||
|
||||
HHOOK hook;
|
||||
BOOL is_success = FALSE;
|
||||
|
||||
/*
|
||||
* Callback for a low-level mouse hook, which checks, if a single click occurs.
|
||||
*/
|
||||
LRESULT CALLBACK single_click_proc(int nCode, WPARAM wParam, LPARAM lp) {
|
||||
if (nCode >= 0) {
|
||||
if (wParam == WM_LBUTTONUP) {
|
||||
is_success = TRUE;
|
||||
PostMessageA(NULL, WM_CUSTOM, 0 , 0);
|
||||
}
|
||||
}
|
||||
return CallNextHookEx(hook, nCode, wParam, lp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculates the current time in milliseconds by querying system time as
|
||||
* FILETIME and converting it to Unix epoch format.
|
||||
*
|
||||
* Taken from https://gist.github.com/e-yes/278302
|
||||
*/
|
||||
u_int64 get_current_time_in_millis(){
|
||||
FILETIME ft;
|
||||
LARGE_INTEGER li;
|
||||
|
||||
/*
|
||||
* Gets the amount of 100 nano seconds intervals elapsed since January 1,
|
||||
* 1601 (UTC) and copy it * to a LARGE_INTEGER structure.
|
||||
*/
|
||||
GetSystemTimeAsFileTime(&ft);
|
||||
li.LowPart = ft.dwLowDateTime;
|
||||
li.HighPart = ft.dwHighDateTime;
|
||||
|
||||
u_int64 ms = li.QuadPart;
|
||||
ms -= 116444736000000000LL; /* Converts from file time to UNIX epoch time. */
|
||||
ms /= 10000; /* From 100 nano seconds (10^-7) to 1 millisecond (10^-3) intervals */
|
||||
|
||||
return ms;
|
||||
}
|
||||
|
||||
/* Tracks the point in time of last click */
|
||||
u_int64 last = 0;
|
||||
|
||||
/* Default double click time in milliseconds */
|
||||
u_int double_click_time = 500;
|
||||
|
||||
/*
|
||||
* Callback for a low-level mouse hook, which checks, if a double click occurs.
|
||||
* The presence of a double click is assumed if two clicks are observed within
|
||||
* the time frame double_click_time.
|
||||
*/
|
||||
LRESULT CALLBACK double_click_proc(int code, WPARAM wp, LPARAM lp) {
|
||||
if (code >= 0) {
|
||||
if (wp == WM_LBUTTONDOWN) {
|
||||
|
||||
u_int64 now = get_current_time_in_millis();
|
||||
if((now - last) < double_click_time){
|
||||
is_success = TRUE;
|
||||
PostMessageA(NULL, WM_CUSTOM, 0 , 0);
|
||||
}
|
||||
last = now;
|
||||
}
|
||||
}
|
||||
return CallNextHookEx(hook, code, wp, lp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Installs a low-level mouse hook with the specified callback.
|
||||
*/
|
||||
int install_hook(LRESULT CALLBACK (*callback)(int code, WPARAM wp, LPARAM lp)){
|
||||
SetTimer(NULL, 0, MAX_DURATION, (TIMERPROC) &timer_proc);
|
||||
hook = SetWindowsHookEx(WH_MOUSE_LL, callback, NULL, 0);
|
||||
MSG msg;
|
||||
|
||||
while (GetMessage(&msg, NULL, 0, 0) > 0) {
|
||||
if (msg.message == WM_CUSTOM) {
|
||||
/* Timer ended, exit message loop*/
|
||||
break;
|
||||
}
|
||||
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessage(&msg);
|
||||
}
|
||||
|
||||
/* Clean up */
|
||||
KillTimer(NULL, 0);
|
||||
UnhookWindowsHookEx(hook);
|
||||
|
||||
if (is_success)
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
/*
|
||||
* Checks for a single click with a technique used in the UpClicker trojan.
|
||||
* See https://webcache.googleusercontent.com/search?q=cache:NeVZ4J1Y-cQJ:https://www.fireeye.com/blog/threat-research/2012/12/dont-click-the-left-mouse-button-trojan-upclicker.html+&cd=1&hl=en&ct=clnk&gl=de
|
||||
*/
|
||||
int rtt_mouse_click() {
|
||||
return install_hook(&single_click_proc);
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks for dobule clicks.
|
||||
*
|
||||
* In the past several malware samples waited for a certain amount of clicks
|
||||
* (not necessarily double clicks) with different techniques, see for example:
|
||||
*
|
||||
* https://www.ptsecurity.com/ww-en/analytics/antisandbox-techniques/ mentioning
|
||||
* MyWeb backdoor used by APT15 in 2010, where three left clicks were required
|
||||
*
|
||||
* https://www.fireeye.com/blog/threat-research/2014/06/turing-test-in-reverse-new-sandbox-evasion-techniques-seek-human-interaction.html
|
||||
* referencing the use of GetAsyncKeyState()
|
||||
*/
|
||||
int rtt_mouse_double_click() {
|
||||
/* Determines double click time set on system */
|
||||
double_click_time = GetDoubleClickTime();
|
||||
/* Checks, if a double click occurs */
|
||||
return install_hook(&double_click_proc);
|
||||
}
|
||||
|
||||
BOOL is_timeout = FALSE;
|
||||
BOOL is_within_rect = FALSE;
|
||||
|
||||
LRESULT CALLBACK timed_dialog_proc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) {
|
||||
|
||||
RECT rect;
|
||||
int btn_w, btn_h;
|
||||
|
||||
switch (msg) {
|
||||
case WM_CREATE:
|
||||
SetTimer(hwnd, 1, MAX_DURATION, NULL);
|
||||
|
||||
/* Determines "usable" size inside of the current window */
|
||||
GetClientRect(hwnd, &rect);
|
||||
|
||||
/* Calculates button size */
|
||||
btn_w = (rect.right - rect.left) / 2;
|
||||
btn_h = (rect.bottom - rect.top) / 2;
|
||||
|
||||
/* Create two buttons - randomly chosen and "Quit" */
|
||||
CreateWindowW(L"Button", L"OK", WS_VISIBLE | WS_CHILD, 0, TEXT_OFF,
|
||||
btn_w, btn_h, hwnd, (HMENU) ID_OK, NULL, NULL);
|
||||
|
||||
CreateWindowW(L"Button", L"Quit", WS_VISIBLE | WS_CHILD, 0 + btn_w,
|
||||
TEXT_OFF, btn_w, btn_h, hwnd, (HMENU) ID_QUIT, NULL, NULL);
|
||||
/* Ensure dialog is displayed at the very top */
|
||||
/* SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | */
|
||||
/* SWP_SHOWWINDOW); */
|
||||
break;
|
||||
|
||||
case WM_TIMER:
|
||||
is_timeout = TRUE;
|
||||
DestroyWindow(hwnd);
|
||||
break;
|
||||
|
||||
case WM_COMMAND:
|
||||
|
||||
{
|
||||
/*
|
||||
* Plausibility check, whether cursor is still within rect.
|
||||
* Buttons are not focusable, therefore pressing return is not in this case possible
|
||||
*/
|
||||
POINT p;
|
||||
GetCursorPos(&p);
|
||||
GetWindowRect(hwnd, &rect);
|
||||
|
||||
if (p.x >= rect.left && p.x <= rect.right && p.y >= rect.top
|
||||
&& p.y <= rect.bottom)
|
||||
is_within_rect = TRUE;
|
||||
}
|
||||
|
||||
/* Destroys and recreates a new window on "Ok" */
|
||||
if (LOWORD(wp) == ID_OK) {
|
||||
MessageBeep(MB_OK);
|
||||
DestroyWindow(hwnd);
|
||||
}
|
||||
|
||||
/* Sets stop condition, if "Quit" was clicked */
|
||||
if (LOWORD(wp) == ID_QUIT) {
|
||||
DestroyWindow(hwnd);
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_DESTROY:
|
||||
PostQuitMessage(0);
|
||||
break;
|
||||
}
|
||||
|
||||
return DefWindowProcW(hwnd, msg, wp, lp);
|
||||
}
|
||||
/*
|
||||
* Displays a dialog and waits for interaction
|
||||
*/
|
||||
int confirm_dialog(BOOL is_plausibility_check) {
|
||||
|
||||
MSG msg;
|
||||
int mx, my, rx, ry, mw, mh;
|
||||
|
||||
/* Defines class and window's attributes*/
|
||||
WNDCLASSW wc = { 0 };
|
||||
wc.lpszClassName = L"RTT window";
|
||||
wc.hInstance = NULL;
|
||||
wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
|
||||
wc.lpfnWndProc = timed_dialog_proc;
|
||||
wc.hCursor = LoadCursor(0, IDC_ARROW);
|
||||
srand(GetTickCount());
|
||||
|
||||
/* Size of screen without taskbar */
|
||||
mx = GetSystemMetrics(SM_CXFULLSCREEN);
|
||||
my = GetSystemMetrics(SM_CYFULLSCREEN);
|
||||
|
||||
/* Limits for window size */
|
||||
mw = GetSystemMetrics(SM_CXMIN);
|
||||
mh = GetSystemMetrics(SM_CYMIN) * 3;
|
||||
|
||||
/* Randomize window position */
|
||||
rx = ((double) rand() / RAND_MAX) * (mx - (4 * mw)) + mw;
|
||||
ry = ((double) rand() / RAND_MAX) * (my - (4 * mh)) + mh;
|
||||
|
||||
RegisterClassW(&wc);
|
||||
CreateWindowW(wc.lpszClassName, L"RTT window",
|
||||
WS_OVERLAPPEDWINDOW | WS_VISIBLE, rx, ry, mw,
|
||||
mh, 0, 0, NULL, 0);
|
||||
|
||||
while (GetMessage(&msg, NULL, 0, 0)) {
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessage(&msg);
|
||||
}
|
||||
|
||||
if (!is_timeout) {
|
||||
if (is_plausibility_check)
|
||||
return !is_within_rect;
|
||||
else
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Wrapper function for displaying a dialog and checking if interaction occurs.
|
||||
*
|
||||
* Found in similar form in XLM-macro with MD5: 50d518246c2b61f5b427948f87a0aa24
|
||||
* See also: https://www.lastline.com/labsblog/evolution-of-excel-4-0-macro-weaponization/
|
||||
*/
|
||||
int rtt_confirm_dialog() {
|
||||
return confirm_dialog(FALSE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Wrapper function like rtt_confirm_dialog(), but performs an additional check
|
||||
* for plausibility by determining, whehter the cursor resides inside the
|
||||
* bounding box of the displayed dialog add the time of confirmation.
|
||||
* Thereby, machine-triggered SendMessageW-interactions can be identified.
|
||||
*/
|
||||
int rtt_plausible_confirm_dialog() {
|
||||
return confirm_dialog(TRUE);
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
#ifndef RTT_H
|
||||
#define RTT_H
|
||||
|
||||
int rtt_mouse_presence();
|
||||
|
||||
int rtt_mouse_move();
|
||||
|
||||
int rtt_mouse_speed_limit();
|
||||
|
||||
int rtt_mouse_click();
|
||||
|
||||
int rtt_mouse_double_click();
|
||||
|
||||
int rtt_confirm_dialog();
|
||||
|
||||
int rtt_plausible_confirm_dialog();
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue