diff --git a/Donot/android-sample/README.MD b/Donot/android-sample/README.MD index 9d32db7..7d99e2d 100644 --- a/Donot/android-sample/README.MD +++ b/Donot/android-sample/README.MD @@ -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 diff --git a/Oceanlotus/APT32-Graph-Deobfuscator.py b/Oceanlotus/APT32-Graph-Deobfuscator.py new file mode 100644 index 0000000..44f9f53 --- /dev/null +++ b/Oceanlotus/APT32-Graph-Deobfuscator.py @@ -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() + + \ No newline at end of file diff --git a/Oceanlotus/README.MD b/Oceanlotus/README.MD new file mode 100644 index 0000000..ef535cc --- /dev/null +++ b/Oceanlotus/README.MD @@ -0,0 +1,5 @@ +# Oceanlotus + +APT32-Graph-Deobfuscator.py come from checkpoint + +https://research.checkpoint.com/deobfuscating-apt32-flow-graphs-with-cutter-and-radare2/ diff --git a/README.md b/README.md index 11e1438..7d2fa67 100644 --- a/README.md +++ b/README.md @@ -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 Korea’s 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 diff --git a/baby-kimsuky/Decode-Cowboy.py b/baby-kimsuky/Decode-Cowboy.py new file mode 100644 index 0000000..134d954 --- /dev/null +++ b/baby-kimsuky/Decode-Cowboy.py @@ -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) diff --git a/baby-kimsuky/README.MD b/baby-kimsuky/README.MD new file mode 100644 index 0000000..e9f1d90 --- /dev/null +++ b/baby-kimsuky/README.MD @@ -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/ diff --git a/baby-kimsuky/babyshark-CVE20188174.php b/baby-kimsuky/babyshark-CVE20188174.php new file mode 100644 index 0000000..a759186 --- /dev/null +++ b/baby-kimsuky/babyshark-CVE20188174.php @@ -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); + +} diff --git a/baby-kimsuky/readme b/baby-kimsuky/readme deleted file mode 100644 index a25474d..0000000 --- a/baby-kimsuky/readme +++ /dev/null @@ -1 +0,0 @@ -### list diff --git a/group123/README.MD b/group123/README.MD index ab13663..7e8604e 100644 --- a/group123/README.MD +++ b/group123/README.MD @@ -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 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