As a principal security researcher on Corelight’s Labs team, I help to solve difficult network security research problems at scale. Corelight’s customers might recognize some of my work if you see the packages “VPN Insights” or “App ID” on your sensors. Outside of my day-to-day role, I have a hobby podcast called eCrimeBytes where we lightheartedly discuss an electronic crime case each week.
In one of my recent episodes, I was discussing how the Gozi banking malware group was finally sentenced to prison. So naturally I wondered if I could detect the Gozi banking malware with Zeek. Not familiar with Zeek logs? You can get a sense for how Zeek works and how it is the foundation for Corelight’s network evidence here. You can also find out more on the Zeek Project website.
During my research, I found this sample of the Gozi banking malware in the wild here:
https://malware-traffic-analysis.net/2023/07/12/index.html
You can download a PCAP of the infection from this link too. This is the same PCAP I used to develop this detection logic in Zeek.
According to the notes from Malware Traffic Analysis, the malware C2 information is summarized by:
GOZI/ISFB C2 TRAFFIC:
- 151.248.117.244 port 80 - diwdjndsfnj.ru - GET /uploaded/[long base64 string with backslashes and underscores].pct
- 151.248.117.244 port 80 - diwdjndsfnj.ru - POST /uploaded/[long base64 string with backslashes and underscores].dib
- 151.248.117.244 port 80 - diwdjndsfnj.ru - GET /uploaded/[long base64 string with backslashes and underscores].pmg
- 151.248.117.244 port 80 - iwqdndomdn.su - GET /uploaded/[long base64 string with backslashes and underscores].pmg
- 151.248.117.244 port 80 - iwqdndomdn.su - POST /uploaded/[long base64 string with backslashes and underscores].dib
GOZI/ISFB MODULES (ENCRYPTED DATA BINARIES):
- 91.199.147.95 port 80 - 91.199.147.95 - GET /vnc32.rar
- 91.199.147.95 port 80 - 91.199.147.95 - GET /vnc64.rar
- 91.199.147.95 port 80 - 91.199.147.95 - GET /stilak32.rar
- 91.199.147.95 port 80 - 91.199.147.95 - GET /stilak64.rar
- 91.199.147.95 port 80 - 91.199.147.95 - GET /cook32.rar
- 91.199.147.95 port 80 - 91.199.147.95 - GET /cook64.rar
At first I thought it would be too simple to detect this malware family through the RAR files it downloads, but after searching through several months of operational traffic from our Polaris Program I found these RAR files to be unique to this malware! I chose to include these file names in my detection methodology by looking for the regular expression: /\/(stilak|cook|vnc)(32|64)\.rar$/
The other detection methodology is to look for long URLs that are base64 encoded in the unique manner Gozi uses. First, Gozi uses a real word for the first URL subdirectory. That real word is "uploaded" in the sample above, but I've seen other words used here. This word is unimportant to the malware and can be ignored for our purposes.
Then, Gozi will base64 encode the encrypted C2 data and add several random forward slashes to make it look like a URL. Gozi will eventually remove these slashes when it decodes this C2 string.
In addition, Gozi encodes the base64 "+", "/", "\n", and "\r" characters as "_2B", "_2F", "_0A", and "_0D". Putting all of that information together leads us to a regular expression of: /^\/\w+\/([a-zA-Z0-9\/]|_\/?2\/?F|_\/?2\/?B|_\/?0\/?A|_\/?0\/?D){200,}\.[a-zA-Z0-9]+$/
However, I saw some rare collisions with the prior regular expression that look like false positives on a customer network. Therefore, I only alert on URLs that have at least 10 forward slashes. When I added this condition to my filter, the false positive collisions went away. The full Unix find command combining both detection methodologies follows (note, use gawk on MacOS):
find /logs -name "http*" | parallel -j 10 zcat {} :::: - | zeek-cut host uri | awk -F '\t' '$2 ~ /\/(stilak|cook|vnc)(32|64)\.rar$/ || ($2 ~ /^\/\w+\/([a-zA-Z0-9\/]|_\/?2\/?F|_\/?2\/?B|_\/?0\/?A|_\/?0\/?D){200,}\.[a-zA-Z0-9]+$/ && gsub(/\//, "/", $2) > 10)'
You will see this regular expression detects 94 lines in the Zeek http.log file for our PCAP sample above:
$ cat http.log | zeek-cut host uri | gawk -F '\t' '$2 ~ /\/(stilak|cook|vnc)(32|64)\.rar$/ || ($2 ~ /^\/\w+\/([a-zA-Z0-9\/]|_\/?2\/?F|_\/?2\/?B|_\/?0\/?A|_\/?0\/?D){200,}\.[a-zA-Z0-9]+$/ && gsub(/\//, "/", $2) > 10)' | wc -l
94
We now need to move this logic into Zeek code so we can run it on a live sensor.
We will catch the URIs in Zeek's HTTP request event. The HTTP request event is documented at: https://docs.zeek.org/en/master/scripts/base/bif/plugins/Zeek_HTTP.events.bif.zeek.html#id-http_request
Translating the logic from the find command naturally leads us to the rest of the code here:
module GoziMalwareDetector;
export {
## Log stream identifier.
redef enum Log::ID += {
LOG
};
## The notice when the C2 is observed.
redef enum Notice::Type += {
GoziActivity,
};
## Record type containing the column fields of the log.
type Info: record {
## Timestamp for when the activity happened.
ts: time &log;
## Unique ID for the connection.
uid: string &log;
## The connection's 4-tuple of endpoint addresses/ports.
id: conn_id &log;
## The Gozi C2 HTTP method.
http_method: string &log &optional;
## The Gozi C2 command, still encoded and encrypted.
payload: string &log &optional;
};
## Default hook into Gozi logging.
global log_gozi: event(rec: Info);
## A default logging policy hook for the stream.
global log_policy: Log::PolicyHook;
}
# Regex - make them globals so they are compiled only once!
global rar_regex = /.*\/(stilak|cook|vnc)(32|64)\.rar$/;
global b64_regex = /^\/[^[:blank:]]+\/([a-zA-Z0-9\/]|_\/?2\/?F|_\/?2\/?B|_\/?0\/?A|_\/?0\/?D){200,}\.[a-zA-Z0-9]+$/;
redef record connection += {
gozi: Info &optional;
};
# Initialize logging state.
hook set_session(c: connection)
{
if ( c?$gozi )
return;
c$gozi = Info($ts=network_time(), $uid=c$uid, $id=c$id);
}
function log_gozi_detected(c: connection)
{
if ( ! c?$gozi )
return;
Log::write(GoziMalwareDetector::LOG, c$gozi);
NOTICE([
$note=GoziMalwareDetector::GoziActivity,
$msg=fmt("Potential Gozi banking malware activity between source %s and dest %s with method %s and URI %s", c$id$orig_h, c$id$resp_h, c$gozi$http_method, c$gozi$payload),
$conn=c,
$identifier=cat(c$id$orig_h, c$id$resp_h)]);
delete c$gozi;
}
event http_request(c: connection, method: string, original_URI: string,
unescaped_URI: string, version: string)
{
hook set_session(c);
local uri: string = to_lower(unescaped_URI);
# We use the entropy check below to throw out long "normal" URIs that might make it through our checks.
# Since the underlying Gozi C2 data is encrypted, entropy should be higher than "normal". I chose this threshold based upon empiracal tests.
if ( uri == rar_regex || ( unescaped_URI == b64_regex && count_substr(unescaped_URI, "/") > 10 && find_entropy(unescaped_URI)$entropy > 4 ) ) {
c$gozi$http_method = method;
c$gozi$payload = unescaped_URI;
log_gozi_detected(c);
return;
}
}
event zeek_init() &priority=5
{
Log::create_stream(GoziMalwareDetector::LOG, [
$columns=Info,
$ev=log_gozi,
$path="gozi",
$policy=GoziMalwareDetector::log_policy]);
}
After running my initial logic on a network for a while, I saw a handful of false positives with very deep URLs with many subdirectories. Since Gozi C2 traffic is encrypted before it is base64 encoded, the entropy should be high on the C2 traffic. Therefore, I added an entropy test on those base64 string candidates, only allowing detections when the entropy is greater than 4 bits per character.
The next question becomes: How many of Gozi's variants will this logic detect?
Good question.
I found some of the below variants of Gozi in Any.run, and when I spot checked the PCAPs Gozi was indeed detected:
You can view, download and install the full source code via Corelight's repository here:
https://github.com/corelight/zeek-gozi-detector
Below are examples of Gozi detections in Zeek logs. These logs are also found in Readme.md in the link above.
gozi.log:
#separator \x09
#set_separator ,
#empty_field (empty)
#unset_field -
#path gozi
#open 2023-07-24-19-00-13
#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p
http_method payload
#types time string addr port addr port string string
1689201023.169002 ClEkJM2Vm5giqnMf4h 10.7.12.121 49799 151.248.117.244 80 GET
/uploaded/8jvrTb2D/c4CxLmogLgZQGC_2FQ_2B2b/Ma3ylmhq8i/MeT_2Fmtq1zDpHZZQ/2OknTIetuvPf/SqlzkcwbzWM/
aFx0b70stnXODu/WDQ2wUhiUaYRbirzPbAvc/2V_2Feb1BeDPQaU0/WZs_2FbMUKJ37c4/Gf5YYgB_2B8BS7mcYa/jECotoj7
R/7bH1bkdIXbwbqpU0Nryv/_2BfEwnTZ0On333QjdJ/fdFWYGQpQofXObilmWG0P_/2BS5YP7Tcj18X/cQoSxxb6/FYqDyT3s
va2N6amcI32HXsv/n6pffb_2FO/UxvFILD91uIk2oNQx/DEmgyRV_2FLi/3pF67pmCpVP/mFnn0G63A1Sv9N/05KeIqFG3zHY
uPPOU/1P.pct
1689201024.619787 ClEkJM2Vm5giqnMf4h 10.7.12.121 49799 151.248.117.244 80 GET
/uploaded/IfjXSAQlkk/LWNR4tkuOMiXdy5H_/2F55TZ5Ulkmf/KZSJQNnlKNL/oXNw6o8qzjnHJh/42iAsB5AuQ8n_2FyMP9
YN/iAK5Z4rM1kiabXH3/Tbf6F39oZk8a8mY/9CVAQTHkAlTev6Vstr/ir_2FniSr/BvZGpW5mVB5nr9pTRisy/xPxWcp7jW1do
RbtDMhT/9qJZuk9sdFnLKTsj12Ald9/3B3TYfDuu18i7/7LNZfim3/7nt5T5HqNSKfJKcDB7vJpeR/BoPpPolfrA/Z_2F56SDX
pQJw4TYJ/45pcw80raC9d/yeDiXFQDftm/RIxazSR_2BPJ0F/wL6l8KasDC6CAzVNLSL_2/F535_2BDNH1/tcGkkEMCZrS/i.pct
1689201025.545122 ClEkJM2Vm5giqnMf4h 10.7.12.121 49799 151.248.117.244 80 GET
/uploaded/MsY1SyBRpp3LKx93fMeITx/w33QoEW7tSfHh/8_2Fqcx1/mAOJzt3teF4AUCrc8IBMkbm/8laKdMc_2F/A0s_2B6
tfYEvyAcP_/2Bk_2Buvkjcq/80G4xrhnFAN/TCW76WZrbPDSMY/_2FhKbl7f8pmHGBET0Y_2/BvcMUzja7B7xdYYK/qwpg5fbr
Mu6AsJ8/Dejh0cXcFfYe8h75Yx/Fn3LZFhMy/29bQzFKZyZHoHgfyV1XJ/Lw1D14ehZUiPYfQxTWv/LlBNcxqMhthgso7PYl8t
Lq/JhO8H_2FKdEez/1uHkLyhp/SD1X5u0VHgJvzUkDhpUUhKb/Q6cyGyHP3A/UPnKN8bJKuY7qHpRC/r94GeIuEWP3a/G9Zo2.pct
1689201032.086164 C4J4Th3PJpwUYZZ6gc 10.7.12.121 49800 151.248.117.244 80 GET
/uploaded/DjeJ0blPQ/_2BkfrDEoFQgD04wO2F7/Ojqqobto35jEVZ1IQyU/G7zu4_2BFUfhIMJcKkibbg/fjjRaEElICvmR/
e5DoJnsG/vsx3T8eOiuXp0AlWknwttvf/A_2FrNprrb/bHnMsv4916Q0BUf_2/B3XIECBmUK_2/FW3G5XPXaPV/ySf6P_2BIXQ
e7C/q0IvZNIlHZt2c8lCjnMGY/BP81zPWMMzAUn3VS/Y_2BCg7CLJsM0vz/MloZ0Th38yNZOadE6L/qwrs9PKza/13Lw9jqWnb
yh08rIXwcG/mMM0HdBwcj6NPi6_2FH/5qnQe2GM1T/ZuHEvxYT/j.pmg
1689201032.792223 CtPZjS20MLrsMUOJi2 10.7.12.121 49801 91.199.147.95 80 GET /vnc32.rar
1689201033.823106 CtPZjS20MLrsMUOJi2 10.7.12.121 49801 91.199.147.95 80 GET /vnc64.rar
1689201045.338855 C4J4Th3PJpwUYZZ6gc 10.7.12.121 49800 151.248.117.244 80 POST
/uploaded/3lFSQwjxUfg8HgrTtqSS/ZKopMRdt0Jtv6ehOunO/ppBgdtF3YUX5Co9W0vX2OQ/UNZWu2BHmWHLi/4Pta3IUW/h8
js8gl66mR4P51kp_2B1rV/l_2BDjFJoW/DhHjLJGFTtgPfY5qz/0jnqt8GbX_2F/M2R1QPMjPhA/1LDVR2FItKJXdJ/1qIKPwGy
BNh80d_2BqfF_/2F6OR9A8MzQ8A1Mu/qqkL0hlaa4U4qkx/TrAV_2BpGP_2B6FnQc/TZAppMESi/SnlO5AU6khpDGRiOveBi/R4
uVGlbArQZk3cDBamb/rekwL.dib
1689201092.103798 C37jN32gN3y3AZzyf6 10.7.12.121
...
/uploaded/VcSCrD_2F_2/BKyBAIxpStofT6/txC4Pni9EG_2BFbKqUx_2/Fw9s4Zg2fwdtvoMJ/Fr6UyR1uP9PRBN1/FQZ6hz6M
b_2Fs_2F49/_2BkvjXF7/w8MfT_2B32RaOM5ijT_2/BzGKFuqMa5OzlLI_2BT/A_2FGOoDi_2FP_2F8zb2Yr/pIb51vNHV25_2/B
GIXUCRW/oMLsPVA84EAhSj9fCXt0ilY/Cxf2YThLeu/NIEKMHtLXO5SEVrsR/EHwwPqidwENp/fDqtP7kFnSX/5o3VYS7mpe5E1c
/x6KP6J7oE0yDXxAJaeyUe/6JuH70X_2Fjs56xO/bGXUaoK1Jmw6y_2/FvqA1eXlmviDm7Rk39/4JHyQ0dF/t.pmg
#close 2023-07-24-19-00-14
notice.log:
#separator \x09
#set_separator ,
#empty_field (empty)
#unset_field -
#path notice
#open 2023-07-24-19-00-13
#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p fuid file_mime_type file_desc proto note msg sub src dst p n peer_descr actions email_dest suppress_for remote_location.country_code remote_location.region remote_location.city remote_location.latitude remote_location.longitude
#types time string addr port addr port string string string enum enum string string addr addr port count string set[enum] set[string] interval string string string double double
1689201023.169002 ClEkJM2Vm5giqnMf4h 10.7.12.121 49799 151.248.117.244 80 - - - tcp GoziMalwareDetector::GoziActivity
Potential Gozi banking malware activity between source 10.7.12.121 and dest 151.248.117.244 with
method GET and URI
/uploaded/8jvrTb2D/c4CxLmogLgZQGC_2FQ_2B2b/Ma3ylmhq8i/MeT_2Fmtq1zDpHZZQ/2OknTIetuvPf/SqlzkcwbzWM/aFx0b7
0stnXODu/WDQ2wUhiUaYRbirzPbAvc/2V_2Feb1BeDPQaU0/WZs_2FbMUKJ37c4/Gf5YYgB_2B8BS7mcYa/jECotoj7R/7bH1bkdIXb
wbqpU0Nryv/_2BfEwnTZ0On333QjdJ/fdFWYGQpQofXObilmWG0P_/2BS5YP7Tcj18X/cQoSxxb6/FYqDyT3sva2N6amcI32HXsv/n6
pffb_2FO/UxvFILD91uIk2oNQx/DEmgyRV_2FLi/3pF67pmCpVP/mFnn0G63A1Sv9N/05KeIqFG3zHYuPPOU/1P.pct
- 10.7.12.121 151.248.117.244 80 - - Notice::ACTION_LOG (empty) 3600.000000 - - - - -
1689201032.792223 CtPZjS20MLrsMUOJi2 10.7.12.121 49801 91.199.147.95 80 - - - tcp
GoziMalwareDetector::GoziActivity
Potential Gozi banking malware activity between source 10.7.12.121 and dest 91.199.147.95 with method
GET and URI /vnc32.rar - 10.7.12.121 91.199.147.95 80 - - Notice::ACTION_LOG (empty) 3600.000000 - - - - -
1689204683.938169 CilF4d1b6woayAE906 10.7.12.121 50413 151.248.117.244 80 - - - tcp
GoziMalwareDetector::GoziActivity
Potential Gozi banking malware activity between source 10.7.12.121 and dest 151.248.117.244 with method
GET and URI /uploaded/bc1bm932Gi8AkB7CMdoi/dV6NGeXlcWdoFbsJnFC/FQZnIx7u0wQZs8gXewgk2O/_2BqEL_2F5vUH/Vjp
cgRrm/NgFQBF4d0ml2qUjPnz05CNh/WBeUsAyX8h/DYU_2Be6ioc_2FA9o/bkBn4zKxTE1N/1SebIT_2BB2/5hdp86s2tIvCxD/tZmU
gcI6tODvZKdoJyys1/e4sK8m7GZS45MABe/ErGNCS3Es2MiUbX/8g2aKSm8DCj6uNwDbP/RnZ8rlx9a/lrUNLg4HRLMm6ysR4pwf/Vt
ZBd5aLMF2Q_2FrqZr/qT7tLbEkySOgW4gIG9cEFr/b3tW5AqoOLBZY/_2FK4oG1/WJk_2B.pmg - 10.7.12.121 151.248.117.244 80 - - Notice::ACTION_LOG (empty) 3600.000000 - - - - -
#close 2023-07-24-19-00-14
We recently used this logic outside the confines of our labs and detected the Gozi C2 communication for one of our customers on their live network.
Also, in case you are wondering, here are the sources that I used throughout this article:
https://malware-traffic-analysis.net/2023/07/12/index.html
https://www.malware-traffic-analysis.net/2023/03/06/index.html
https://unit42.paloaltonetworks.com/march-wireshark-gozi-answers/
https://github.com/mlodic/ursnif_beacon_decryptor
https://github.com/0ver-fl0w/ISFB_Tools
https://twitter.com/Unit42_Intel/status/1633934017031467010
https://app.any.run/tasks/5e4dbbe3-78a0-4c63-80f8-203b7ad91cd0/
https://app.any.run/tasks/d1a96aea-a514-4f86-acd7-e9391a8ec959/
https://app.any.run/tasks/1406d885-93e3-43e6-8182-667adb6f7ff7/
https://app.any.run/tasks/f3771459-62f8-48b8-a1bf-a1b182af3599/
https://app.any.run/tasks/15800ac5-44bf-4628-9653-f0364c80fc79/
https://app.any.run/tasks/59a8fe6a-e6b2-4e09-9333-dd19227c0e51/
https://www.blackberry.com/us/en/solutions/endpoint-security/ransomware-protection/ursnif
Looking for more tips like these? Follow me on YouTube. I have a dedicated Zeek playlist. I also recommend subscribing to our Corelight blog to keep up with what else me and my colleagues find in the wild. If you’re interested in finding command and control on your network, check out our C2 Collection. These are just a few of the many detections that Corelight’s network evidence provides.