This commit is contained in:
blackorbird
2019-05-07 09:54:45 +08:00
9 changed files with 417 additions and 10 deletions

View File

@@ -3,3 +3,5 @@ APT donot(APT-C-35) group new test sample
KashmirVoice.apk (http://com.update.android .v2.test3)
cdf10316664d181749a8ba90a3c07454
incode url: https[:]//justin.drinkeatgood.space/api/V1/public/
password:KashmirVoice

View File

@@ -0,0 +1,293 @@
""" A plugin for Cutter and Radare2 to deobfuscate APT32 flow graphs
This is a python plugin for Cutter that is compatible as an r2pipe script for
radare2 as well. The plugin will help reverse engineers to deobfuscate and remove
junk blocks from APT32 (Ocean Lotus) samples.
"""
__author__ = "Itay Cohen, aka @megabeets_"
__company__ = "Check Point Software Technologies Ltd"
# Check if we're running from cutter
try:
import cutter
from PySide2.QtWidgets import QAction
pipe = cutter
cutter_available = True
# If no, assume running from radare2
except:
import r2pipe
pipe = r2pipe.open()
cutter_available = False
class GraphDeobfuscator:
# A list of pairs of opposite conditional jumps
jmp_pairs = [
['jno', 'jo'],
['jnp', 'jp'],
['jb', 'jnb'],
['jl', 'jnl'],
['je', 'jne'],
['jns', 'js'],
['jnz', 'jz'],
['jc', 'jnc'],
['ja', 'jbe'],
['jae', 'jb'],
['je', 'jnz'],
['jg', 'jle'],
['jge', 'jl'],
['jpe', 'jpo'],
['jne', 'jz']]
def __init__(self, pipe, verbose=False):
"""an initialization function for the class
Arguments:
pipe {r2pipe} -- an instance of r2pipe or Cutter's wrapper
Keyword Arguments:
verbose {bool} -- if True will print logs to the screen (default: {False})
"""
self.pipe = pipe
self.verbose = verbose
def is_successive_fail(self, block_A, block_B):
"""Check if the end address of block_A is the start of block_B
Arguments:
block_A {block_context} -- A JSON object to represent the first block
block_B {block_context} -- A JSON object to represent the second block
Returns:
bool -- True if block_B comes immediately after block_A, False otherwise
"""
return ((block_A["addr"] + block_A["size"]) == block_B["addr"])
def is_opposite_conditional(self, cond_A, cond_B):
"""Check if two operands are opposite conditional jump operands
Arguments:
cond_A {string} -- the conditional jump operand of the first block
cond_B {string} -- the conditional jump operand of the second block
Returns:
bool -- True if the operands are opposite, False otherwise
"""
sorted_pair = sorted([cond_A, cond_B])
for pair in self.jmp_pairs:
if sorted_pair == pair:
return True
return False
def contains_meaningful_instructions (self, block):
'''Check if a block contains meaningful instructions (references, calls, strings,...)
Arguments:
block {block_context} -- A JSON object which represents a block
Returns:
bool -- True if the block contains meaningful instructions, False otherwise
'''
# Get summary of block - strings, calls, references
summary = self.pipe.cmd("pdsb @ {addr}".format(addr=block["addr"]))
return summary != ""
def get_block_end(self, block):
"""Get the address of the last instruction in a given block
Arguments:
block {block_context} -- A JSON object which represents a block
Returns:
The address of the last instruction in the block
"""
# save current seek
self.pipe.cmd("s {addr}".format(addr=block['addr']))
# This will return the address of a block's last instruction
block_end = self.pipe.cmd("?v $ @B:-1")
return block_end
def get_last_mnem_of_block(self, block):
"""Get the mnemonic of the last instruction in a block
Arguments:
block {block_context} -- A JSON object which represents a block
Returns:
string -- the mnemonic of the last instruction in the given block
"""
inst_info = self.pipe.cmdj("aoj @ {addr}".format(addr=self.get_block_end(block)))[0]
return inst_info["mnemonic"]
def get_jump(self, block):
"""Get the address to which a block jumps
Arguments:
block {block_context} -- A JSON object which represents a block
Returns:
addr -- the address to which the block jumps to. If such address doesn't exist, returns False
"""
return block["jump"] if "jump" in block else None
def get_fail_addr(self, block):
"""Get the address to which a block fails
Arguments:
block {block_context} -- A JSON object which represents a block
Returns:
addr -- the address to which the block fail-branches to. If such address doesn't exist, returns False
"""
return block["fail"] if "fail" in block else None
def get_block(self, addr):
"""Get the block context in a given address
Arguments:
addr {addr} -- An address in a block
Returns:
block_context -- the block to which the address belongs
"""
block = self.pipe.cmdj("abj. @ {offset}".format(offset=addr))
return block[0] if block else None
def get_fail_block(self, block):
"""Return the block to which a block branches if the condition is fails
Arguments:
block {block_context} -- A JSON representation of a block
Returns:
block_context -- The block to which the branch fails. If not exists, returns None
"""
# Get the address of the "fail" branch
fail_addr = self.get_fail_addr(block)
if not fail_addr:
return None
# Get a block context of the fail address
fail_block = self.get_block(fail_addr)
return fail_block if fail_block else None
def reanalize_function(self):
"""Re-Analyze a function at a given address
Arguments:
addr {addr} -- an address of a function to be re-analyze
"""
# Seek to the function's start
self.pipe.cmd("s $F")
# Undefine the function in this address
self.pipe.cmd("af- $")
# Define and analyze a function in this address
self.pipe.cmd("afr @ $")
def overwrite_instruction(self, addr):
"""Overwrite a conditional jump to an address, with a JMP to it
Arguments:
addr {addr} -- address of an instruction to be overwritten
"""
jump_destination = self.get_jump(self.pipe.cmdj("aoj @ {addr}".format(addr=addr))[0])
if (jump_destination):
self.pipe.cmd("wai jmp 0x{dest:x} @ {addr}".format(dest=jump_destination, addr=addr))
def get_current_function(self):
"""Return the start address of the current function
Return Value:
The address of the current function. None if no function found.
"""
function_start = int(self.pipe.cmd("?vi $FB"))
return function_start if function_start != 0 else None
def clean_junk_blocks(self):
"""Search a given function for junk blocks, remove them and fix the flow.
"""
# Get all the basic blocks of the function
blocks = self.pipe.cmdj("afbj @ $F")
if not blocks:
print("[X] No blocks found. Is it a function?")
return
# Have we modified any instruction in the function?
# If so, a reanalyze of the function is required
modified = False
# Iterate over all the basic blocks of the function
for block in blocks:
fail_block = self.get_fail_block(block)
# Make validation checks
if not fail_block or \
not self.is_successive_fail(block, fail_block) or \
self.contains_meaningful_instructions(fail_block) or \
not self.is_opposite_conditional(self.get_last_mnem_of_block(block), self.get_last_mnem_of_block(fail_block)):
continue
if self.verbose:
print ("Potential junk: 0x{junk_block:x} (0x{fix_block:x})".format(junk_block=fail_block["addr"], fix_block=block["addr"]))
self.overwrite_instruction(self.get_block_end(block))
modified = True
if modified:
self.reanalize_function()
def clean_graph(self):
"""the initial function of the class. Responsible to enable cache and start the cleaning
"""
# Enable cache writing mode. changes will only take place in the session and
# will not override the binary
self.pipe.cmd("e io.cache=true")
self.clean_junk_blocks()
if cutter_available:
# This part will be executed only if Cutter is available. This will
# create the cutter plugin and UI objects for the plugin
class GraphDeobfuscatorCutter(cutter.CutterPlugin):
name = "APT32 Graph Deobfuscator"
description = "Graph Deobfuscator for APT32 Samples"
version = "1.0"
author = "Itay Cohen (@Megabeets_)"
def setupPlugin(self):
pass
def setupInterface(self, main):
# Create a new action (menu item)
action = QAction("APT32 Graph Deobfuscator", main)
action.setCheckable(False)
# Connect the action to a function - cleaner.
# A click on this action will trigger the function
action.triggered.connect(self.cleaner)
# Add the action to the "Windows -> Plugins" menu
pluginsMenu = main.getMenuByType(main.MenuType.Plugins)
pluginsMenu.addAction(action)
def cleaner(self):
graph_deobfuscator = GraphDeobfuscator(pipe)
graph_deobfuscator.clean_graph()
cutter.refresh()
def create_cutter_plugin():
return GraphDeobfuscatorCutter()
if __name__ == "__main__":
graph_deobfuscator = GraphDeobfuscator(pipe)
graph_deobfuscator.clean_graph()

5
Oceanlotus/README.MD Normal file
View File

@@ -0,0 +1,5 @@
# Oceanlotus
APT32-Graph-Deobfuscator.py come from checkpoint
https://research.checkpoint.com/deobfuscating-apt32-flow-graphs-with-cutter-and-radare2/

View File

@@ -52,6 +52,9 @@ https://unit42.paloaltonetworks.com/unit42-freemilk-highly-targeted-spear-phishi
### baby related kimsuky
▶BabyShark Malware Part Two Attacks Continue Using KimJongRAT and PCRat (April 26, 2019)
https://unit42.paloaltonetworks.com/babyshark-malware-part-two-attacks-continue-using-kimjongrat-and-pcrat/
▶Operation Giant Baby, a giant threat (March 28, 2019)
https://blog.alyac.co.kr/2223
@@ -69,6 +72,7 @@ https://blog.alyac.co.kr/1640
### kimsuky
▶ Analysis of "Smoke Screen" in APT campaign aimed at Korea and America (April 17 , 2019)
@@ -80,7 +84,23 @@ https://blog.alyac.co.kr/2234
▶ Kimsuky Organization, Watering Hole Started "Operation Low Kick"(March 21, 2019)
https://blog.alyac.co.kr/2209
### Jaku
▶ SiliVaccine: Inside North Koreas Anti-Virus (May 1, 2018)
https://research.checkpoint.com/silivaccine-a-look-inside-north-koreas-anti-virus/
### Lazarus
LAZARUS APT TARGETS MAC USERS WITH POISONED WORD DOCUMENT
https://www.sentinelone.com/blog/lazarus-apt-targets-mac-users-poisoned-word-document/
### Oceanlotus
▶ Oceanlotus in the first quarter of 2019 for the attack technology of China.(April 24, 2019)
https://mp.weixin.qq.com/s/xPsEXp2J5IE7wNSMEVC24A
▶ Deobfuscating APT32 Flow Graphs with Cutter and Radare2 (April 24, 2019)
https://research.checkpoint.com/deobfuscating-apt32-flow-graphs-with-cutter-and-radare2/
▶ OceanLotus Steganography Malware Analysis White Paper (April 2 , 2019)
https://threatvector.cylance.com/en_us/home/report-oceanlotus-apt-group-leveraging-steganography.html

View File

@@ -0,0 +1,41 @@
import base64
with open(cowboy, r) as file_in, open(cowboy_clear.bin, wb) as file_out:
EncStr = file_in.read()
BlkSz = 10
len_EncStr = len(EncStr)
NonBlk10_ptr = len_EncStr (BlkSz -1) * (len_EncStr // BlkSz)
NonBlk10 = EncStr [:NonBlk10_ptr]
result =
EncStr = EncStr [NonBlk10_ptr::]
#print EncStr
x = range (-1,BlkSz-1)
Blksize1 = len_EncStr // BlkSz
for n in x:
loop_buff1_ptr = n * (len_EncStr // BlkSz)
loop_buff1 = EncStr [loop_buff1_ptr:loop_buff1_ptr+Blksize1]
#print loop_buff1
result = loop_buff1 + result
result = result + NonBlk10
clear = base64.b64decode(result)[::-1]
print clear
file_out.write(clear)

4
baby-kimsuky/README.MD Normal file
View File

@@ -0,0 +1,4 @@
### Baby~~~
▶ BabyShark Malware Part Two Attacks Continue Using KimJongRAT and PCRat (April 26, 2019)
https://unit42.paloaltonetworks.com/babyshark-malware-part-two-attacks-continue-using-kimjongrat-and-pcrat/

View File

@@ -0,0 +1,29 @@
if(file_exists($filename))
{
if($ff=fopen(“resp”,”a”))
{
fwrite($ff, $date . . $ip . .$useragent. reopen document. .”\r\n”);
fclose($ff);
}
header(“location: http://google[.]com”);
exit;
}
if($ff=fopen(“resp”,”a”))
{
fwrite($ff, $date . . $ip . .$useragent. open document. .”\r\n”);
fclose($ff);
}

View File

@@ -1 +0,0 @@
### list

View File

@@ -2,14 +2,27 @@
## 20190423
two relate phishing operation:
### Spear Phishing operation:
Group123, APT attack impersonating Unification Ministry, spread malicious code to Google Drive https://blog.alyac.co.kr/2268 (April 22 , 2019)
'group123' group 'survey on the total number of discovery of separated families in North and South' https://blog.alyac.co.kr/1767 (July 28, 2014)
Group123, APT attack impersonating Unification Ministry, spread malicious code to Google Drive
https://blog.alyac.co.kr/2268 (April 22 , 2019)
related
'group123' group 'survey on the total number of discovery of separated families in North and South'
https://blog.alyac.co.kr/1767 (July 28, 2014)
IOC
email_93682646.html
88107e3c785d3d30e5f6fc191622a157
memo.utr
86f83586c96943ce96309e3017a3500c
email:
@@ -19,27 +32,28 @@ Lee Soo-hyun <loveshlee@unikorea.go.kr>
info:
http://155.138.236.240/sec[.]png?id=
phishing:
input password and login it will redirect to unikorea.go.kr
### phishing:
### input password and login it will redirect to unikorea.go.kr
https://unikorea.go.kr/upload/editUpload/20190418/2019041814360535872.png
https://unikorea.go.kr/upload/editUpload/20190418/2019041814364795734.png
The html file is misleading in this two-step process and will connect you to a specific Google Drive address in the background.
### The html file is misleading in this two-step process and will connect you to a specific Google Drive address in the background.
download:memo.utr
google drive owner: 한국정치학회
Gmail:kpsapress@gmail.com
decode PE and collect private information
post to "pcloud"
### post to "pcloud"
the authorize email is kcrc1214@hanmail.net ,2018.12.3 join
The attacking organization seems to have registered Russian expressions to intentionally give the analysts a false flag, and when translated into English, it will change to the expression 'Humpty Dumpty'.
D:\System\Kernel32\Shell32\Sample\Release\Шалтай-Болтай.pdb (Humpty Dumpty)
### D:\System\Kernel32\Shell32\Sample\Release\Шалтай-Болтай.pdb (Humpty Dumpty)
HTML code feature
### HTML code feature
<meta http-equiv ='Content-Type'content ='text / html; charset = UTF-8'/>
<meta http-equiv ='Cache-Control'content ='no-cache'/>