-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathfeed.xml
732 lines (459 loc) · 190 KB
/
feed.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.1.0">Jekyll</generator><link href="https://mattcasmith.net/feed.xml" rel="self" type="application/atom+xml" /><link href="https://mattcasmith.net/" rel="alternate" type="text/html" /><updated>2024-07-20T15:31:47+01:00</updated><id>https://mattcasmith.net/feed.xml</id><title type="html">MattCASmith</title><subtitle>A blog about cyber security and technology</subtitle><entry><title type="html">Endpoint detection and response (EDR) - setting the record straight</title><link href="https://mattcasmith.net/2024/07/20/crowdstrike-bug-edr-setting-record-straight" rel="alternate" type="text/html" title="Endpoint detection and response (EDR) - setting the record straight" /><published>2024-07-20T01:00:00+01:00</published><updated>2024-07-20T01:00:00+01:00</updated><id>https://mattcasmith.net/2024/07/20/crowdstrike-bug-edr-setting-record-straight</id><content type="html" xml:base="https://mattcasmith.net/2024/07/20/crowdstrike-bug-edr-setting-record-straight"><p>When I went to bed on the evening of Friday 19th July, I couldn’t sleep. It was a stuffy summer’s night in London, and the adrenaline was still pumping through my veins after one of the more notable days in recent memory for cyber security. Still, laying awake gave me time to reflect on what I’d seen.</p>
<p>Cyber security is a function that normally runs quietly in the background, so when it does make the headlines, it’s often for the wrong reasons. In the past, we’ve seen huge incidents like <a href="https://www.wired.com/story/notpetya-cyberattack-ukraine-russia-code-crashed-the-world/" target="_blank">NotPetya</a> and <a href="https://en.wikipedia.org/wiki/WannaCry_ransomware_attack" target="_blank">Wannacry</a> become global news, but yesterday, it was a security product itself that stole the show.</p>
<h3 id="what-exactly-happened">What exactly happened?</h3>
<p>In short: CrowdStrike pushed a bad update to Falcon, a leading endpoint detection and response (EDR) product, that caused a lot of Windows computers to fail to boot. This triggered chaos due to the number of computers around the world running the software. A workaround was quickly discovered, but it needed to be completed manually on each affected system, causing a huge headache for IT administrators.</p>
<p>President and CEO George Kurtz <a href="https://x.com/George_Kurtz/status/1814235001745027317" target="_blank">took to X</a> to try to reassure the world that no cyber security incident had occurred, and said that CrowdStrike had deployed a fix. He would later share more apologetic posts, including <a href="https://www.crowdstrike.com/blog/our-statement-on-todays-outage/" target="_blank">a letter on the firm’s website</a>, but CrowdStrike as a company is not the focus of this post.</p>
<p style="width:100% !important; text-align: center !important"><img src="/wp-content/uploads/2024/07/crowdstrike-edr_00.png" class="half" /></p>
<p>Cyber security is a cat and mouse game between those who wish to do harm and those who protect our systems, and for this reason security software needs constant updates to detect and prevent the latest attacker techniques. To effectively monitor endpoints, the software must also run in a privileged state, which means that if it fails, it can easily cause more problems than standard user mode applications.</p>
<p>Therefore, although CrowdStrike will certainly want to go over its internal testing and review processes, it’s important to remember that a similar thing could happen if any other security vendor made a similar mistake (for example, we saw <a href="https://www.theregister.com/2019/10/15/blue_screen_of_death_symantec/" target="_blank">a similar incident on a smaller scale</a> with Symantec in 2019).</p>
<h3 id="misconceptions-and-inaccuracies">Misconceptions and inaccuracies</h3>
<p>There are a thousand articles out there about the bug itself, how it was fixed, and what this means for CrowdStrike, so I won’t retread that ground here. After the initial shock of what was happening, the next thing that caught my attention was the way CrowdStrike - and by extension EDR as a whole - was being discussed, which showed a complete lack of understanding of the technology and the entire sector.</p>
<p>The internet is a terrible place to read about things you’re an expert in, but this trend wasn’t limited to the general population on social media. Even on relatively technical forums like <a href="https://news.ycombinator.com/" target="_blank">Hacker News</a> there was a strange tone to the conversations, and experienced technology journalists at respected publications dropped huge inaccuracies into their writing. Broadly, these misconceptions fell into three categories:</p>
<p> 1. “EDR is anti-virus software”<br />
2. “EDR is corporate spyware”<br />
3. “EDR isn’t worth the risk”</p>
<p>I don’t think any of these inaccuracies were spread maliciously. I think they came more from a lack of understanding of what an EDR tool is and does and what cyber security teams are there to do. Therefore, I thought it would be worth writing an article covering the truth behind each of these sentiments.</p>
<h3 id="1-edr-is-anti-virus-software">1. “EDR is anti-virus software”</h3>
<p>Since home and office computing became mainstream around the 1990s, the vague security threat of “viruses” has loomed. In that era, some of these were actually quite fun. You wouldn’t want them to spread to your PC, but they’d often do quirky things like <a href="https://www.youtube.com/watch?v=z7g-v3d7-Gk" target="_blank">make all the letters fall to the bottom of your screen</a> or challenge you to a game of cards to save the contents of your hard drive.</p>
<p>As malware became more serious, the internet and portable media allowed it to spread more freely, and awareness grew. It became common practice to buy an anti-virus product to identify and stop malicious software, and therefore it was only natural that the name “anti-virus” became a casual synonym for all security software, much like any video game console was sometimes referred to as “a Nintendo”.</p>
<p>But the truth is that traditional anti-virus software is quite limited. It relies on hashes and signatures to detect malware, and therefore if the author finds a way to alter their malicious code slightly each time it spreads (polymorphism), it can evade detection. Because it scans for files, standard anti-virus software can also miss attackers’ abuse of legitimate tools, because the files on disk are all benign.</p>
<p>The other limitation of traditional anti-virus software is that it usually only provides a small set of automated remediation actions. If it detects malware, it will at most usually terminate any associated processes and quarantine the files. Any further action requires manual access to the computer.</p>
<p>EDR tools, including CrowdStrike, do a whole lot more than this. While they <em>do</em> scan files and quarantine them (CrowdStrike even has a component called next-gen anti-virus, or NGAV), to call them “anti-virus” is to sell their capabilities seriously short. EDR products essentially monitor everything that happens on a computer and pick through it with a fine-toothed comb for malicious behaviour - even for threats that do not involve files, like in-memory malware or attackers with direct hands-on-keyboard access.</p>
<p>When malicious activity is detected, EDR also gives cyber security professionals many more options to respond to attacks. If it looks like malware could spread between systems, they can disconnect them from the network with a click to contain the incident. They can reverse changes made by threat actors. And if necessary, they can access the system via a remote prompt to check its current state and perform remediation actions. EDR is an invaluable Swiss army knife of response capabilities for defenders.</p>
<h3 id="2-edr-is-corporate-spyware">2. “EDR is corporate spyware”</h3>
<p>Imagine you’re tasked with protecting your organisation against cyber criminals, ransomware, and other threats - things that if they went unchecked could severely impact operations, cause irrepairable reputational damage, or cost huge sums of money. Ultimately, you and your colleagues’ jobs could all depend on your success. What’s the best tool your could have in your arsenal to detect malicious activity?</p>
<p>The answer is logs. If you can see exactly which files are written and by whom or what, which processes are run and with which command line arguments, who logged in and where from, and which IP addresses systems are communicating with, you’ll have a huge bank of data to review for signs that something is wrong, and to fully investigate any incidents when they do occur to know exactly what happened.</p>
<p>At an endpoint level, this is exactly what CrowdStrike and other EDR products do. They watch everything that’s going on, review it for malicious activity, and provide responders with detailed log data that they can pick through to determine the full scope of an incident in order to clean up the mess and put in place improved security measures to prevent the same thing from happening again in future.</p>
<p>Cyber security professionals sitting in <a href="/2018/06/01/what-a-security-operations-centre-soc-is-and-how-it-works/" target="_blank">security operations centres (SOCs)</a> have no interest in whether Bob from accounting is spending too much time on Facebook, and frankly, they don’t have the time to check. They are interested solely in investigating threats to the organisation - perhaps if Bob clicked a phishing link in an email or downloaded malware from a site claiming to offer free software.</p>
<p>In these cases, the logs provided by an EDR tool are invaluable to working out where the threat came from and how much damage it caused. Analysts can use the information within to find any other malicious files on the system and remove them, as well as identifying the exact domain and URL associated with each threat so they can be blocked and other users can’t fall foul of them in future.</p>
<p>I can understand the confusion when there are so many stories about employers watching workers through webcams and monitoring their mouse movements, but that is not the objective of an EDR product. Cyber security is not HR and it’s not some sort of hi-tech productivity monitoring service - it’s there to protect the company from malicious attacks and to contain any incidents that do occur before they can escalate, and it might well have saved your job at some point without you realising it.</p>
<h3 id="3-edr-isnt-worth-the-risk">3. “EDR isn’t worth the risk”</h3>
<p>This is by far the most subjective item on this list, and perhaps the hardest to weigh up. If EDR products like CrowdStrike require such low-level, privileged access, and one slip from a vendor can render a system unusable, is it worth the risk to run the security software in the first place?</p>
<p>I think there are a couple of misconceptions to address for those who would answer “no”. Firstly, despite what many small business leaders might think, your organisation <em>is</em> a target for cyber criminals. We are past the point where only those holding immense wealth or state secrets are attacked. <em>Your</em> business needs <em>your</em> data to operate. Even if the contents of your files are worthless to the threat actors, they know you’re likely to pay up to restore operations if they can manage to deploy ransomware on your network.</p>
<p>Secondly, this stuff isn’t rare - it happens all the time. For the reasons above, cyber criminals do not need to target specific organisations. Instead, they can scan the internet en masse and fire out “spray-and-pray” campaigns, then work with whatever vulnerabilities are found or whichever users bite. If you’re unaware of any attempts to compromise your organisation then you’re either very lucky or you need to go and pat your security team on the back for the work they’re doing in the background to keep things safe.</p>
<p>Then there’s the fallout and human impact of an incident when a threat does get through. Having worked in and around incident response for years, I can tell you that a significant incident can be a very traumatic event. It’s not just not being able to work - it’s not being able to pay employees because the payroll server is encrypted, not knowing if you’ll be able to support your family, and in same cases lives lost.</p>
<p>Cynics will point out that yesterday’s CrowdStrike incident had similar consequences, but I’d encourage you to weigh that against all the times CrowdStrike and other EDR products have <em>prevented</em> similar scenarios - many instances of which you’re unlikely to be aware of. The bug shouldn’t have made it to production and I’m sure security vendors around the world are now taking a cautious look at their development and deployment processes, but in my opinion we should be careful not to let this one very visible slip-up overshadow all the would-be disasters that were quietly stopped behind the scenes.</p></content><author><name>mattcasmith</name></author><summary type="html">When I went to bed on the evening of Friday 19th July, I couldn’t sleep. It was a stuffy summer’s night in London, and the adrenaline was still pumping through my veins after one of the more notable days in recent memory for cyber security. Still, laying awake gave me time to reflect on what I’d seen. Cyber security is a function that normally runs quietly in the background, so when it does make the headlines, it’s often for the wrong reasons. In the past, we’ve seen huge incidents like NotPetya and Wannacry become global news, but yesterday, it was a security product itself that stole the show. What exactly happened? In short: CrowdStrike pushed a bad update to Falcon, a leading endpoint detection and response (EDR) product, that caused a lot of Windows computers to fail to boot. This triggered chaos due to the number of computers around the world running the software. A workaround was quickly discovered, but it needed to be completed manually on each affected system, causing a huge headache for IT administrators. President and CEO George Kurtz took to X to try to reassure the world that no cyber security incident had occurred, and said that CrowdStrike had deployed a fix. He would later share more apologetic posts, including a letter on the firm’s website, but CrowdStrike as a company is not the focus of this post. Cyber security is a cat and mouse game between those who wish to do harm and those who protect our systems, and for this reason security software needs constant updates to detect and prevent the latest attacker techniques. To effectively monitor endpoints, the software must also run in a privileged state, which means that if it fails, it can easily cause more problems than standard user mode applications. Therefore, although CrowdStrike will certainly want to go over its internal testing and review processes, it’s important to remember that a similar thing could happen if any other security vendor made a similar mistake (for example, we saw a similar incident on a smaller scale with Symantec in 2019). Misconceptions and inaccuracies There are a thousand articles out there about the bug itself, how it was fixed, and what this means for CrowdStrike, so I won’t retread that ground here. After the initial shock of what was happening, the next thing that caught my attention was the way CrowdStrike - and by extension EDR as a whole - was being discussed, which showed a complete lack of understanding of the technology and the entire sector. The internet is a terrible place to read about things you’re an expert in, but this trend wasn’t limited to the general population on social media. Even on relatively technical forums like Hacker News there was a strange tone to the conversations, and experienced technology journalists at respected publications dropped huge inaccuracies into their writing. Broadly, these misconceptions fell into three categories: 1. “EDR is anti-virus software” 2. “EDR is corporate spyware” 3. “EDR isn’t worth the risk” I don’t think any of these inaccuracies were spread maliciously. I think they came more from a lack of understanding of what an EDR tool is and does and what cyber security teams are there to do. Therefore, I thought it would be worth writing an article covering the truth behind each of these sentiments. 1. “EDR is anti-virus software” Since home and office computing became mainstream around the 1990s, the vague security threat of “viruses” has loomed. In that era, some of these were actually quite fun. You wouldn’t want them to spread to your PC, but they’d often do quirky things like make all the letters fall to the bottom of your screen or challenge you to a game of cards to save the contents of your hard drive. As malware became more serious, the internet and portable media allowed it to spread more freely, and awareness grew. It became common practice to buy an anti-virus product to identify and stop malicious software, and therefore it was only natural that the name “anti-virus” became a casual synonym for all security software, much like any video game console was sometimes referred to as “a Nintendo”. But the truth is that traditional anti-virus software is quite limited. It relies on hashes and signatures to detect malware, and therefore if the author finds a way to alter their malicious code slightly each time it spreads (polymorphism), it can evade detection. Because it scans for files, standard anti-virus software can also miss attackers’ abuse of legitimate tools, because the files on disk are all benign. The other limitation of traditional anti-virus software is that it usually only provides a small set of automated remediation actions. If it detects malware, it will at most usually terminate any associated processes and quarantine the files. Any further action requires manual access to the computer. EDR tools, including CrowdStrike, do a whole lot more than this. While they do scan files and quarantine them (CrowdStrike even has a component called next-gen anti-virus, or NGAV), to call them “anti-virus” is to sell their capabilities seriously short. EDR products essentially monitor everything that happens on a computer and pick through it with a fine-toothed comb for malicious behaviour - even for threats that do not involve files, like in-memory malware or attackers with direct hands-on-keyboard access. When malicious activity is detected, EDR also gives cyber security professionals many more options to respond to attacks. If it looks like malware could spread between systems, they can disconnect them from the network with a click to contain the incident. They can reverse changes made by threat actors. And if necessary, they can access the system via a remote prompt to check its current state and perform remediation actions. EDR is an invaluable Swiss army knife of response capabilities for defenders. 2. “EDR is corporate spyware” Imagine you’re tasked with protecting your organisation against cyber criminals, ransomware, and other threats - things that if they went unchecked could severely impact operations, cause irrepairable reputational damage, or cost huge sums of money. Ultimately, you and your colleagues’ jobs could all depend on your success. What’s the best tool your could have in your arsenal to detect malicious activity? The answer is logs. If you can see exactly which files are written and by whom or what, which processes are run and with which command line arguments, who logged in and where from, and which IP addresses systems are communicating with, you’ll have a huge bank of data to review for signs that something is wrong, and to fully investigate any incidents when they do occur to know exactly what happened. At an endpoint level, this is exactly what CrowdStrike and other EDR products do. They watch everything that’s going on, review it for malicious activity, and provide responders with detailed log data that they can pick through to determine the full scope of an incident in order to clean up the mess and put in place improved security measures to prevent the same thing from happening again in future. Cyber security professionals sitting in security operations centres (SOCs) have no interest in whether Bob from accounting is spending too much time on Facebook, and frankly, they don’t have the time to check. They are interested solely in investigating threats to the organisation - perhaps if Bob clicked a phishing link in an email or downloaded malware from a site claiming to offer free software. In these cases, the logs provided by an EDR tool are invaluable to working out where the threat came from and how much damage it caused. Analysts can use the information within to find any other malicious files on the system and remove them, as well as identifying the exact domain and URL associated with each threat so they can be blocked and other users can’t fall foul of them in future. I can understand the confusion when there are so many stories about employers watching workers through webcams and monitoring their mouse movements, but that is not the objective of an EDR product. Cyber security is not HR and it’s not some sort of hi-tech productivity monitoring service - it’s there to protect the company from malicious attacks and to contain any incidents that do occur before they can escalate, and it might well have saved your job at some point without you realising it. 3. “EDR isn’t worth the risk” This is by far the most subjective item on this list, and perhaps the hardest to weigh up. If EDR products like CrowdStrike require such low-level, privileged access, and one slip from a vendor can render a system unusable, is it worth the risk to run the security software in the first place? I think there are a couple of misconceptions to address for those who would answer “no”. Firstly, despite what many small business leaders might think, your organisation is a target for cyber criminals. We are past the point where only those holding immense wealth or state secrets are attacked. Your business needs your data to operate. Even if the contents of your files are worthless to the threat actors, they know you’re likely to pay up to restore operations if they can manage to deploy ransomware on your network. Secondly, this stuff isn’t rare - it happens all the time. For the reasons above, cyber criminals do not need to target specific organisations. Instead, they can scan the internet en masse and fire out “spray-and-pray” campaigns, then work with whatever vulnerabilities are found or whichever users bite. If you’re unaware of any attempts to compromise your organisation then you’re either very lucky or you need to go and pat your security team on the back for the work they’re doing in the background to keep things safe. Then there’s the fallout and human impact of an incident when a threat does get through. Having worked in and around incident response for years, I can tell you that a significant incident can be a very traumatic event. It’s not just not being able to work - it’s not being able to pay employees because the payroll server is encrypted, not knowing if you’ll be able to support your family, and in same cases lives lost. Cynics will point out that yesterday’s CrowdStrike incident had similar consequences, but I’d encourage you to weigh that against all the times CrowdStrike and other EDR products have prevented similar scenarios - many instances of which you’re unlikely to be aware of. The bug shouldn’t have made it to production and I’m sure security vendors around the world are now taking a cautious look at their development and deployment processes, but in my opinion we should be careful not to let this one very visible slip-up overshadow all the would-be disasters that were quietly stopped behind the scenes.</summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://mattcasmith.net/wp-content/uploads/2023/01/mcas.png" /><media:content medium="image" url="https://mattcasmith.net/wp-content/uploads/2023/01/mcas.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Centralisation, repeatability, and automation in a modular SOC</title><link href="https://mattcasmith.net/2023/11/26/modular-soc-centralisation-repeatability-automation" rel="alternate" type="text/html" title="Centralisation, repeatability, and automation in a modular SOC" /><published>2023-11-26T00:00:00+00:00</published><updated>2023-11-26T00:00:00+00:00</updated><id>https://mattcasmith.net/2023/11/26/modular-soc-centralisation-repeatability-automation</id><content type="html" xml:base="https://mattcasmith.net/2023/11/26/modular-soc-centralisation-repeatability-automation"><p>The dictionary definition of “modular” leaves a little to be desired: “Employing or involving a module or modules as the basis of design or construction.” What is implied, but that I would make explicit, is that parts of the whole can be swapped out easily while maintaining the functionality of the product.</p>
<p>Take <a href="https://frame.work/gb/en" target="_blank">Framework</a>, for example – it bucks the trend for impenetrable, unrepairable, and uncustomisable laptops by introducing a modular design. Want a different processor? Or a keyboard with a numpad? Or even a coloured bezel around your screen? Easy. Everything simply clips in and out, meaning that your device can be adapted over time without the waste of abandoning it for a whole new model.</p>
<p>The same should apply to your security operations centre (SOC). You’ll always want additional functionality to adapt to advances in technology and changing business requirements, but you should build a foundation that allows for these more tactical changes without wasting time altering the fundamentals, or introducing inefficiency by tacking on extra processes for your team to remember.</p>
<h3 id="cra-as-an-approach-to-a-modular-soc">CRA as an approach to a modular SOC</h3>
<p>To achieve this, I try to keep three things in mind: centralisation, repeatability, and automation.</p>
<h4 id="centralisation">Centralisation</h4>
<p>An initial priority should be to develop one queue to rule them all. Rather than having analysts jump between different security tools to look for detections, they should be centralised and integrated into a single pane of glass showing the current state of play across all platforms.</p>
<p>This doesn’t just apply to detections, but also to communications with your users or customers and also any output from automation tools that must be reviewed. Storing all of this in central tickets or cases not only enables quicker responses to incoming messages and replies, but also ensures the information is easily available to analysts working on similar detections and queries in future.</p>
<p>This setup also means analysts themselves can be modular - not in such a way that individuals are viewed as interchangeable in their roles, but in a way that any ticket that enters (or returns to) the queue can be claimed by any available analyst, empowering them to work more efficiently and exposing them to a greater variety of scenarios. For example, by picking up a reply to an existing ticket, a more junior analyst has the opportunity to learn about the analysis performed by the original owner.</p>
<h4 id="repeatability">Repeatability</h4>
<p>Supporting the technology should be processes, templates, and so on that ensure that work is completed consistently, and providing less experienced analysts with documentation to refer to for unfamiliar case types. Templates are huge timesavers for any content that analysts must compose multiple times per day, and pre-cooked log queries and scripts can help to cut the time spent on common investigations.</p>
<p>Centralisation helps here as it reduces the number of processes required - for example, if closing a case in the security orchestration, automation, and response (SOAR) tool automatically closes detections via an API, analysts only need to know how to do that, and not how to resolve detections in tools A, B, and C.</p>
<p>By this point, onboarding a new tool should be as simple as performing an API integration and producing response processes for any new detection types. Once detections and tickets are centralised and processes are recorded, the workflow for each can become so standardised that it may even be possible (in theory, at least) to map out the entire analyst-facing process into a single flowchart.</p>
<h4 id="automation">Automation</h4>
<p>A great way to save analysts’ time is to automate basic, often-repeated response processes, but even before that the easiest and most impactful automation workflows are often those associated with your SOC’s housekeeping - agent health checks, weekly metrics, sweeps for unresolved detections, and so on.</p>
<p>Further down the line, of course, your automation suite will extend into more complex and specialised processes - automatic triage and remediation of commonly seen types of malware, for example. At this point the testing process becomes critical, because a wrong step during eradication can be disastrous.</p>
<p>You might be thinking, “How can I ever fully trust the automation logic to make the right call all the time?” But to this I’d argue that even partial automation is more efficient than none at all. Sending output to a ticket for an analyst to review, or running a triage script as a first step to provide the basis for an analyst to perform remediation on the discovered artefacts, still saves time and makes people’s jobs easier.</p>
<h3 id="adapting-and-expanding">Adapting and expanding</h3>
<p>Once the above has been implemented, even to a basic level, adapting SOC operations to new challenges becomes far easier than it would be in a more manual, less consolidated environment.</p>
<ul>
<li>
<p><strong>Procured a new security tool?</strong> Perform the relevant API integration with your SOAR so its detections appear in the queue, and build out any basic automation workflows and processes required</p>
</li>
<li>
<p><strong>Analyst working a big case off sick?</strong> No problem - when the customer next replies to the ticket it’ll pop right into the queue, with all the relevant infromation available to the next available analyst</p>
</li>
<li>
<p><strong>Swamped by a new, widespread type of malware?</strong> Build out triage and/or remediation scripts and set up a workflow whereby your SOAR runs them automatically for relevant detections</p>
</li>
<li>
<p><strong>Onboarding a new part of the organisation?</strong> Once its tools are integrated via their APIs and the relevant escalation contacts are centrally stored, it’ll slot right in alongside your existing customers</p>
</li>
</ul>
<p>Both IT and cyber security are rapidly changing fields, and therefore it makes sense to build a SOC that is agile and adaptable to those changes. By structuring your SOC with modularity in mind, you’re putting yourself and your team in the best position to respond swiftly when change inevitably occurs.</p></content><author><name>mattcasmith</name></author><summary type="html">The dictionary definition of “modular” leaves a little to be desired: “Employing or involving a module or modules as the basis of design or construction.” What is implied, but that I would make explicit, is that parts of the whole can be swapped out easily while maintaining the functionality of the product. Take Framework, for example – it bucks the trend for impenetrable, unrepairable, and uncustomisable laptops by introducing a modular design. Want a different processor? Or a keyboard with a numpad? Or even a coloured bezel around your screen? Easy. Everything simply clips in and out, meaning that your device can be adapted over time without the waste of abandoning it for a whole new model. The same should apply to your security operations centre (SOC). You’ll always want additional functionality to adapt to advances in technology and changing business requirements, but you should build a foundation that allows for these more tactical changes without wasting time altering the fundamentals, or introducing inefficiency by tacking on extra processes for your team to remember. CRA as an approach to a modular SOC To achieve this, I try to keep three things in mind: centralisation, repeatability, and automation. Centralisation An initial priority should be to develop one queue to rule them all. Rather than having analysts jump between different security tools to look for detections, they should be centralised and integrated into a single pane of glass showing the current state of play across all platforms. This doesn’t just apply to detections, but also to communications with your users or customers and also any output from automation tools that must be reviewed. Storing all of this in central tickets or cases not only enables quicker responses to incoming messages and replies, but also ensures the information is easily available to analysts working on similar detections and queries in future. This setup also means analysts themselves can be modular - not in such a way that individuals are viewed as interchangeable in their roles, but in a way that any ticket that enters (or returns to) the queue can be claimed by any available analyst, empowering them to work more efficiently and exposing them to a greater variety of scenarios. For example, by picking up a reply to an existing ticket, a more junior analyst has the opportunity to learn about the analysis performed by the original owner. Repeatability Supporting the technology should be processes, templates, and so on that ensure that work is completed consistently, and providing less experienced analysts with documentation to refer to for unfamiliar case types. Templates are huge timesavers for any content that analysts must compose multiple times per day, and pre-cooked log queries and scripts can help to cut the time spent on common investigations. Centralisation helps here as it reduces the number of processes required - for example, if closing a case in the security orchestration, automation, and response (SOAR) tool automatically closes detections via an API, analysts only need to know how to do that, and not how to resolve detections in tools A, B, and C. By this point, onboarding a new tool should be as simple as performing an API integration and producing response processes for any new detection types. Once detections and tickets are centralised and processes are recorded, the workflow for each can become so standardised that it may even be possible (in theory, at least) to map out the entire analyst-facing process into a single flowchart. Automation A great way to save analysts’ time is to automate basic, often-repeated response processes, but even before that the easiest and most impactful automation workflows are often those associated with your SOC’s housekeeping - agent health checks, weekly metrics, sweeps for unresolved detections, and so on. Further down the line, of course, your automation suite will extend into more complex and specialised processes - automatic triage and remediation of commonly seen types of malware, for example. At this point the testing process becomes critical, because a wrong step during eradication can be disastrous. You might be thinking, “How can I ever fully trust the automation logic to make the right call all the time?” But to this I’d argue that even partial automation is more efficient than none at all. Sending output to a ticket for an analyst to review, or running a triage script as a first step to provide the basis for an analyst to perform remediation on the discovered artefacts, still saves time and makes people’s jobs easier. Adapting and expanding Once the above has been implemented, even to a basic level, adapting SOC operations to new challenges becomes far easier than it would be in a more manual, less consolidated environment. Procured a new security tool? Perform the relevant API integration with your SOAR so its detections appear in the queue, and build out any basic automation workflows and processes required Analyst working a big case off sick? No problem - when the customer next replies to the ticket it’ll pop right into the queue, with all the relevant infromation available to the next available analyst Swamped by a new, widespread type of malware? Build out triage and/or remediation scripts and set up a workflow whereby your SOAR runs them automatically for relevant detections Onboarding a new part of the organisation? Once its tools are integrated via their APIs and the relevant escalation contacts are centrally stored, it’ll slot right in alongside your existing customers Both IT and cyber security are rapidly changing fields, and therefore it makes sense to build a SOC that is agile and adaptable to those changes. By structuring your SOC with modularity in mind, you’re putting yourself and your team in the best position to respond swiftly when change inevitably occurs.</summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://mattcasmith.net/wp-content/uploads/2023/01/mcas.png" /><media:content medium="image" url="https://mattcasmith.net/wp-content/uploads/2023/01/mcas.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Cyber security sometimes means learning things backwards</title><link href="https://mattcasmith.net/2023/03/26/cyber-security-learning-things-backwards" rel="alternate" type="text/html" title="Cyber security sometimes means learning things backwards" /><published>2023-03-26T00:00:00+00:00</published><updated>2023-03-26T00:00:00+00:00</updated><id>https://mattcasmith.net/2023/03/26/cyber-security-learning-things-backwards</id><content type="html" xml:base="https://mattcasmith.net/2023/03/26/cyber-security-learning-things-backwards"><p>Stick around cyber security Twitter or LinkedIn for long enough and you’ll likely see somebody raise a question about how to get into the industry. You’ll also likely see a reply that describes a kind of rite of passage from sysadmin, to SOC analyst, to just about any other security role.</p>
<p>The logic behind this - that you need to know how systems work in order to defend them - is sound, but I feel like the entry requirements are often exaggerated as a form of gatekeeping, and that years in a full-on sysadmin role aren’t necessarily required to investigate cyber attacks effectively. There are other paths.</p>
<h3 id="learning-the-ropes">Learning the ropes</h3>
<p>I’m a good example here. From childhood I was <em>vaguely</em> technical in a hobbyist sense. That was enough for me to peer through the glass into the tech world and get a rough feeling for how computers work, but I certainly wasn’t administrating corporate Active Directory domains during my years as a journalist.</p>
<p>Some education was required. At the <a href="/2017/03/27/finishing-line-ive-passed-my-gcih-exam/">SANS Cyber Retraining Academy</a> I took courses that equipped me with fundamentals covering operating systems, networking, and components of business environments that I hadn’t had any exposure to in my days tinkering with my home computer. It took the scattered knowledge I’d accumulated, filled in the blanks, and gave it better structure and context.</p>
<p>One of the best decisions I made around that time was to teach myself Python and the Linux command line. Do I use these specific tools every day now? No. But defensive PowerShell scripts, malicious JavaScript files, and commands run by threat actors all use very similar syntax, to a level that I could decode and roughly understand them even before I properly taught myself the languages involved.</p>
<h3 id="beyond-the-basics">Beyond the basics</h3>
<p>Not everything can be learnt in the classroom, and I’ve written peviously about how <a href="/2018/01/20/what-cyber-security-courses-dont-prepare-you-for/">applying foundational knowledge outside of a lab environment takes some practice</a>, but with the basics under your belt, you can sometimes learn just as much about the way technology works by examining the way threat actors try to abuse it as you can by building and configuring the systems yourself.</p>
<p>The key as far as I’m concerned isn’t years on the helpdesk (although that won’t hurt), it’s staying curious and doing more digging than your day job requires. Let your investigations and administrative duties point you in a useful direction (who better to show you what you need to learn to stop attackers than the attackers themselves?) but stick with the material and go deeper than you need to.</p>
<p>Eventually you’ll develop a computing literacy that extends beyond your immediate knowledge. As an example, I recently helped an administrator to troubleshoot an Active Directory <a href="https://learn.microsoft.com/en-us/previous-versions/windows/desktop/policy/group-policy-objects" target="_blank">group policy object (GPO)</a>. As a non-sysadmin, I’d never configured one myself, but my training taught me <em>what</em> a GPO is, I’d seen enough to know what they look like, and some quick research was enough to work out the specifics.</p>
<p>Cyber security - particularly on the defensive side, where your work is rarely planned ahead - can throw practically anything at you, and it’s impossible to learn everything to the depth that an administrator looking after a certain type of system would. The best approach is to know your basics and stay curious to continuously grow and build on top of that. This way you’ll be clued up enough to understand the documentation (and, let’s face it, your Google results) when something new lands on your desk.</p></content><author><name>mattcasmith</name></author><summary type="html">Stick around cyber security Twitter or LinkedIn for long enough and you’ll likely see somebody raise a question about how to get into the industry. You’ll also likely see a reply that describes a kind of rite of passage from sysadmin, to SOC analyst, to just about any other security role. The logic behind this - that you need to know how systems work in order to defend them - is sound, but I feel like the entry requirements are often exaggerated as a form of gatekeeping, and that years in a full-on sysadmin role aren’t necessarily required to investigate cyber attacks effectively. There are other paths. Learning the ropes I’m a good example here. From childhood I was vaguely technical in a hobbyist sense. That was enough for me to peer through the glass into the tech world and get a rough feeling for how computers work, but I certainly wasn’t administrating corporate Active Directory domains during my years as a journalist. Some education was required. At the SANS Cyber Retraining Academy I took courses that equipped me with fundamentals covering operating systems, networking, and components of business environments that I hadn’t had any exposure to in my days tinkering with my home computer. It took the scattered knowledge I’d accumulated, filled in the blanks, and gave it better structure and context. One of the best decisions I made around that time was to teach myself Python and the Linux command line. Do I use these specific tools every day now? No. But defensive PowerShell scripts, malicious JavaScript files, and commands run by threat actors all use very similar syntax, to a level that I could decode and roughly understand them even before I properly taught myself the languages involved. Beyond the basics Not everything can be learnt in the classroom, and I’ve written peviously about how applying foundational knowledge outside of a lab environment takes some practice, but with the basics under your belt, you can sometimes learn just as much about the way technology works by examining the way threat actors try to abuse it as you can by building and configuring the systems yourself. The key as far as I’m concerned isn’t years on the helpdesk (although that won’t hurt), it’s staying curious and doing more digging than your day job requires. Let your investigations and administrative duties point you in a useful direction (who better to show you what you need to learn to stop attackers than the attackers themselves?) but stick with the material and go deeper than you need to. Eventually you’ll develop a computing literacy that extends beyond your immediate knowledge. As an example, I recently helped an administrator to troubleshoot an Active Directory group policy object (GPO). As a non-sysadmin, I’d never configured one myself, but my training taught me what a GPO is, I’d seen enough to know what they look like, and some quick research was enough to work out the specifics. Cyber security - particularly on the defensive side, where your work is rarely planned ahead - can throw practically anything at you, and it’s impossible to learn everything to the depth that an administrator looking after a certain type of system would. The best approach is to know your basics and stay curious to continuously grow and build on top of that. This way you’ll be clued up enough to understand the documentation (and, let’s face it, your Google results) when something new lands on your desk.</summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://mattcasmith.net/wp-content/uploads/2023/01/mcas.png" /><media:content medium="image" url="https://mattcasmith.net/wp-content/uploads/2023/01/mcas.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Using winget to automate software deployment to a new laptop</title><link href="https://mattcasmith.net/2023/02/17/winget-automate-software-deployment-new-laptop" rel="alternate" type="text/html" title="Using winget to automate software deployment to a new laptop" /><published>2023-02-17T00:00:00+00:00</published><updated>2023-02-17T00:00:00+00:00</updated><id>https://mattcasmith.net/2023/02/17/winget-automate-software-deployment-new-laptop</id><content type="html" xml:base="https://mattcasmith.net/2023/02/17/winget-automate-software-deployment-new-laptop"><p>I got my first new laptop in six years this week! The new hardware is definitely exciting, but reviews aren’t really my thing, so while I’ll inevitably <a href="https://twitter.com/mattcasmith" target="_blank">tweet</a> about how the Microsoft Surface Laptop fares, that’s not the purpose of this post. This is more about the mundane job of porting all my usual applications over to a new PC, and a handy new Windows feature that helped to make things a little easier - <code>winget</code>.</p>
<p>Before we get started, I want to acknowledge both that I’m <a href="https://www.bleepingcomputer.com/news/security/winget-how-to-use-windows-10s-new-native-package-manager/" target="_blank">late to the party</a> on <code>winget</code> and that there have been <a href="https://chocolatey.org/" target="_blank">package managers</a> around on Windows for years - but it’s certainly a cool and useful addition to the core operating system, which bucks the trend when you consider how frequently Microsoft <em>removes</em> more advanced features and options these days (or at least applies confusing double UIs).</p>
<p>I’ll also concede that taking the time to write a PowerShell script to automate software deployment is probably overkill for most users - myself included. The time spent on development and testing is <em>probably</em> greater than the amount of time it would take to download and install each package individually, but at the end of the day I now have a pre-baked script I can use to drop my software on any future systems.</p>
<h3 id="initial-research">Initial research</h3>
<p>Before I even got as far as creating a <code>PS1</code> file, my first step was to do some research to identify the software packages I wanted to install. To achieve this, I used the following <code>winget</code> command.</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="n">winget</span> <span class="n">search</span> <span class="o">&lt;</span><span class="n">APP_NAME</span><span class="o">&gt;</span></code></pre></figure>
<p>This ran a search across all the configured repositories (by default, basically the Microsoft Store and <code>winget</code>’s own one). The most important details were in the <code>Id</code> column, which generally follows the format <code>Developer.Product</code>. There were a lot of third-party apps that had the names of popular software in their own names, so this helped to sort the wheat from the chaff and identify the real packages.</p>
<p><img src="/wp-content/uploads/2023/02/winget_0.png" /></p>
<p>During this planning stage, I considered the software I wanted to deploy to my new system and ran the <code>winget search</code> command to find the corresponding packages until I had a complete list.</p>
<h3 id="the-powershell-script">The PowerShell script</h3>
<p>Now I could start writing a PowerShell script to iterate through my list of applications and try to install each of them. Firstly, I created an array to store the list (note that only the <code>Id</code> values here match the repository data - the names are my own more readable ones, for display and logging only).</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="err">$</span><span class="n">install_apps</span> <span class="o">=</span>
<span class="p">(</span><span class="s">"Firefox"</span><span class="p">,</span> <span class="s">"Mozilla.Firefox"</span><span class="p">),</span>
<span class="p">(</span><span class="s">"Steam"</span><span class="p">,</span> <span class="s">"Valve.Steam"</span><span class="p">),</span>
<span class="p">(</span><span class="s">"Discord"</span><span class="p">,</span> <span class="s">"Discord.Discord"</span><span class="p">),</span>
<span class="p">(</span><span class="s">"Netflix"</span><span class="p">,</span> <span class="s">"9WZDNCRFJ3TJ"</span><span class="p">),</span>
<span class="p">(</span><span class="s">"Spotify"</span><span class="p">,</span> <span class="s">"Spotify.Spotify"</span><span class="p">)</span></code></pre></figure>
<p>Next, I used a <code>foreach</code> loop to work through each application in <code>$install_apps</code> and attempt to run <code>winget</code> to install the corresponding package according to its <code>Id</code>, using the <code>--silent</code> flag to keep output to a minimum and require as little user interaction as possible to complete the deployment.</p>
<p>There are a number of reasons why installation of any given application might fail - a missing package or insufficient permissions to name just a couple. To prevent missing software from flying under the radar, I used an <code>if</code>/<code>else</code> statement based on the <code>winget</code> exit code (<code>$LASTEXITCODE</code>) to detect any issues.</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="n">foreach</span> <span class="p">(</span><span class="err">$</span><span class="n">app</span> <span class="ow">in</span> <span class="err">$</span><span class="n">install_apps</span><span class="p">)</span> <span class="p">{</span>
<span class="n">Write</span><span class="o">-</span><span class="n">Host</span> <span class="s">"Installing"</span> <span class="err">$</span><span class="n">app</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="s">"..."</span>
<span class="n">winget</span> <span class="n">install</span> <span class="o">--</span><span class="n">silent</span> <span class="o">--</span><span class="nb">id</span> <span class="err">$</span><span class="n">app</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span>
<span class="k">if</span> <span class="p">(</span><span class="err">$</span><span class="n">LASTEXITCODE</span> <span class="o">-</span><span class="n">eq</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
<span class="n">Write</span><span class="o">-</span><span class="n">Host</span> <span class="err">$</span><span class="n">app</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="s">"installation complete."</span>
<span class="n">Write</span><span class="o">-</span><span class="n">Host</span> <span class="s">""</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="n">Write</span><span class="o">-</span><span class="n">Host</span> <span class="err">$</span><span class="n">app</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="s">"installation failed."</span>
<span class="n">Write</span><span class="o">-</span><span class="n">Host</span> <span class="s">""</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></figure>
<p>And that’s pretty much all I needed to get going! I also added a section to install Windows Subsystem for Linux (with Ubuntu) and a few extra lines to log failures to a file (in case I missed them in the terminal), but the code above would be perfectly adequate to try to install all the packages in the given list.</p>
<h3 id="the-results">The results</h3>
<p>So how did things go with the new laptop? Did my <code>winget</code> PowerShell script solve all my problems and allow me to kick back and relax as my applications were automatically deployed to my new system?</p>
<p>Well, to tell the complete truth, not quite… But it was very close.</p>
<p><img src="/wp-content/uploads/2023/02/winget_1.png" /></p>
<p>As you can see from the screenshot above, <code>winget</code> undoubtedly made the job much quicker and easier, but it wasn’t <em>entirely</em> hands-off. Spotify was the only app that failed to install for some reason (perhaps because it’s one of those apps that come half-installed on a fresh Windows system?), but there were also a couple of others that required me to hit the <code>Y</code> key to accept licence agreements and terms of use.</p>
<p style="float: left; width: 90%; padding: 5px; padding-left: 15px; padding-right: 5%; margin: 30px 0 10px 0; font-style: italic; border-left: 5px solid #FFFF00; background: #FFFFB9; color: #333;">I eventually found a solution to this part of the issue - unfortunately just after I ran the script. You can accept licence agreements automatically by using the <code>--accept-source-agreements</code> and <code>--accept-package-agreements</code> parameters when running <code>winget</code>.</p>
<p>Still, it was a fun experiment, saved a lot of time downloading installers from developer websites and running them manually via the GUI, and leaves me with a script that will be easy to repurpose to (almost) automate the setup of any new computers I adopt in future. While <code>winget</code> isn’t the perfect zero-touch solution, I’d definitely recommend giving it a go - particularly if you ever find yourself in a situation where you need to set up multiple systems in a short time and a cheat code would help to preserve your sanity.</p></content><author><name>mattcasmith</name></author><summary type="html">I got my first new laptop in six years this week! The new hardware is definitely exciting, but reviews aren’t really my thing, so while I’ll inevitably tweet about how the Microsoft Surface Laptop fares, that’s not the purpose of this post. This is more about the mundane job of porting all my usual applications over to a new PC, and a handy new Windows feature that helped to make things a little easier - winget. Before we get started, I want to acknowledge both that I’m late to the party on winget and that there have been package managers around on Windows for years - but it’s certainly a cool and useful addition to the core operating system, which bucks the trend when you consider how frequently Microsoft removes more advanced features and options these days (or at least applies confusing double UIs). I’ll also concede that taking the time to write a PowerShell script to automate software deployment is probably overkill for most users - myself included. The time spent on development and testing is probably greater than the amount of time it would take to download and install each package individually, but at the end of the day I now have a pre-baked script I can use to drop my software on any future systems. Initial research Before I even got as far as creating a PS1 file, my first step was to do some research to identify the software packages I wanted to install. To achieve this, I used the following winget command. winget search &lt;APP_NAME&gt; This ran a search across all the configured repositories (by default, basically the Microsoft Store and winget’s own one). The most important details were in the Id column, which generally follows the format Developer.Product. There were a lot of third-party apps that had the names of popular software in their own names, so this helped to sort the wheat from the chaff and identify the real packages. During this planning stage, I considered the software I wanted to deploy to my new system and ran the winget search command to find the corresponding packages until I had a complete list. The PowerShell script Now I could start writing a PowerShell script to iterate through my list of applications and try to install each of them. Firstly, I created an array to store the list (note that only the Id values here match the repository data - the names are my own more readable ones, for display and logging only). $install_apps = ("Firefox", "Mozilla.Firefox"), ("Steam", "Valve.Steam"), ("Discord", "Discord.Discord"), ("Netflix", "9WZDNCRFJ3TJ"), ("Spotify", "Spotify.Spotify") Next, I used a foreach loop to work through each application in $install_apps and attempt to run winget to install the corresponding package according to its Id, using the --silent flag to keep output to a minimum and require as little user interaction as possible to complete the deployment. There are a number of reasons why installation of any given application might fail - a missing package or insufficient permissions to name just a couple. To prevent missing software from flying under the radar, I used an if/else statement based on the winget exit code ($LASTEXITCODE) to detect any issues. foreach ($app in $install_apps) { Write-Host "Installing" $app[0] "..." winget install --silent --id $app[1] if ($LASTEXITCODE -eq 0) { Write-Host $app[0] "installation complete." Write-Host "" } else { Write-Host $app[0] "installation failed." Write-Host "" } } And that’s pretty much all I needed to get going! I also added a section to install Windows Subsystem for Linux (with Ubuntu) and a few extra lines to log failures to a file (in case I missed them in the terminal), but the code above would be perfectly adequate to try to install all the packages in the given list. The results So how did things go with the new laptop? Did my winget PowerShell script solve all my problems and allow me to kick back and relax as my applications were automatically deployed to my new system? Well, to tell the complete truth, not quite… But it was very close. As you can see from the screenshot above, winget undoubtedly made the job much quicker and easier, but it wasn’t entirely hands-off. Spotify was the only app that failed to install for some reason (perhaps because it’s one of those apps that come half-installed on a fresh Windows system?), but there were also a couple of others that required me to hit the Y key to accept licence agreements and terms of use. I eventually found a solution to this part of the issue - unfortunately just after I ran the script. You can accept licence agreements automatically by using the --accept-source-agreements and --accept-package-agreements parameters when running winget. Still, it was a fun experiment, saved a lot of time downloading installers from developer websites and running them manually via the GUI, and leaves me with a script that will be easy to repurpose to (almost) automate the setup of any new computers I adopt in future. While winget isn’t the perfect zero-touch solution, I’d definitely recommend giving it a go - particularly if you ever find yourself in a situation where you need to set up multiple systems in a short time and a cheat code would help to preserve your sanity.</summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://mattcasmith.net/wp-content/uploads/2023/02/winget_1.png" /><media:content medium="image" url="https://mattcasmith.net/wp-content/uploads/2023/02/winget_1.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">2023: Thoughts on new challenges and sharing experiences</title><link href="https://mattcasmith.net/2023/01/13/2023-new-challenges-sharing-experiences" rel="alternate" type="text/html" title="2023&#58; Thoughts on new challenges and sharing experiences" /><published>2023-01-13T00:00:00+00:00</published><updated>2023-01-13T00:00:00+00:00</updated><id>https://mattcasmith.net/2023/01/13/2023-new-challenges-sharing-experiences</id><content type="html" xml:base="https://mattcasmith.net/2023/01/13/2023-new-challenges-sharing-experiences"><p>A belated happy new year! If you’re reading this, I hope you have a terrific 2023.</p>
<p>I recently went back and read some old posts that have long since been deleted from this blog - writings from my journalism days that reminded me of my perspectives on certain news events and emerging technologies from the mid-2010s and sparked some nostalgia for my early twenties along the way.</p>
<p>By the end of 2017, I’d landed a job in cyber security and the tone of my blog changed. Gone were the slightly rambling posts that gave a sense of personality, and what came was primarily content consisting of technical notes about things I’d been working on in <a href="/category/programming">Python</a> or a particular <a href="/category/cyber-security">forensic artefact</a> (don’t get me wrong - I’m proud of many of these articles, and they remain a useful way of organising my thoughts).</p>
<h3 id="reasons-for-retreating">Reasons for retreating</h3>
<p>My change of industry played a role in this. Despite having reported on cyber security for four years beforehand, my hesitance over ranting about a sector I was quite new to in the same way I’d written about consumer technology was justified. Another factor was that my job just wasn’t as public - an interesting conversation or briefing as a journalist might make a good blog post if it didn’t fit my employer’s site, but accounts of incidents I work and security discussions with clients must stay behind closed doors.</p>
<p>But there was also a force at play that was more toxic than humbleness or confidentiality: anxiety. I’d seen flashes of it before as I battled imposter syndrome on entering cyber security, but the real thing crept up on me during the COVID-19 pandemic, and I spent a good deal of 2022 pushing myself in various ways to get it under control. It still has its occasional moments, but I’m in a much better place heading into 2023.</p>
<p>While keeping background stress as low as possible helped with this, I was also anxious in less of a clinical sense about writing. The less you put yourself out there, the less likely it is that you’ll be called out on something, or somebody will tell you that you’re wrong. Inertia is like laying in a safety net.</p>
<h3 id="incremental-improvement">Incremental improvement</h3>
<p>But if you’re sitting still on the net, paralysed out of fear of action and its consequences, you’ll never walk the tightrope. Sure, it’s far less likely that you’ll fail, but you’ll also never do anything remarkable. For all we know, the greatest ideas in history might not be the ones in modern day textbooks - they might never have made it that far, living and dying inside their creators’ heads because they were never shared.</p>
<p>I doubt anything I write on these pages will change the world, and most of my ideas are iterations on or combinations of existing ones, rather than fully original thoughts. But even if I have nothing revolutonary to say, the little things that inspire me to put digital pen to paper could still be enough to make our small corner of the world a better place, even if it’s just a tip that saves someone else five minutes.</p>
<p>Some changes in my life have put me what will hopefully be a far better place to trial ideas and implement change in 2023, and I intend to write some more personal, thoughtful posts to share my learnings from these experiments. I still might not be able to go into as much detail as I did a decade ago, but I’m feeling energetic, inspired, and more motivated to share my experiences than I have been in years.</p>
<p>If you’re interested, check <a href="https://mattcasmith.net">my blog</a> regularly for new posts, and <a href="https://twitter.com/mattcasmith" target="_blank">follow me on Twitter</a> for day-to-day updates. Feel free to <a href="mailto:[email protected]">email me</a>, too - it’s an exciting, optimistic time and I’d be happy to connect.</p></content><author><name>mattcasmith</name></author><summary type="html">A belated happy new year! If you’re reading this, I hope you have a terrific 2023. I recently went back and read some old posts that have long since been deleted from this blog - writings from my journalism days that reminded me of my perspectives on certain news events and emerging technologies from the mid-2010s and sparked some nostalgia for my early twenties along the way. By the end of 2017, I’d landed a job in cyber security and the tone of my blog changed. Gone were the slightly rambling posts that gave a sense of personality, and what came was primarily content consisting of technical notes about things I’d been working on in Python or a particular forensic artefact (don’t get me wrong - I’m proud of many of these articles, and they remain a useful way of organising my thoughts). Reasons for retreating My change of industry played a role in this. Despite having reported on cyber security for four years beforehand, my hesitance over ranting about a sector I was quite new to in the same way I’d written about consumer technology was justified. Another factor was that my job just wasn’t as public - an interesting conversation or briefing as a journalist might make a good blog post if it didn’t fit my employer’s site, but accounts of incidents I work and security discussions with clients must stay behind closed doors. But there was also a force at play that was more toxic than humbleness or confidentiality: anxiety. I’d seen flashes of it before as I battled imposter syndrome on entering cyber security, but the real thing crept up on me during the COVID-19 pandemic, and I spent a good deal of 2022 pushing myself in various ways to get it under control. It still has its occasional moments, but I’m in a much better place heading into 2023. While keeping background stress as low as possible helped with this, I was also anxious in less of a clinical sense about writing. The less you put yourself out there, the less likely it is that you’ll be called out on something, or somebody will tell you that you’re wrong. Inertia is like laying in a safety net. Incremental improvement But if you’re sitting still on the net, paralysed out of fear of action and its consequences, you’ll never walk the tightrope. Sure, it’s far less likely that you’ll fail, but you’ll also never do anything remarkable. For all we know, the greatest ideas in history might not be the ones in modern day textbooks - they might never have made it that far, living and dying inside their creators’ heads because they were never shared. I doubt anything I write on these pages will change the world, and most of my ideas are iterations on or combinations of existing ones, rather than fully original thoughts. But even if I have nothing revolutonary to say, the little things that inspire me to put digital pen to paper could still be enough to make our small corner of the world a better place, even if it’s just a tip that saves someone else five minutes. Some changes in my life have put me what will hopefully be a far better place to trial ideas and implement change in 2023, and I intend to write some more personal, thoughtful posts to share my learnings from these experiments. I still might not be able to go into as much detail as I did a decade ago, but I’m feeling energetic, inspired, and more motivated to share my experiences than I have been in years. If you’re interested, check my blog regularly for new posts, and follow me on Twitter for day-to-day updates. Feel free to email me, too - it’s an exciting, optimistic time and I’d be happy to connect.</summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://mattcasmith.net/wp-content/uploads/2023/01/mcas.png" /><media:content medium="image" url="https://mattcasmith.net/wp-content/uploads/2023/01/mcas.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Investigating Explorer’s temporary ZIP folders and retrieving files</title><link href="https://mattcasmith.net/2022/12/15/investigating-explorer-temporary-zip-folders" rel="alternate" type="text/html" title="Investigating Explorer's temporary ZIP folders and retrieving files" /><published>2022-12-14T00:00:00+00:00</published><updated>2022-12-14T00:00:00+00:00</updated><id>https://mattcasmith.net/2022/12/15/investigating-explorer-temporary-zip-folders</id><content type="html" xml:base="https://mattcasmith.net/2022/12/15/investigating-explorer-temporary-zip-folders"><p>If I was to describe how often malware is downloaded within ZIP archives, “common” would be a huge understatement. A key artefact in these investigations is the temporary directory Windows creates when a user opens an archive in Explorer, but I recently realised I’d never actually run a proper test to see when the folder is created, when it is not, and when it is deleted. So to clear that up, here’s a quick blog post.</p>
<h3 id="zip-archives-on-easy-mode">ZIP archives on easy mode</h3>
<p>There was a time when a ZIP archive was an indecipherable box that Windows couldn’t see inside. To access the files within, you’d have to download third-party software, like a trial version of WinZip that would incessantly prompt you to buy a licence whenever you downloaded something from the internet.</p>
<p>That all came to an end with - I <em>think</em>, although I may be slightly mistaken - Windows XP, when Microsoft added native capabilities within Windows Explorer to zip and unzip files. This is now a near-seamless experience, with the user able to open a ZIP archive as though it were a normal folder, as seen below.</p>
<p><img src="/wp-content/uploads/2022/12/explorer-temporary-zip-folders_1.png" /></p>
<p>But what’s going on behind the scenes when this happens, and what artefacts does it leave behind for forensicators and responders? With so much malware downloaded from the internet and attached to emails inside ZIP archives, knowing where to look can be a powerful tool in tracing user actions.</p>
<h3 id="the-amazing-disappearing-folder">The amazing disappearing folder</h3>
<p>This focus of this post is the temporary directory that Explorer creates, quietly unzipping parts of the archive on demand in the background so that it can present the user with that seamless interface on the front end. It’s located within the user’s <code>AppData</code> <code>Temp</code> folder, under a path like this:</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="n">C</span><span class="p">:</span>\<span class="n">Users</span>\<span class="n">user</span>\<span class="n">AppData</span>\<span class="n">Local</span>\<span class="n">Temp</span>\<span class="n">Temp1_zipname</span><span class="p">.</span><span class="nb">zip</span>\</code></pre></figure>
<p>But when exactly is the folder created? I previously assumed, wrongfully, that it would be created as soon as the user double-clicked the ZIP file in Explorer and viewed the directory listing within. However, that is not the case, and as long as the user stays within Explorer only, no folder is created.</p>
<p>As an aside, I’ve noted that this also applies to nested folders within the ZIP archive - the user can browse them without triggering the <code>Temp1</code> folder creation. However, if the ZIP archive contains <em>another</em> ZIP archive and the user opens that with Explorer, a Temp1 folder is created for the <em>parent</em> archive.</p>
<p>The most common way a user will trigger the Temp1 folder’s creation, however, is by opening a file within the archive. This will create the folder and populate it with <em>only the file the user has actually opened</em>. Subsequent files are copied to the folder as they are opened by the user, but it’s worth noting that the <code>Temp1</code> folder <em>will not contain all files from the archive unless the user opens them all</em>.</p>
<p><img src="/wp-content/uploads/2022/12/explorer-temporary-zip-folders_2.png" /></p>
<p>These files are persistent regardless of whether the user closes them or opens other files. Interestingly, if the user makes changes to a file within the archive, by default the Save As dialogue box shows them the <code>Temp1</code> folder, meaning an inattentive user might actually end up saving their edited copies to the same location (although this is unlikely, as they would have to give their file a different name).</p>
<p>Once the <code>Temp1</code> folder is created, it remains in place for as long as the user has <em>any</em> ZIP archive open in Explorer. This is simple enough when working with one archive - the user closes the ZIP file and the <code>Temp1</code> folder disappears. But if the user opens files within <code>a.zip</code> and <code>b.zip</code>, then closes <code>b.zip</code>, the <code>Temp1</code> folders for <em>both</em> archives will remain until <code>a.zip</code> is also closed in Explorer.</p>
<p>It’s also worth noting here that the folder deletion doesn’t care about the files inside - it’ll happen whether they’re still open or not. The only scenario I could find where specific files matter is if the user saves an edited copy of a file (with a different name, since the originals are read-only) inside the <code>Temp1</code> folder or moves a new file inside. If there’s a file that “doesn’t belong” at the time folder deletion is triggered, Explorer deletes the original files but leaves the directory and the “custom” files where they are.</p>
<p>Another short note, this time on naming: The temporary folders almost always start <code>Temp1</code>. In the previous example, there would be two folders named <code>Temp1_a.zip</code> and <code>Temp1_b.zip</code>. The only time the number increments is when dealing with two archives with <em>exactly the same name</em>. If the user opens files within two archives, both called <code>a.zip</code>, at the same time, Explorer creates <code>Temp1_a.zip</code> and <code>Temp2_a.zip</code>.</p>
<h3 id="summary">Summary</h3>
<p>Using the newfound knowledge from our experiments, we can draw up a rough guide to this behaviour and what your observations from log- and disk-based investigations might mean. As always, it’s best not to rely completely on this information and cross-reference with other artefacts wherever possible.</p>
<table>
<tbody>
<tr>
<td><strong>Observed</strong></td>
<td><strong>Inferred</strong></td>
</tr>
<tr>
<td><code>Temp1</code> folder created</td>
<td>User opened a file within the ZIP archive with Explorer</td>
</tr>
<tr>
<td>File inside <code>Temp1</code> folder</td>
<td>User opened this file from ZIP archive <em>OR</em> manually wrote it to folder</td>
</tr>
<tr>
<td><code>Temp1</code> folder present</td>
<td>User still has at least one ZIP archive open <em>OR</em> manually wrote a file inside</td>
</tr>
<tr>
<td><code>Temp1</code> folder deleted</td>
<td>User closed <em>all</em> open ZIP archives in Explorer</td>
</tr>
<tr>
<td><code>Temp1</code> folder not created</td>
<td>User did not open any files from ZIP archive in Explorer</td>
</tr>
<tr>
<td><code>Temp2</code> folder created</td>
<td>User opened files inside two identically named ZIP files with Explorer</td>
</tr>
</tbody>
</table></content><author><name>mattcasmith</name></author><summary type="html">If I was to describe how often malware is downloaded within ZIP archives, “common” would be a huge understatement. A key artefact in these investigations is the temporary directory Windows creates when a user opens an archive in Explorer, but I recently realised I’d never actually run a proper test to see when the folder is created, when it is not, and when it is deleted. So to clear that up, here’s a quick blog post. ZIP archives on easy mode There was a time when a ZIP archive was an indecipherable box that Windows couldn’t see inside. To access the files within, you’d have to download third-party software, like a trial version of WinZip that would incessantly prompt you to buy a licence whenever you downloaded something from the internet. That all came to an end with - I think, although I may be slightly mistaken - Windows XP, when Microsoft added native capabilities within Windows Explorer to zip and unzip files. This is now a near-seamless experience, with the user able to open a ZIP archive as though it were a normal folder, as seen below. But what’s going on behind the scenes when this happens, and what artefacts does it leave behind for forensicators and responders? With so much malware downloaded from the internet and attached to emails inside ZIP archives, knowing where to look can be a powerful tool in tracing user actions. The amazing disappearing folder This focus of this post is the temporary directory that Explorer creates, quietly unzipping parts of the archive on demand in the background so that it can present the user with that seamless interface on the front end. It’s located within the user’s AppData Temp folder, under a path like this: C:\Users\user\AppData\Local\Temp\Temp1_zipname.zip\ But when exactly is the folder created? I previously assumed, wrongfully, that it would be created as soon as the user double-clicked the ZIP file in Explorer and viewed the directory listing within. However, that is not the case, and as long as the user stays within Explorer only, no folder is created. As an aside, I’ve noted that this also applies to nested folders within the ZIP archive - the user can browse them without triggering the Temp1 folder creation. However, if the ZIP archive contains another ZIP archive and the user opens that with Explorer, a Temp1 folder is created for the parent archive. The most common way a user will trigger the Temp1 folder’s creation, however, is by opening a file within the archive. This will create the folder and populate it with only the file the user has actually opened. Subsequent files are copied to the folder as they are opened by the user, but it’s worth noting that the Temp1 folder will not contain all files from the archive unless the user opens them all. These files are persistent regardless of whether the user closes them or opens other files. Interestingly, if the user makes changes to a file within the archive, by default the Save As dialogue box shows them the Temp1 folder, meaning an inattentive user might actually end up saving their edited copies to the same location (although this is unlikely, as they would have to give their file a different name). Once the Temp1 folder is created, it remains in place for as long as the user has any ZIP archive open in Explorer. This is simple enough when working with one archive - the user closes the ZIP file and the Temp1 folder disappears. But if the user opens files within a.zip and b.zip, then closes b.zip, the Temp1 folders for both archives will remain until a.zip is also closed in Explorer. It’s also worth noting here that the folder deletion doesn’t care about the files inside - it’ll happen whether they’re still open or not. The only scenario I could find where specific files matter is if the user saves an edited copy of a file (with a different name, since the originals are read-only) inside the Temp1 folder or moves a new file inside. If there’s a file that “doesn’t belong” at the time folder deletion is triggered, Explorer deletes the original files but leaves the directory and the “custom” files where they are. Another short note, this time on naming: The temporary folders almost always start Temp1. In the previous example, there would be two folders named Temp1_a.zip and Temp1_b.zip. The only time the number increments is when dealing with two archives with exactly the same name. If the user opens files within two archives, both called a.zip, at the same time, Explorer creates Temp1_a.zip and Temp2_a.zip. Summary Using the newfound knowledge from our experiments, we can draw up a rough guide to this behaviour and what your observations from log- and disk-based investigations might mean. As always, it’s best not to rely completely on this information and cross-reference with other artefacts wherever possible. Observed Inferred Temp1 folder created User opened a file within the ZIP archive with Explorer File inside Temp1 folder User opened this file from ZIP archive OR manually wrote it to folder Temp1 folder present User still has at least one ZIP archive open OR manually wrote a file inside Temp1 folder deleted User closed all open ZIP archives in Explorer Temp1 folder not created User did not open any files from ZIP archive in Explorer Temp2 folder created User opened files inside two identically named ZIP files with Explorer</summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://mattcasmith.net/wp-content/uploads/2022/12/explorer-temporary-zip-folders_0.png" /><media:content medium="image" url="https://mattcasmith.net/wp-content/uploads/2022/12/explorer-temporary-zip-folders_0.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Parsing login sessions from the Windows event log with PowerShell</title><link href="https://mattcasmith.net/2022/12/03/parsing-login-sessions-windows-security-event-log-powershell" rel="alternate" type="text/html" title="Parsing login sessions from the Windows event log with PowerShell" /><published>2022-12-03T00:00:00+00:00</published><updated>2022-12-03T00:00:00+00:00</updated><id>https://mattcasmith.net/2022/12/03/parsing-logon-sessions-windows-security-event-log-powershell</id><content type="html" xml:base="https://mattcasmith.net/2022/12/03/parsing-login-sessions-windows-security-event-log-powershell"><p>Faced with a day at home recovering from my most recent COVID-19 booster vaccine, I realised I hadn’t written anything more than a few lines of PowerShell in a while and decided to spend some time working on something interesting. The idea occurred to me to try to correlate Windows login sessions from the Security event log, and the Windows Logon Session EVTX Parser script is the result.</p>
<h3 id="contents">Contents</h3>
<p>1. <a href="#introduction">Introduction</a><br />
a. <a href="#testing-and-limitations">Testing and limitations</a><br />
2. <a href="#configuration">Configuration</a><br />
a. <a href="#verbosity">Verbosity</a><br />
3. <a href="#reading-the-output">Reading the output</a><br />
4. <a href="#download">Download</a><br />
5. <a href="#future-development">Future development</a></p>
<h3 id="introduction">Introduction</h3>
<p>The Windows Logon Session EVTX Parser is a script that reads either a live or an exported Windows Security event log and produces a list of login sessions, including the login and logout times.</p>
<p>Granted, Windows event logs - and particularly these events - are likely among the first logs you would onboard to a security information and event management (SIEM) tool in a corporate environment, but there may be situations where data isn’t so accessible - for example, when investigating a host that has not been onboarded to the central repository, or a personal device involved in a forensics case.</p>
<p style="float: left; width: 90%; padding: 5px; padding-left: 15px; padding-right: 5%; margin: 30px 0 10px 0; font-style: italic; border-left: 5px solid red; background: #ff9999; color: #333;">This script is a learning/hobby project and some aspects may not follow best practices. While you're welcome to use it, you do so at your own risk. Make sure you take a backup of your files before trying it out, and don't go running it in production without proper checks.</p>
<p>The Windows Security event log generates events with the ID <a href="https://www.ultimatewindowssecurity.com/securitylog/encyclopedia/event.aspx?eventID=4624" target="_blank"><code>4624</code></a> whenever a user logs in. These events contain a multitude of useful information, from the timestamp and username to the login type (e.g. local or remote) and - where relevant - the remote IP address used. When the user logs out, some combination of the events <a href="https://www.ultimatewindowssecurity.com/securitylog/encyclopedia/event.aspx?eventID=4634" target="_blank"><code>4634</code></a> and <a href="https://www.ultimatewindowssecurity.com/securitylog/encyclopedia/event.aspx?eventID=4647" target="_blank"><code>4647</code></a> is generated (see the links for more information on what and when).</p>
<p>These events are linked together by a common field called <code>Logon ID</code>, which is a pseudo-unique value specific to a single login session. But there’s a catch - Windows doesn’t make it very easy to use this data because the value is buried in each event’s <code>Message</code> field. The Event Viewer doesn’t allow you to sort or filter based on it, and this gets crumpled into a single, hard-to-read cell in a CSV export.</p>
<p><img src="/wp-content/uploads/2022/12/windows_logon_session_evtx_parser_0.png" /></p>
<p>This is where the Windows Logon Session EVTX Parser comes in. Run the PowerShell script against a Windows Security event log and it will automatically find login and logout events, extract the relevant data from the <code>Message</code> field, correlate events to identify login sessions, and present the findings in a neat table.</p>
<h4 id="testing-and-limitations">Testing and limitations</h4>
<p>Testing of the Windows Logon Session EVTX Parser is an ongoing process. The script is still a little rough around the edges (hopefully you’ll give me a pass on that one, given the circumstances under which I wrote it) and currently does not include error handling, so you may have to do some manual investigation if anything goes wrong. However, I have tested all the options (live and exported event logs, different verbosity levels, CSV and terminal-only output) and everything seems to be in working order.</p>
<h3 id="configuration">Configuration</h3>
<p>The Windows Logon Session EVTX Parser is relatively easy to configure by directly editing the variables within the script itself. There are three primary settings that can be adjusted to your needs.</p>
<table>
<tbody>
<tr>
<td><strong>Variable</strong></td>
<td><strong>Example</strong></td>
<td><strong>Purpose</strong></td>
</tr>
<tr>
<td><code>$logFile</code></td>
<td>C:\temp\export.evtx</td>
<td>Either set to the path of an exported Windows Security event log EVTX file, or leave blank to use the live log (script must be run with administrator privileges if accessing live logs)</td>
</tr>
<tr>
<td><code>$verbosity</code></td>
<td>1</td>
<td>Must be set to one of the three verbosity settings (<a href="#verbosity">see below</a>)</td>
</tr>
<tr>
<td><code>$outputFile</code></td>
<td>C:\temp\logon_sessions.csv</td>
<td>Either set a path to write a CSV file of logon sessions to, or leave blank to print logon sessions to the terminal only</td>
</tr>
</tbody>
</table>
<p>Once the variables have been configured, the script can be run as a standard PowerShell script. If you wish to read the live event log, ensure that you are running the script from an administrator prompt.</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="p">.</span>\<span class="n">windows</span><span class="o">-</span><span class="n">logon</span><span class="o">-</span><span class="n">session</span><span class="o">-</span><span class="n">evtx</span><span class="o">-</span><span class="n">parser</span><span class="p">.</span><span class="n">ps1</span></code></pre></figure>
<h4 id="verbosity">Verbosity</h4>
<p>The <code>$verbosity</code> variable is perhaps the one that requires the most explanation. There are three possible levels of verbosity, and each has its own benefits and caveats, detailed below.</p>
<p>Setting <code>$verbosity</code> to <code>1</code> will exclude <code>SYSTEM</code> account logons and limit output to complete logon sessions (i.e. those with events for both the user logging in and logging out). This provides the most concise results, but will exclude any active sessions that do not yet have logout events, meaning it is best used in scenarios where its is known that the subject of the investigation is no longer active.</p>
<p>Setting <code>$verbosity</code> to <code>2</code> will still exclude <code>SYSTEM</code> account logons, but include a row in the output for each other login event, even if it does not have an associated logout event. This is likely the most useful verbosity level in most scenarios, as the results will include active sessions.</p>
<p>Setting <code>$verbosity</code> to <code>3</code> will include a row in the output for all logins, including those for the <code>SYSTEM</code> account and those with no associated logout event. This setting provides the most complete output, but includes a lot of noise from the <code>SYSTEM</code> account that you’ll probably need to filter out yourself.</p>
<h3 id="reading-the-output">Reading the output</h3>
<p>Assuming you’ve populated the <code>$outputFile</code> variable, the Windows Logon Session EVTX Parser will drop a CSV containing output of your selected verbosity at your chosen path. In the example below, the script returned complete login sessions only (<code>$verbosity = 1</code>), and I have filtered out service account events.</p>
<p><img src="/wp-content/uploads/2022/12/windows_logon_session_evtx_parser_1.png" /></p>
<p>The timestamp and event ID columns are self-explanatory, but you might be wondering why I decided to include both <code>LoginUserName</code> and <code>LogoutUserName</code>. This is because when the user logs in using a Microsoft account, the login event shows the email address and the logout event shows the short/local username, so this can therefore be a useful way of correlating the two to determine which are related.</p>
<p><code>LoginType</code> shows <a href="https://eventlogxp.com/blog/logon-type-what-does-it-mean/" target="_blank">the Windows logon type</a>, which helps to understand <em>how</em> the user logged on. In the example, I have highlighted a Type 3 (Network) logon to a shared folder from a virtual machine in orange, and a Type 4 (Batch) logon in green (this is actually the scheduled task for my backup utility, <a href="/2021/01/01/backutil-windows-backup-utility">Backutil</a>).</p>
<p>I’d recommend opening the output CSV in Excel and playing around with filters - particularly if you’ve run the script at the higher verbosity levels - but I’m sure most people reading this are already familiar.</p>
<h3 id="download">Download</h3>
<p>Use the link below to download the script. You’re free to run it as you wish - just please let me know if you encounter any bugs so I can work on fixing them in future releases!</p>
<p style="float: left; width: 90%; padding: 5px; padding-left: 15px; padding-right: 5%; margin: 30px 0 10px 0; font-style: italic; border-left: 5px solid red; background: #ff9999; color: #333;">This script is a learning/hobby project and some aspects may not follow best practices. While you're welcome to use it, you do so at your own risk. Make sure you take a backup of your files before trying it out, and don't go running it in production without proper checks.</p>
<table>
<tbody>
<tr>
<td><a href="https://github.com/mattcasmith/windows-logon-session-evtx-parser"><img src="/assets/images/download.png" style="width: 50px" /></a></td>
<td><a href="https://github.com/mattcasmith/windows-logon-session-evtx-parser">Download WLSEP v0.1</a><br />7KB, PS1</td>
<td>The PowerShell script is available to download directly from <a target="_blank" href="https://github.com/mattcasmith/windows-logon-session-evtx-parser">the Windows Logon Session EVTX Parser GitHub repository</a>.</td>
</tr>
</tbody>
</table>
<h3 id="future-development">Future development</h3>
<p>As I mentioned earlier, the script is still rough in some areas, so when I have the time I would like to make a few improvements to tidy things up and improve functionality. These include:</p>
<ul>
<li>
<p><strong>General housekeeping</strong> - The script was written in a single day while I was feeling rather groggy, so there are almost certainly omissions, inefficiencies, and so on that I’ll find on review.</p>
</li>
<li>
<p><strong>Error handling</strong> - The script will currently run to completion while throwing PowerShell errors if something goes wrong. I’d like to add some proper error handling to deal with that more gracefully.</p>
</li>
<li>
<p><strong>Additional functionality</strong> - I would consider adding the option to include further events associated with Windows login sessions (e.g. failed logins) to the results at some point in future, and possibly the capability for the configuration variables to be provided via the command line.</p>
</li>
</ul>
<p>Have you got more ideas for how the script could be improved? Or have you found bugs that I haven’t? Please <a href="mailto:[email protected]">send me an email</a> to let me know so I can add them to the development backlog.</p>
<p>If you’re interested in the project, check back regularly for new releases. I’ll also announce any updates on <a target="_blank" href="https://twitter.com/mattcasmith">my Twitter account</a>, and may add some form of banner to <a href="https://mattcasmith.net">my site’s homepage</a>.</p></content><author><name>mattcasmith</name></author><summary type="html">Faced with a day at home recovering from my most recent COVID-19 booster vaccine, I realised I hadn’t written anything more than a few lines of PowerShell in a while and decided to spend some time working on something interesting. The idea occurred to me to try to correlate Windows login sessions from the Security event log, and the Windows Logon Session EVTX Parser script is the result. Contents 1. Introduction a. Testing and limitations 2. Configuration a. Verbosity 3. Reading the output 4. Download 5. Future development Introduction The Windows Logon Session EVTX Parser is a script that reads either a live or an exported Windows Security event log and produces a list of login sessions, including the login and logout times. Granted, Windows event logs - and particularly these events - are likely among the first logs you would onboard to a security information and event management (SIEM) tool in a corporate environment, but there may be situations where data isn’t so accessible - for example, when investigating a host that has not been onboarded to the central repository, or a personal device involved in a forensics case. This script is a learning/hobby project and some aspects may not follow best practices. While you're welcome to use it, you do so at your own risk. Make sure you take a backup of your files before trying it out, and don't go running it in production without proper checks. The Windows Security event log generates events with the ID 4624 whenever a user logs in. These events contain a multitude of useful information, from the timestamp and username to the login type (e.g. local or remote) and - where relevant - the remote IP address used. When the user logs out, some combination of the events 4634 and 4647 is generated (see the links for more information on what and when). These events are linked together by a common field called Logon ID, which is a pseudo-unique value specific to a single login session. But there’s a catch - Windows doesn’t make it very easy to use this data because the value is buried in each event’s Message field. The Event Viewer doesn’t allow you to sort or filter based on it, and this gets crumpled into a single, hard-to-read cell in a CSV export. This is where the Windows Logon Session EVTX Parser comes in. Run the PowerShell script against a Windows Security event log and it will automatically find login and logout events, extract the relevant data from the Message field, correlate events to identify login sessions, and present the findings in a neat table. Testing and limitations Testing of the Windows Logon Session EVTX Parser is an ongoing process. The script is still a little rough around the edges (hopefully you’ll give me a pass on that one, given the circumstances under which I wrote it) and currently does not include error handling, so you may have to do some manual investigation if anything goes wrong. However, I have tested all the options (live and exported event logs, different verbosity levels, CSV and terminal-only output) and everything seems to be in working order. Configuration The Windows Logon Session EVTX Parser is relatively easy to configure by directly editing the variables within the script itself. There are three primary settings that can be adjusted to your needs. Variable Example Purpose $logFile C:\temp\export.evtx Either set to the path of an exported Windows Security event log EVTX file, or leave blank to use the live log (script must be run with administrator privileges if accessing live logs) $verbosity 1 Must be set to one of the three verbosity settings (see below) $outputFile C:\temp\logon_sessions.csv Either set a path to write a CSV file of logon sessions to, or leave blank to print logon sessions to the terminal only Once the variables have been configured, the script can be run as a standard PowerShell script. If you wish to read the live event log, ensure that you are running the script from an administrator prompt. .\windows-logon-session-evtx-parser.ps1 Verbosity The $verbosity variable is perhaps the one that requires the most explanation. There are three possible levels of verbosity, and each has its own benefits and caveats, detailed below. Setting $verbosity to 1 will exclude SYSTEM account logons and limit output to complete logon sessions (i.e. those with events for both the user logging in and logging out). This provides the most concise results, but will exclude any active sessions that do not yet have logout events, meaning it is best used in scenarios where its is known that the subject of the investigation is no longer active. Setting $verbosity to 2 will still exclude SYSTEM account logons, but include a row in the output for each other login event, even if it does not have an associated logout event. This is likely the most useful verbosity level in most scenarios, as the results will include active sessions. Setting $verbosity to 3 will include a row in the output for all logins, including those for the SYSTEM account and those with no associated logout event. This setting provides the most complete output, but includes a lot of noise from the SYSTEM account that you’ll probably need to filter out yourself. Reading the output Assuming you’ve populated the $outputFile variable, the Windows Logon Session EVTX Parser will drop a CSV containing output of your selected verbosity at your chosen path. In the example below, the script returned complete login sessions only ($verbosity = 1), and I have filtered out service account events. The timestamp and event ID columns are self-explanatory, but you might be wondering why I decided to include both LoginUserName and LogoutUserName. This is because when the user logs in using a Microsoft account, the login event shows the email address and the logout event shows the short/local username, so this can therefore be a useful way of correlating the two to determine which are related. LoginType shows the Windows logon type, which helps to understand how the user logged on. In the example, I have highlighted a Type 3 (Network) logon to a shared folder from a virtual machine in orange, and a Type 4 (Batch) logon in green (this is actually the scheduled task for my backup utility, Backutil). I’d recommend opening the output CSV in Excel and playing around with filters - particularly if you’ve run the script at the higher verbosity levels - but I’m sure most people reading this are already familiar. Download Use the link below to download the script. You’re free to run it as you wish - just please let me know if you encounter any bugs so I can work on fixing them in future releases! This script is a learning/hobby project and some aspects may not follow best practices. While you're welcome to use it, you do so at your own risk. Make sure you take a backup of your files before trying it out, and don't go running it in production without proper checks. Download WLSEP v0.17KB, PS1 The PowerShell script is available to download directly from the Windows Logon Session EVTX Parser GitHub repository. Future development As I mentioned earlier, the script is still rough in some areas, so when I have the time I would like to make a few improvements to tidy things up and improve functionality. These include: General housekeeping - The script was written in a single day while I was feeling rather groggy, so there are almost certainly omissions, inefficiencies, and so on that I’ll find on review. Error handling - The script will currently run to completion while throwing PowerShell errors if something goes wrong. I’d like to add some proper error handling to deal with that more gracefully. Additional functionality - I would consider adding the option to include further events associated with Windows login sessions (e.g. failed logins) to the results at some point in future, and possibly the capability for the configuration variables to be provided via the command line. Have you got more ideas for how the script could be improved? Or have you found bugs that I haven’t? Please send me an email to let me know so I can add them to the development backlog. If you’re interested in the project, check back regularly for new releases. I’ll also announce any updates on my Twitter account, and may add some form of banner to my site’s homepage.</summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://mattcasmith.net/wp-content/uploads/2022/12/windows_logon_session_evtx_parser_0.png" /><media:content medium="image" url="https://mattcasmith.net/wp-content/uploads/2022/12/windows_logon_session_evtx_parser_0.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Using Tkinter to build simple GUIs for Python apps</title><link href="https://mattcasmith.net/2022/08/21/using-tkinter-gui-python-apps" rel="alternate" type="text/html" title="Using Tkinter to build simple GUIs for Python apps" /><published>2022-08-21T01:00:00+01:00</published><updated>2022-08-21T01:00:00+01:00</updated><id>https://mattcasmith.net/2022/08/21/using-tkinter-gui-python-apps</id><content type="html" xml:base="https://mattcasmith.net/2022/08/21/using-tkinter-gui-python-apps"><p>I’ve written many Python scripts and apps in the past, including my Windows backup utility <a href="/2021/01/01/backutil-windows-backup-utility">Backutil</a>, which is probably the my most complex project to date. But I’ve always designed these to run in the background or on the command line, and haven’t ever tried to build an app with a graphical user interface (GUI).</p>
<p>I was recently studying for a few <a href="/category/cyber-security">cyber security</a> certifications and realised it would be useful to have an app for displaying flashcards. There are probably a million solutions for this online already, but I thought it would be a good opportunity to learn how to add a GUI to control some simple Python functions. So I took to Google, looked up some tutorials, and cobbled something together on a Saturday afternoon.</p>
<p>I’ll share the full code in future via GitHub and another blog post (there are still some more features I want to add), but for now I’ll focus on the GUI-related elements and how they connect to everything else.</p>
<h3 id="introducting-tkinter">Introducting Tkinter</h3>
<p><a href="https://docs.python.org/3/library/tkinter.html" target="_blank">Tkinter</a> is a Python library that allows you to work with the Tcl/Tk GUI toolkit to create applications that will work across Windows, macOS, and Linux. You can visit the link to review the full documentation, but I’ll cover some of the basics I learnt and how I used them for my flashcards app in this post.</p>
<p>As with any Python extension, the first step is to import the library.</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="kn">from</span> <span class="nn">tkinter</span> <span class="kn">import</span> <span class="o">*</span>
<span class="kn">from</span> <span class="nn">tkinter</span> <span class="kn">import</span> <span class="n">ttk</span></code></pre></figure>
<p>To understand what comes next, it’s probably better to see what the end result looks like first, so here’s the very simple GUI that we’ll be constructing in the following code segments for the flashcards app.</p>
<p><img src="/wp-content/uploads/2022/08/tkinter-python_0.png" /></p>
<p>As you can see, we have a main area that displays the content of the current flashcard, a button to flip the card to the other side, previous/next buttons and a counter to show the current position, and a button to reshuffle the deck into a new random order. There are also some keyboard shortcuts so the user doesn’t always need to navigate with the mouse. In the following sections, I’ll go over how each of these work.</p>
<h3 id="setting-up-the-window-and-grid">Setting up the window and grid</h3>
<p>We need a canvas before we can do any painting, and in this case the canvas is the application’s main window - also known as <code>root</code>. We can set this up with a few simple lines of code.</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="n">root</span> <span class="o">=</span> <span class="n">Tk</span><span class="p">()</span>
<span class="n">root</span><span class="p">.</span><span class="n">title</span><span class="p">(</span><span class="s">"Flashcards!"</span><span class="p">)</span>
<span class="n">root</span><span class="p">.</span><span class="n">geometry</span><span class="p">(</span><span class="s">"400x400"</span><span class="p">)</span></code></pre></figure>
<p>You can probably already glean the basics just by reading this, but with <code>title</code> we have set the window title to <code>Flashcards!</code>, and with <code>geometry</code> we have set the default window size to 400 by 400 pixels.</p>
<p>Before we can place individual elements in our window, we need to establish a grid, which we’ll call <code>mainframe</code>. The following lines create this frame and centre it with the compass-point <code>sticky</code> attributes.</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="n">mainframe</span> <span class="o">=</span> <span class="n">ttk</span><span class="p">.</span><span class="n">Frame</span><span class="p">(</span><span class="n">root</span><span class="p">,</span> <span class="n">width</span><span class="o">=</span><span class="mi">400</span><span class="p">,</span> <span class="n">height</span><span class="o">=</span><span class="mi">400</span><span class="p">)</span>
<span class="n">mainframe</span><span class="p">.</span><span class="n">grid</span><span class="p">(</span><span class="n">column</span><span class="o">=</span><span class="mi">0</span><span class="p">,</span> <span class="n">row</span><span class="o">=</span><span class="mi">0</span><span class="p">,</span> <span class="n">sticky</span><span class="o">=</span><span class="p">(</span><span class="n">N</span><span class="p">,</span> <span class="n">W</span><span class="p">,</span> <span class="n">E</span><span class="p">,</span> <span class="n">S</span><span class="p">))</span></code></pre></figure>
<p>With the grid itself set up, we can now apply attributes to the individual rows and columns. My basic GUI will require four rows, but only one of them needs any special configuration. I’m using <code>weight</code> and <code>minsize</code> on the second row to reserve plenty of space for the content of the cards.</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="n">mainframe</span><span class="p">.</span><span class="n">rowconfigure</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">weight</span><span class="o">=</span><span class="mi">8</span><span class="p">,</span> <span class="n">minsize</span><span class="o">=</span><span class="mi">295</span><span class="p">)</span></code></pre></figure>
<p>And that’s our grid set up and formatted! To help you to visualise where we’re adding each button and label in the next section, here’s roughly how the grid aligns to the finished GUI.</p>
<p><img src="/wp-content/uploads/2022/08/tkinter-python_1.png" /></p>
<p>Notice how the tweaks we made to the second row ensure there’s plenty of space in the middle in case the user provides lengthy content for their flashcards. You’ll probably also spot that the main card content and the Flip button don’t respect the column layout - we’ll see how to achieve this in a moment.</p>
<h3 id="buttons-labels-and-string-variables">Buttons, labels, and string variables</h3>
<p>Now we can get to placing the window contents - but we still have one more important job to do first. The text in the main card content section and the counter in the top right will need to change based on the user’s input, and to be able to update them we must establish them as <code>StringVar()</code> variables.</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="n">card_content</span> <span class="o">=</span> <span class="n">StringVar</span><span class="p">()</span>
<span class="n">card_number</span> <span class="o">=</span> <span class="n">StringVar</span><span class="p">()</span></code></pre></figure>
<p>Then we create each button and label individually and assign their attributes.</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="n">ttk</span><span class="p">.</span><span class="n">Button</span><span class="p">(</span><span class="n">mainframe</span><span class="p">,</span> <span class="n">text</span><span class="o">=</span><span class="s">"Flip"</span><span class="p">,</span> <span class="n">command</span><span class="o">=</span><span class="n">flip_card</span><span class="p">).</span><span class="n">grid</span><span class="p">(</span><span class="n">column</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">row</span><span class="o">=</span><span class="mi">2</span><span class="p">,</span> <span class="n">columnspan</span><span class="o">=</span><span class="mi">3</span><span class="p">,</span> <span class="n">sticky</span><span class="o">=</span><span class="p">(</span><span class="n">W</span><span class="p">,</span><span class="n">E</span><span class="p">,</span><span class="n">S</span><span class="p">))</span>
<span class="n">ttk</span><span class="p">.</span><span class="n">Button</span><span class="p">(</span><span class="n">mainframe</span><span class="p">,</span> <span class="n">text</span><span class="o">=</span><span class="s">"&lt;&lt;&lt;"</span><span class="p">,</span> <span class="n">command</span><span class="o">=</span><span class="n">prev_card</span><span class="p">,</span> <span class="n">width</span><span class="o">=</span><span class="mi">15</span><span class="p">).</span><span class="n">grid</span><span class="p">(</span><span class="n">column</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">row</span><span class="o">=</span><span class="mi">3</span><span class="p">,</span> <span class="n">sticky</span><span class="o">=</span><span class="p">(</span><span class="n">W</span><span class="p">,</span><span class="n">S</span><span class="p">))</span>
<span class="n">ttk</span><span class="p">.</span><span class="n">Button</span><span class="p">(</span><span class="n">mainframe</span><span class="p">,</span> <span class="n">text</span><span class="o">=</span><span class="s">"Shuffle"</span><span class="p">,</span> <span class="n">command</span><span class="o">=</span><span class="n">randomise_data</span><span class="p">,</span> <span class="n">width</span><span class="o">=</span><span class="mi">15</span><span class="p">).</span><span class="n">grid</span><span class="p">(</span><span class="n">column</span><span class="o">=</span><span class="mi">2</span><span class="p">,</span> <span class="n">row</span><span class="o">=</span><span class="mi">3</span><span class="p">,</span> <span class="n">sticky</span><span class="o">=</span><span class="n">S</span><span class="p">)</span>
<span class="n">ttk</span><span class="p">.</span><span class="n">Button</span><span class="p">(</span><span class="n">mainframe</span><span class="p">,</span> <span class="n">text</span><span class="o">=</span><span class="s">"&gt;&gt;&gt;"</span><span class="p">,</span> <span class="n">command</span><span class="o">=</span><span class="n">next_card</span><span class="p">,</span> <span class="n">width</span><span class="o">=</span><span class="mi">15</span><span class="p">).</span><span class="n">grid</span><span class="p">(</span><span class="n">column</span><span class="o">=</span><span class="mi">3</span><span class="p">,</span> <span class="n">row</span><span class="o">=</span><span class="mi">3</span><span class="p">,</span> <span class="n">sticky</span><span class="o">=</span><span class="p">(</span><span class="n">E</span><span class="p">,</span><span class="n">S</span><span class="p">))</span>
<span class="n">ttk</span><span class="p">.</span><span class="n">Label</span><span class="p">(</span><span class="n">mainframe</span><span class="p">,</span> <span class="n">font</span><span class="o">=</span><span class="s">"Verdana"</span><span class="p">,</span> <span class="n">textvariable</span><span class="o">=</span><span class="n">card_content</span><span class="p">,</span> <span class="n">wraplength</span><span class="o">=</span><span class="mi">390</span><span class="p">,</span> <span class="n">anchor</span><span class="o">=</span><span class="s">"center"</span><span class="p">,</span> <span class="n">width</span><span class="o">=</span><span class="mi">38</span><span class="p">).</span><span class="n">grid</span><span class="p">(</span><span class="n">column</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">row</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">columnspan</span><span class="o">=</span><span class="mi">3</span><span class="p">)</span>
<span class="n">ttk</span><span class="p">.</span><span class="n">Label</span><span class="p">(</span><span class="n">mainframe</span><span class="p">,</span> <span class="n">textvariable</span><span class="o">=</span><span class="n">card_number</span><span class="p">,</span> <span class="n">anchor</span><span class="o">=</span><span class="s">"e"</span><span class="p">,</span> <span class="n">width</span><span class="o">=</span><span class="mi">15</span><span class="p">).</span><span class="n">grid</span><span class="p">(</span><span class="n">column</span><span class="o">=</span><span class="mi">3</span><span class="p">,</span> <span class="n">row</span><span class="o">=</span><span class="mi">0</span><span class="p">)</span>
<span class="n">ttk</span><span class="p">.</span><span class="n">Label</span><span class="p">(</span><span class="n">mainframe</span><span class="p">,</span> <span class="n">text</span><span class="o">=</span><span class="s">"MattCASmith.net"</span><span class="p">,</span> <span class="n">anchor</span><span class="o">=</span><span class="s">"w"</span><span class="p">,</span> <span class="n">width</span><span class="o">=</span><span class="mi">17</span><span class="p">).</span><span class="n">grid</span><span class="p">(</span><span class="n">column</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">row</span><span class="o">=</span><span class="mi">0</span><span class="p">)</span></code></pre></figure>
<p>In the brackets after each <code>Button</code> or <code>Label</code> is created, we first state which element it should be placed within (<code>mainframe</code>) and then provide various configurations. There are three that are worth explaining:</p>
<ul>
<li>
<p><code>textvariable</code> - used to assign the two <code>StringVar()</code> variables we established to their respective labels. This is necessary for dynamic text. If the text is static, <code>text</code> can be used instead</p>
</li>
<li>
<p><code>anchor</code> - sets the alignment of the label text based on compass point values</p>
</li>
<li>
<p><code>command</code> - links buttons to the Python functions that must be executed when they are clicked. For example, clicking the Shuffle button will execute the <code>randomise_data</code> function</p>
</li>
</ul>
<p>In the brackets after <code>grid</code> we establish the placement of each element in the grid we set up earlier. The <code>column</code> and <code>row</code> numbers assign a cell to the element, and <code>sticky</code> sets its alignment within that cell. An interesting attribute here is <code>columnspan</code>, which allows an element to sit across multiple columns - as is the case here with the main content and the Shuffle button.</p>
<h3 id="keyboard-shortcuts">Keyboard shortcuts</h3>
<p>With our GUI mostly in place, we have an issue. When revising using a flashcards app, you’d want to be able to cycle quickly between the cards and flip them instantly to see whether you’re right or wrong. But at the moment our user needs to click on the corresponding buttons each time they want to change the view. So let’s fix that with some keyboard shortcuts so they can study at speed.</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="n">root</span><span class="p">.</span><span class="n">bind</span><span class="p">(</span><span class="s">'&lt;Left&gt;'</span><span class="p">,</span> <span class="n">prev_card</span><span class="p">)</span>
<span class="n">root</span><span class="p">.</span><span class="n">bind</span><span class="p">(</span><span class="s">'&lt;Right&gt;'</span><span class="p">,</span> <span class="n">next_card</span><span class="p">)</span>
<span class="n">root</span><span class="p">.</span><span class="n">bind</span><span class="p">(</span><span class="s">'&lt;Return&gt;'</span><span class="p">,</span> <span class="n">flip_card</span><span class="p">)</span></code></pre></figure>
<p>This should be fairly self-explanatory, but essentially what each line achieves is to bind a Python function (<code>prev_card</code>, <code>next_card</code>, <code>flip_card</code>) to a key (the left and right arrows and return, respectively), reducing the work required to cycle through cards and providing a much smoother user experience.</p>
<h3 id="drawing-the-window">Drawing the window</h3>
<p>There’s just a little more admin to do before our application will run properly. Firstly, let’s add some padding to each cell in the grid to space out our buttons and labels a little.</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="k">for</span> <span class="n">child</span> <span class="ow">in</span> <span class="n">mainframe</span><span class="p">.</span><span class="n">winfo_children</span><span class="p">():</span>
<span class="n">child</span><span class="p">.</span><span class="n">grid_configure</span><span class="p">(</span><span class="n">padx</span><span class="o">=</span><span class="mi">5</span><span class="p">,</span> <span class="n">pady</span><span class="o">=</span><span class="mi">5</span><span class="p">)</span></code></pre></figure>
<p>Finally, we use <code>mainloop()</code> to run our application and draw the window. When execution reaches this line, the window will be drawn and the app will essentially sit idle awaiting user input, so it’s important this we did the necessary legwork beforehand to make sure everything is in place and presentable.</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="n">root</span><span class="p">.</span><span class="n">mainloop</span><span class="p">()</span></code></pre></figure>
<p>And there you have it - this code (and the functions required to provide the actual functionality) should be all you need to create a very simple GUI application using Python. Flashcards! is currently at the minimum viable product stage - it’s perfectly usable as a study tool, but still rough round the edges. Once I’ve had a chance to spruce it up a bit I’ll be sure to share the full project code. Watch this space!</p></content><author><name>mattcasmith</name></author><summary type="html">I’ve written many Python scripts and apps in the past, including my Windows backup utility Backutil, which is probably the my most complex project to date. But I’ve always designed these to run in the background or on the command line, and haven’t ever tried to build an app with a graphical user interface (GUI). I was recently studying for a few cyber security certifications and realised it would be useful to have an app for displaying flashcards. There are probably a million solutions for this online already, but I thought it would be a good opportunity to learn how to add a GUI to control some simple Python functions. So I took to Google, looked up some tutorials, and cobbled something together on a Saturday afternoon. I’ll share the full code in future via GitHub and another blog post (there are still some more features I want to add), but for now I’ll focus on the GUI-related elements and how they connect to everything else. Introducting Tkinter Tkinter is a Python library that allows you to work with the Tcl/Tk GUI toolkit to create applications that will work across Windows, macOS, and Linux. You can visit the link to review the full documentation, but I’ll cover some of the basics I learnt and how I used them for my flashcards app in this post. As with any Python extension, the first step is to import the library. from tkinter import * from tkinter import ttk To understand what comes next, it’s probably better to see what the end result looks like first, so here’s the very simple GUI that we’ll be constructing in the following code segments for the flashcards app. As you can see, we have a main area that displays the content of the current flashcard, a button to flip the card to the other side, previous/next buttons and a counter to show the current position, and a button to reshuffle the deck into a new random order. There are also some keyboard shortcuts so the user doesn’t always need to navigate with the mouse. In the following sections, I’ll go over how each of these work. Setting up the window and grid We need a canvas before we can do any painting, and in this case the canvas is the application’s main window - also known as root. We can set this up with a few simple lines of code. root = Tk() root.title("Flashcards!") root.geometry("400x400") You can probably already glean the basics just by reading this, but with title we have set the window title to Flashcards!, and with geometry we have set the default window size to 400 by 400 pixels. Before we can place individual elements in our window, we need to establish a grid, which we’ll call mainframe. The following lines create this frame and centre it with the compass-point sticky attributes. mainframe = ttk.Frame(root, width=400, height=400) mainframe.grid(column=0, row=0, sticky=(N, W, E, S)) With the grid itself set up, we can now apply attributes to the individual rows and columns. My basic GUI will require four rows, but only one of them needs any special configuration. I’m using weight and minsize on the second row to reserve plenty of space for the content of the cards. mainframe.rowconfigure(1, weight=8, minsize=295) And that’s our grid set up and formatted! To help you to visualise where we’re adding each button and label in the next section, here’s roughly how the grid aligns to the finished GUI. Notice how the tweaks we made to the second row ensure there’s plenty of space in the middle in case the user provides lengthy content for their flashcards. You’ll probably also spot that the main card content and the Flip button don’t respect the column layout - we’ll see how to achieve this in a moment. Buttons, labels, and string variables Now we can get to placing the window contents - but we still have one more important job to do first. The text in the main card content section and the counter in the top right will need to change based on the user’s input, and to be able to update them we must establish them as StringVar() variables. card_content = StringVar() card_number = StringVar() Then we create each button and label individually and assign their attributes. ttk.Button(mainframe, text="Flip", command=flip_card).grid(column=1, row=2, columnspan=3, sticky=(W,E,S)) ttk.Button(mainframe, text="&lt;&lt;&lt;", command=prev_card, width=15).grid(column=1, row=3, sticky=(W,S)) ttk.Button(mainframe, text="Shuffle", command=randomise_data, width=15).grid(column=2, row=3, sticky=S) ttk.Button(mainframe, text="&gt;&gt;&gt;", command=next_card, width=15).grid(column=3, row=3, sticky=(E,S)) ttk.Label(mainframe, font="Verdana", textvariable=card_content, wraplength=390, anchor="center", width=38).grid(column=1, row=1, columnspan=3) ttk.Label(mainframe, textvariable=card_number, anchor="e", width=15).grid(column=3, row=0) ttk.Label(mainframe, text="MattCASmith.net", anchor="w", width=17).grid(column=1, row=0) In the brackets after each Button or Label is created, we first state which element it should be placed within (mainframe) and then provide various configurations. There are three that are worth explaining: textvariable - used to assign the two StringVar() variables we established to their respective labels. This is necessary for dynamic text. If the text is static, text can be used instead anchor - sets the alignment of the label text based on compass point values command - links buttons to the Python functions that must be executed when they are clicked. For example, clicking the Shuffle button will execute the randomise_data function In the brackets after grid we establish the placement of each element in the grid we set up earlier. The column and row numbers assign a cell to the element, and sticky sets its alignment within that cell. An interesting attribute here is columnspan, which allows an element to sit across multiple columns - as is the case here with the main content and the Shuffle button. Keyboard shortcuts With our GUI mostly in place, we have an issue. When revising using a flashcards app, you’d want to be able to cycle quickly between the cards and flip them instantly to see whether you’re right or wrong. But at the moment our user needs to click on the corresponding buttons each time they want to change the view. So let’s fix that with some keyboard shortcuts so they can study at speed. root.bind('&lt;Left&gt;', prev_card) root.bind('&lt;Right&gt;', next_card) root.bind('&lt;Return&gt;', flip_card) This should be fairly self-explanatory, but essentially what each line achieves is to bind a Python function (prev_card, next_card, flip_card) to a key (the left and right arrows and return, respectively), reducing the work required to cycle through cards and providing a much smoother user experience. Drawing the window There’s just a little more admin to do before our application will run properly. Firstly, let’s add some padding to each cell in the grid to space out our buttons and labels a little. for child in mainframe.winfo_children(): child.grid_configure(padx=5, pady=5) Finally, we use mainloop() to run our application and draw the window. When execution reaches this line, the window will be drawn and the app will essentially sit idle awaiting user input, so it’s important this we did the necessary legwork beforehand to make sure everything is in place and presentable. root.mainloop() And there you have it - this code (and the functions required to provide the actual functionality) should be all you need to create a very simple GUI application using Python. Flashcards! is currently at the minimum viable product stage - it’s perfectly usable as a study tool, but still rough round the edges. Once I’ve had a chance to spruce it up a bit I’ll be sure to share the full project code. Watch this space!</summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://mattcasmith.net/wp-content/uploads/2022/08/tkinter-python_0.png" /><media:content medium="image" url="https://mattcasmith.net/wp-content/uploads/2022/08/tkinter-python_0.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Linux .bash_history: Basics, behaviours, and forensics</title><link href="https://mattcasmith.net/2022/02/22/bash-history-basics-behaviours-forensics" rel="alternate" type="text/html" title="Linux .bash&#95;history&#58; Basics, behaviours, and forensics" /><published>2022-02-22T00:00:00+00:00</published><updated>2022-02-22T00:00:00+00:00</updated><id>https://mattcasmith.net/2022/02/22/bash-history-basics-behaviours-forensics</id><content type="html" xml:base="https://mattcasmith.net/2022/02/22/bash-history-basics-behaviours-forensics"><p>During any incident investigation on a Linux system, one of the most valuable things for responders and forensicators to establish is which commands were run. This is key to finding out what an attacker or malicious user was attempting to do, and what remediation activities are required.</p>
<p>The <code>.bash_history</code> file, located in each user’s home directory, is usually the investigator’s first stop for this information. This file contains a list of Bash commands recently run by the user and may appear relatively simple at first glance, but there are many ins and outs to its behaviours.</p>
<p>Most importantly, commands entered in a running Bash terminal are stored in memory and are only written to the file when the terminal is closed. As you can imagine, this means many caveats can be applied to the file’s contents, and in this post I aim to cover some of the most common scenarios, how they affect <code>.bash_history</code>, and alternatives when it does not contain the activity you are looking for.</p>
<h3 id="contents">Contents</h3>
<p>1. <a href="#bash_history-behaviours"><code>.bash_history</code> behaviours</a><br />
a. <a href="#first-a-note-on-the-histcontrol-variable">The <code>$HISTCONTROL</code> variable</a><br />
b. <a href="#the-history-command-outputs-a-combined-history-from-bash_history-and-memory">The <code>history</code> command</a><br />
c. <a href="#commands-are-written-to-bash_history-in-the-order-terminals-are-closed-not-the-order-they-are-run">Terminal windows and command order</a><br />
d. <a href="#commands-run-with-logical-and--appear-as-a-single-entry-in-bash_history">Logical AND (<code>&amp;&amp;</code>)</a><br />
e. <a href="#commands-run-within-scripts-are-not-reflected-in-bash_history">Commands within scripts</a><br />
f. <a href="#if-a-terminal-is-stopped-with-the-kill-command-commands-are-still-written-to-bash_history">The <code>kill</code> command</a><br />
g. <a href="#however-if-the--sigkill-switch-is-used-commands-are-not-written-to-bash_history">The <code>-SIGKILL</code> switch</a><br />
h. <a href="#ssh-sessions-behave-in-a-similar-way-to-standard-terminals">SSH sessions</a><br />
2. <a href="#finding-bash-commands-in-memory">Finding Bash commands in memory</a><br />
3. <a href="#further-reading">Further reading</a><br /></p>
<h3 id="bash_history-behaviours"><code>.bash_history</code> behaviours</h3>
<p>To test exactly when commands are and aren’t recorded to the user’s <code>.bash_history</code> file, I ran a series of tests covering common scenarios in which commands might be run. All of these tests were run on a clean installation of CentOS. Here’s what I found…</p>
<h4 id="first-a-note-on-the-histcontrol-variable">First, a note on the <code>$HISTCONTROL</code> variable…</h4>
<p>It is important to check the contents of the <code>$HISTCONTROL</code> variable on the subject system (after taking images and preserving evidence, so you’re not overwriting <code>.bash_history</code> with your own commands) as this has a potentially significant bearing on which commands will be written to the file.</p>
<blockquote>
<p>echo $HISTCONTROL</p>
</blockquote>
<p>The command above will return one of the following strings. The table below details which commands <code>.bash_history</code> will ignore when each of the possible values is present.</p>
<table>
<thead>
<tr>
<th style="text-align: left">$HISTCONTROL value</th>
<th style="text-align: left">Description</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: left">ignorespace</td>
<td style="text-align: left">Excludes commands with a preceding space</td>
</tr>
<tr>
<td style="text-align: left">ignoredups</td>
<td style="text-align: left">Excludes subsequent duplicate commands</td>
</tr>
<tr>
<td style="text-align: left">ignorespace:ignoredups</td>
<td style="text-align: left">Excludes both commands with a preceding space and subsequent duplicates</td>
</tr>
<tr>
<td style="text-align: left">ignoreboth</td>
<td style="text-align: left">Excludes both commands with a preceding space and subsequent duplicates</td>
</tr>
</tbody>
</table>
<p>Therefore if the system has one of these values in the <code>$HISTCONTROL</code> variable, there may be commands missing from <code>.bash_history</code>. If it is set to <code>ignorespace</code> and the attacker is aware, they could even just slip in a space before each of their commands and write nothing to the history file at all!</p>
<p>The CentOS system where I performed the testing for this post had <code>$HISTCONTROL</code> set to <code>ignoredups</code>. However, I also performed checks across other Linux distros I had available (namely Kali, Tsurugi, and Windows Subsystem for Linux) and it was set to <code>ignoreboth</code> for them all. So it’s important to check what you’re dealing with on the particular system you’re investigating.</p>
<p>It’s also trivial to change the contents of the <code>$HISTCONTROL</code> variable to exclude more commands from <code>.bash_history</code>, so watch out for evidence it has changed, which will look something like the following:</p>
<blockquote>
<p>export HISTCONTROL=ignoreboth</p>
</blockquote>
<h4 id="the-history-command-outputs-a-combined-history-from-bash_history-and-memory">The <code>history</code> command outputs a combined history from <code>.bash_history</code> and memory.</h4>
<p>On a live Linux system, you might run the <code>history</code> command in a terminal window to review recently executed commands. The output of this command combines both the contents of <code>.bash_history</code> and any commands held in memory from the current session.</p>
<p>In the example below, we can see that the test command <code>doesthiscommandshowinhistory?</code> is returned by the <code>history</code> command on the left, but has not yet been written to disk as shown on the right.</p>
<p><img src="/wp-content/uploads/2022/02/bash-history_0.png" /></p>
<p>Bear in mind that <code>history</code> will show commands from the memory of only the current Bash terminal - it won’t have access to those belonging to other processes. Since you won’t have access to the attacker’s terminal (unless the incident response team directly interrupted a hands-on-keyboard session), this means it won’t usually be much more useful than <code>.bash_history</code> itself.</p>
<h4 id="commands-are-written-to-bash_history-in-the-order-terminals-are-closed-not-the-order-they-are-run">Commands are written to <code>.bash_history</code> in the order terminals are closed, not the order they are run.</h4>
<p>For this test, I opened two Bash terminals and ran example commands containing text stating which terminal they belonged to and in which order they were run. As I closed the first terminal before the second, you can see that both of its commands appear first in <code>.bash_history</code>, even though some of the second terminal’s commands were run first. It is the order the Bash processes end that matters.</p>
<p><img src="/wp-content/uploads/2022/02/bash-history_1.png" /></p>
<p>As was discussed before, note that if we had run the <code>history</code> command in one of these terminals, it would only have returned its own commands because each process has its own space in memory.</p>
<h4 id="commands-run-with-logical-and--appear-as-a-single-entry-in-bash_history">Commands run with logical AND (<code>&amp;&amp;</code>) appear as a single entry in <code>.bash_history</code>.</h4>
<p>If we string several commands together using <code>&amp;&amp;</code> - a technique often used to save time (or in less innocent circumstances to avoid an attacker having to submit commands multiple times and risk attracting unwanted attention) - they will still only appear as one line in <code>.bash_history</code>.</p>
<p><img src="/wp-content/uploads/2022/02/bash-history_1.1.png" /></p>
<p>This is an important detail as investigators and forensicators, as it preserves the original context in which these commands were run. If they were run back-to-back in this manner, this invites the question: “Why?”</p>
<h4 id="commands-run-within-scripts-are-not-reflected-in-bash_history">Commands run within scripts are not reflected in <code>.bash_history</code>.</h4>
<p>To see whether commands in scripts are added to <code>.bash_history</code> or not, I created a script called <code>folder_test.sh</code> that would create a new folder, navigate into it, list the contents, navigate back up a level, and then delete the folder. You can see its contents in the left-hand window below.</p>
<p>On the right, I ran the script with <code>./folder_test.sh</code>. We know it executed successfully because the contents (i.e. nothing) are printed to the terminal. However, when we check the output of the <code>history</code> command we can see that although the command that <em>ran</em> the script was recorded, commands run <em>within</em> the script were not - an important distinction to remember when investigating Linux systems.</p>
<p><img src="/wp-content/uploads/2022/02/bash-history_1.2.png" /></p>
<p>This means that our attacker could potentially download or create a script named something innocuous like <code>file_cleanup.sh</code>, execute it, and we would be none the wiser as to what it did based on the contents of <code>.bash_history</code>. There would likely be artefacts elsewhere on the system to give us a clue as to what they were trying to do, but that’s a different topic for another day.</p>
<h4 id="if-a-terminal-is-stopped-with-the-kill-command-commands-are-still-written-to-bash_history">If a terminal is stopped with the <code>kill</code> command, commands are still written to <code>.bash_history</code>.</h4>
<p>This one was simple enough to test. I ran the command <code>doesthiscommandappearinhistory?</code> in a terminal, then opened a new terminal and killed it using its process ID, then checked <code>.bash_history</code>.</p>
<p>As you can see below, despite the Bash instance being killed, the command was still written to the log (the following <code>echo $$</code> command was the one I used to identify the process ID of that terminal).</p>
<p><img src="/wp-content/uploads/2022/02/bash-history_2.png" /></p>
<h4 id="however-if-the--sigkill-switch-is-used-commands-are-not-written-to-bash_history">However, if the <code>-SIGKILL</code> switch is used, commands are not written to <code>.bash_history</code>.</h4>
<p>More useful for red teamers - and something to bear in mind for blue teamers - is that <code>.bash_history</code> is not written if the terminal is killed with the <code>-SIGKILL</code> switch. Repeating the same experiment as above but with the additional switch meant no commands were written to disk.</p>
<p><img src="/wp-content/uploads/2022/02/bash-history_3.png" /></p>
<p>This is because by default <code>kill</code> sends the <code>SIGTERM</code> signal, which gracefully kills the process and allows Bash to write to <code>.bash_history</code> as it is closing down. <code>SIGKILL</code>, on the other hand, kills the process immediately before the commands can be written to the file.</p>
<h4 id="ssh-sessions-behave-in-a-similar-way-to-standard-terminals">SSH sessions behave in a similar way to standard terminals.</h4>
<p>If we run commands within an SSH session and then quit with <code>exit</code> or by closing the window, <code>.bash_history</code> is written in much the same way as it usually would be. The same also applies if we close the Command Prompt running the SSH session with Task Manager - presumably because Windows gives it time to tear down the connection in the background.</p>
<p><img src="/wp-content/uploads/2022/02/bash-history_4.png" /></p>
<p>Interestingly, if connecting via SSH from a Linux terminal, running a standard <code>kill</code> command against the terminal will not close it until the user has ended the SSH session themselves. Using the <code>-SIGKILL</code> switch ends the terminal process and SSH session immediately, but still writes the commands run during the session to the user’s <code>.bash_history</code> file.</p>
<p>This is likely because this is the work of <code>sshd</code>, which sees its client kill the connection but itself closes gracefully. If we use <code>kill</code> directly on <code>sshd</code> itself - even with the <code>-SIGKILL</code> switch - commands are still written to the file, which makes me wonder whether there is actually a way around this for SSH sessions.</p>
<p><img src="/wp-content/uploads/2022/02/bash-history_5.png" /></p>
<h3 id="finding-bash-commands-in-memory">Finding Bash commands in memory</h3>
<p>All the tests above showed when commands are and are not written to the on-disk <code>.bash_history</code> file. Now let’s see what we can do when an attacker has an active session (and therefore commands in memory) but either <code>.bash_history</code> has not been written yet or they have cleared its contents. This may also work for recently closed Bash terminal sessions that did not end gracefully.</p>
<p>The first thing we need to do is take a memory image of the Linux system - for this we can use a tool called <a href="https://github.com/504ensicsLabs/LiME" target="_blank">Linux Memory Extractor (LiME)</a>. In a live incident scenario there are mechanisms to push the image to external storage or across the network, but I ran a basic command to create it locally.</p>
<blockquote>
<p>sudo insmod lime-4.18.0-240.22.1.el8_3.x86_64.ko “path=/mem-img.mem format=lime”</p>
</blockquote>
<p>To analyse our new memory image, we’ll use <a href="https://www.volatilityfoundation.org/" target="_blank">Volatility</a>, which is currently considered the pinnacle of memory forensics toolkits. It’s not quite as simple to run Volatility against Linux memory images as it is for Windows images. I won’t go into the full process here, but you need to <a href="https://github.com/volatilityfoundation/volatility/wiki/Linux" target="_blank">create your own profile</a> for the specific Linux distro and kernel version from which the image was captured.</p>
<p>Once the profile is in place and ready to go, we can run Volatility’s <code>linux_bash</code> module with the following command to search for Bash commands held in memory.</p>
<blockquote>
<p>python2 vol.py -f mem-img.mem –profile=LinuxCentOSx64 linux_bash</p>
</blockquote>
<p>In the screenshot below, you can see a small extract from the results, are ordered first by the process ID of the Bash terminal to which the commands belonged and then by the time they were run. This includes commands that were run too recently to have been written to <code>.bash_history</code>, and towards the bottom you’ll even see the commands I ran to set up and run LiME.</p>
<p><img src="/wp-content/uploads/2022/02/bash-history_6.png" /></p>
<p>One small word of warning regarding timestamps… Everything from the <code>ls</code> command at <code>15:59:18</code> downwards appears to be correct, but you’ll probably notice that all the commands above that allegedly ran at exactly the same second, which is obviously not right. Further investigation is needed to work out why exactly that is, but it’s likely that there is some limit to the number of Bash command timestamps stored in memory, or that Volatility cannot always read them accurately.</p>
<h3 id="further-reading">Further reading</h3>
<p>The links below lead to pages that either inspired this post or provided useful information to compile it, including some more in-depth technical information on various features discussed above.</p>
<p>• <a href="https://www.gnu.org/software/bash/manual/html_node/Bash-History-Facilities.html" target="_blank">Bash history manual page</a> (gnu.org)<br />
• <a href="https://www.geeksforgeeks.org/histcontrol-command-in-linux-with-examples/" target="_blank">$HISTCONTROL command in Linux with examples</a> (geeksforgeeks.org)<br />
• <a href="https://linuxhandbook.com/sigterm-vs-sigkill/" target="_blank">SIGINT vs SIGKILL</a> (linuxhandbook.com)<br />
• <a href="https://github.com/volatilityfoundation/volatility/wiki/Linux" target="_blank">Volatility Linux documentation</a> (github.com)<br />
• <a href="https://www.crowdstrike.com/blog/how-to-extract-memory-information-to-spot-linux-malware/" target="_blank">How to extract memory information to spot Linux malware</a> (crowdstrike.com)</p>
<p><em>Updated 25/02/2022 to add section on the $HISTCONTROL variable.</em></p></content><author><name>mattcasmith</name></author><summary type="html">During any incident investigation on a Linux system, one of the most valuable things for responders and forensicators to establish is which commands were run. This is key to finding out what an attacker or malicious user was attempting to do, and what remediation activities are required. The .bash_history file, located in each user’s home directory, is usually the investigator’s first stop for this information. This file contains a list of Bash commands recently run by the user and may appear relatively simple at first glance, but there are many ins and outs to its behaviours. Most importantly, commands entered in a running Bash terminal are stored in memory and are only written to the file when the terminal is closed. As you can imagine, this means many caveats can be applied to the file’s contents, and in this post I aim to cover some of the most common scenarios, how they affect .bash_history, and alternatives when it does not contain the activity you are looking for. Contents 1. .bash_history behaviours a. The $HISTCONTROL variable b. The history command c. Terminal windows and command order d. Logical AND (&amp;&amp;) e. Commands within scripts f. The kill command g. The -SIGKILL switch h. SSH sessions 2. Finding Bash commands in memory 3. Further reading .bash_history behaviours To test exactly when commands are and aren’t recorded to the user’s .bash_history file, I ran a series of tests covering common scenarios in which commands might be run. All of these tests were run on a clean installation of CentOS. Here’s what I found… First, a note on the $HISTCONTROL variable… It is important to check the contents of the $HISTCONTROL variable on the subject system (after taking images and preserving evidence, so you’re not overwriting .bash_history with your own commands) as this has a potentially significant bearing on which commands will be written to the file. echo $HISTCONTROL The command above will return one of the following strings. The table below details which commands .bash_history will ignore when each of the possible values is present. $HISTCONTROL value Description ignorespace Excludes commands with a preceding space ignoredups Excludes subsequent duplicate commands ignorespace:ignoredups Excludes both commands with a preceding space and subsequent duplicates ignoreboth Excludes both commands with a preceding space and subsequent duplicates Therefore if the system has one of these values in the $HISTCONTROL variable, there may be commands missing from .bash_history. If it is set to ignorespace and the attacker is aware, they could even just slip in a space before each of their commands and write nothing to the history file at all! The CentOS system where I performed the testing for this post had $HISTCONTROL set to ignoredups. However, I also performed checks across other Linux distros I had available (namely Kali, Tsurugi, and Windows Subsystem for Linux) and it was set to ignoreboth for them all. So it’s important to check what you’re dealing with on the particular system you’re investigating. It’s also trivial to change the contents of the $HISTCONTROL variable to exclude more commands from .bash_history, so watch out for evidence it has changed, which will look something like the following: export HISTCONTROL=ignoreboth The history command outputs a combined history from .bash_history and memory. On a live Linux system, you might run the history command in a terminal window to review recently executed commands. The output of this command combines both the contents of .bash_history and any commands held in memory from the current session. In the example below, we can see that the test command doesthiscommandshowinhistory? is returned by the history command on the left, but has not yet been written to disk as shown on the right. Bear in mind that history will show commands from the memory of only the current Bash terminal - it won’t have access to those belonging to other processes. Since you won’t have access to the attacker’s terminal (unless the incident response team directly interrupted a hands-on-keyboard session), this means it won’t usually be much more useful than .bash_history itself. Commands are written to .bash_history in the order terminals are closed, not the order they are run. For this test, I opened two Bash terminals and ran example commands containing text stating which terminal they belonged to and in which order they were run. As I closed the first terminal before the second, you can see that both of its commands appear first in .bash_history, even though some of the second terminal’s commands were run first. It is the order the Bash processes end that matters. As was discussed before, note that if we had run the history command in one of these terminals, it would only have returned its own commands because each process has its own space in memory. Commands run with logical AND (&amp;&amp;) appear as a single entry in .bash_history. If we string several commands together using &amp;&amp; - a technique often used to save time (or in less innocent circumstances to avoid an attacker having to submit commands multiple times and risk attracting unwanted attention) - they will still only appear as one line in .bash_history. This is an important detail as investigators and forensicators, as it preserves the original context in which these commands were run. If they were run back-to-back in this manner, this invites the question: “Why?” Commands run within scripts are not reflected in .bash_history. To see whether commands in scripts are added to .bash_history or not, I created a script called folder_test.sh that would create a new folder, navigate into it, list the contents, navigate back up a level, and then delete the folder. You can see its contents in the left-hand window below. On the right, I ran the script with ./folder_test.sh. We know it executed successfully because the contents (i.e. nothing) are printed to the terminal. However, when we check the output of the history command we can see that although the command that ran the script was recorded, commands run within the script were not - an important distinction to remember when investigating Linux systems. This means that our attacker could potentially download or create a script named something innocuous like file_cleanup.sh, execute it, and we would be none the wiser as to what it did based on the contents of .bash_history. There would likely be artefacts elsewhere on the system to give us a clue as to what they were trying to do, but that’s a different topic for another day. If a terminal is stopped with the kill command, commands are still written to .bash_history. This one was simple enough to test. I ran the command doesthiscommandappearinhistory? in a terminal, then opened a new terminal and killed it using its process ID, then checked .bash_history. As you can see below, despite the Bash instance being killed, the command was still written to the log (the following echo $$ command was the one I used to identify the process ID of that terminal). However, if the -SIGKILL switch is used, commands are not written to .bash_history. More useful for red teamers - and something to bear in mind for blue teamers - is that .bash_history is not written if the terminal is killed with the -SIGKILL switch. Repeating the same experiment as above but with the additional switch meant no commands were written to disk. This is because by default kill sends the SIGTERM signal, which gracefully kills the process and allows Bash to write to .bash_history as it is closing down. SIGKILL, on the other hand, kills the process immediately before the commands can be written to the file. SSH sessions behave in a similar way to standard terminals. If we run commands within an SSH session and then quit with exit or by closing the window, .bash_history is written in much the same way as it usually would be. The same also applies if we close the Command Prompt running the SSH session with Task Manager - presumably because Windows gives it time to tear down the connection in the background. Interestingly, if connecting via SSH from a Linux terminal, running a standard kill command against the terminal will not close it until the user has ended the SSH session themselves. Using the -SIGKILL switch ends the terminal process and SSH session immediately, but still writes the commands run during the session to the user’s .bash_history file. This is likely because this is the work of sshd, which sees its client kill the connection but itself closes gracefully. If we use kill directly on sshd itself - even with the -SIGKILL switch - commands are still written to the file, which makes me wonder whether there is actually a way around this for SSH sessions. Finding Bash commands in memory All the tests above showed when commands are and are not written to the on-disk .bash_history file. Now let’s see what we can do when an attacker has an active session (and therefore commands in memory) but either .bash_history has not been written yet or they have cleared its contents. This may also work for recently closed Bash terminal sessions that did not end gracefully. The first thing we need to do is take a memory image of the Linux system - for this we can use a tool called Linux Memory Extractor (LiME). In a live incident scenario there are mechanisms to push the image to external storage or across the network, but I ran a basic command to create it locally. sudo insmod lime-4.18.0-240.22.1.el8_3.x86_64.ko “path=/mem-img.mem format=lime” To analyse our new memory image, we’ll use Volatility, which is currently considered the pinnacle of memory forensics toolkits. It’s not quite as simple to run Volatility against Linux memory images as it is for Windows images. I won’t go into the full process here, but you need to create your own profile for the specific Linux distro and kernel version from which the image was captured. Once the profile is in place and ready to go, we can run Volatility’s linux_bash module with the following command to search for Bash commands held in memory. python2 vol.py -f mem-img.mem –profile=LinuxCentOSx64 linux_bash In the screenshot below, you can see a small extract from the results, are ordered first by the process ID of the Bash terminal to which the commands belonged and then by the time they were run. This includes commands that were run too recently to have been written to .bash_history, and towards the bottom you’ll even see the commands I ran to set up and run LiME. One small word of warning regarding timestamps… Everything from the ls command at 15:59:18 downwards appears to be correct, but you’ll probably notice that all the commands above that allegedly ran at exactly the same second, which is obviously not right. Further investigation is needed to work out why exactly that is, but it’s likely that there is some limit to the number of Bash command timestamps stored in memory, or that Volatility cannot always read them accurately. Further reading The links below lead to pages that either inspired this post or provided useful information to compile it, including some more in-depth technical information on various features discussed above. • Bash history manual page (gnu.org) • $HISTCONTROL command in Linux with examples (geeksforgeeks.org) • SIGINT vs SIGKILL (linuxhandbook.com) • Volatility Linux documentation (github.com) • How to extract memory information to spot Linux malware (crowdstrike.com) Updated 25/02/2022 to add section on the $HISTCONTROL variable.</summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://mattcasmith.net/wp-content/uploads/2022/02/bash-history_1.png" /><media:content medium="image" url="https://mattcasmith.net/wp-content/uploads/2022/02/bash-history_1.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">SANS Holiday Hack Challenge 2021: Slot machine walkthrough</title><link href="https://mattcasmith.net/2022/01/08/sans-holiday-hack-2021-slot-machine" rel="alternate" type="text/html" title="SANS Holiday Hack Challenge 2021&#58; Slot machine walkthrough" /><published>2022-01-08T00:01:00+00:00</published><updated>2022-01-08T00:01:00+00:00</updated><id>https://mattcasmith.net/2022/01/08/sans-holiday-hack-2021-slot-machine</id><content type="html" xml:base="https://mattcasmith.net/2022/01/08/sans-holiday-hack-2021-slot-machine"><p>Here’s one more writeup from the <a href="https://www.sans.org/mlp/holiday-hack-challenge/" target="_blank">SANS Holiday Hack Challenge</a>! The slot machine hack was one of the showpiece challenges this year, so I thought I’d put together a quick blog post to guide you through the process of identifying and exploiting a vulnerability in the game.</p>
<h3 id="the-challenge">The challenge</h3>
<p>Our task is clear enough. We’re given a link to an online slot machine and the following request:</p>
<blockquote>
<p>Test the security of Jack Frost’s slot machines. What does the Jack Frost Tower casino security team threaten to do when your coin total exceeds 1,000? Submit the string in the server <code>data.response</code> element.</p>
</blockquote>
<p>Let’s follow the link and take a look at the slot machine. It’s fairly typical for such games (at least as per my understanding following <a href="/2019/08/26/im-back-def-con-inspired-hacking/">a short trip to Las Vegas for Black Hat and Def Con</a>). You can choose various options before placing a bet by hitting the Spin button. Your credit is updated based on the outcome.</p>
<p><img src="/wp-content/uploads/2022/01/sans-holiday-challenge-slots_1.png" /></p>
<p>Since our objective is to hit more than 1,000 credit, we <em>could</em> just play the slots for a long time to try to get lucky. But that would take forever. Instead, let’s take under the hood to see what we can hack…</p>
<h3 id="observing-requests">Observing requests</h3>
<p>Like any web application, the slot machine must be communicating with a server behind the scenes to make playing the game possible. By using the Burp Suite proxy’s intercept function, we can hold requests and responses between the web browser and the server to give us time to review their contents and see what’s going on when we place a bet. Here’s what is sent when we hit the Spin button…</p>
<p><img src="/wp-content/uploads/2022/01/sans-holiday-challenge-slots_2.png" /></p>
<p>This looks like a pretty standard web request. We can see that this is a <code>POST</code> request, which means that the client is transmitting information to the server (<code>slots.jackfrosttower.com</code>). We can see that the request provides some cookie information, details about the page we’re on and our user agent, and so on. This is all pretty normal for this kind of request. But what about that last line?</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="n">betamount</span><span class="o">=</span><span class="mi">1</span><span class="o">&amp;</span><span class="n">numline</span><span class="o">=</span><span class="mi">20</span><span class="o">&amp;</span><span class="n">cpl</span><span class="o">=</span><span class="mf">0.1</span></code></pre></figure>
<p>Comparing these variable names to the GUI, we can ascertain that this is our browser telling the server how much we’re betting, the number of lines we’re betting on, and the cost per line. Interesting. By tweaking these values, we might be able to:</p>
<ul>
<li>Change the bet amount to a bigger stake than we can actually afford</li>
<li>Bet on more lines than actually exist on the slot machine</li>
<li>Adjust the cost per line to alter the slot machine’s financial calculations</li>
</ul>
<p>From here, it’s a matter of trial and error. Attempting the first two potential exploits returns a <code>Server Error</code> response. That leaves only one option - but how can we use the <code>cpl</code> variable to our advantage?</p>
<h3 id="crafting-a-malicious-request">Crafting a malicious request</h3>
<p>So here’s the plan: We’re going to set <code>cpl</code> to a negative value to confuse the game if this input isn’t validated. For example, if we set it to <code>-100</code> and there are 20 lines to play, that spin would cost us -2,000 credits to play and therefore actually <em>increase</em> our credit by 2,000 (and any winnings from the spin).</p>
<p>To do this, we hit the Spin button on the slot machine’s GUI. Burp Suite intercepts the request. We must very quickly change the <code>cpl</code> value to <code>-100</code> and hit the Forward button before the spin times out.</p>
<p><img src="/wp-content/uploads/2022/01/sans-holiday-challenge-slots_3.png" /></p>
<p>It looks like the server doesn’t check that <code>cpl</code> is set to a valid value before executing the spin. All goes to plan and when the play is complete our credit is now in the thousands! Mission complete - almost.</p>
<p>Our task was actually to find a value in the JSON response once our credit exceeds 1,000. To do this, we can take a look for the relevant request and response on Burp Suite’s HTTP history page. Sure enough, in the <code>data.response</code> field, we can see the following message from casino security:</p>
<p><img src="/wp-content/uploads/2022/01/sans-holiday-challenge-slots_4.png" /></p>
<p>And that’s our flag: <code>I'm going to have some bouncer trolls bounce you right out of this casino!</code> Hopefully we have enough time to submit it and take the credit for our hacking first.</p>
<p>If you found this walkthrough interesting, please consider <a href="https://twitter.com/mattcasmith" target="_blank">following me on Twitter</a>, and don’t forget to sign up for the next SANS Holiday Hack Challenge circa December 2022!</p></content><author><name>mattcasmith</name></author><summary type="html">Here’s one more writeup from the SANS Holiday Hack Challenge! The slot machine hack was one of the showpiece challenges this year, so I thought I’d put together a quick blog post to guide you through the process of identifying and exploiting a vulnerability in the game. The challenge Our task is clear enough. We’re given a link to an online slot machine and the following request: Test the security of Jack Frost’s slot machines. What does the Jack Frost Tower casino security team threaten to do when your coin total exceeds 1,000? Submit the string in the server data.response element. Let’s follow the link and take a look at the slot machine. It’s fairly typical for such games (at least as per my understanding following a short trip to Las Vegas for Black Hat and Def Con). You can choose various options before placing a bet by hitting the Spin button. Your credit is updated based on the outcome. Since our objective is to hit more than 1,000 credit, we could just play the slots for a long time to try to get lucky. But that would take forever. Instead, let’s take under the hood to see what we can hack… Observing requests Like any web application, the slot machine must be communicating with a server behind the scenes to make playing the game possible. By using the Burp Suite proxy’s intercept function, we can hold requests and responses between the web browser and the server to give us time to review their contents and see what’s going on when we place a bet. Here’s what is sent when we hit the Spin button… This looks like a pretty standard web request. We can see that this is a POST request, which means that the client is transmitting information to the server (slots.jackfrosttower.com). We can see that the request provides some cookie information, details about the page we’re on and our user agent, and so on. This is all pretty normal for this kind of request. But what about that last line? betamount=1&amp;numline=20&amp;cpl=0.1 Comparing these variable names to the GUI, we can ascertain that this is our browser telling the server how much we’re betting, the number of lines we’re betting on, and the cost per line. Interesting. By tweaking these values, we might be able to: Change the bet amount to a bigger stake than we can actually afford Bet on more lines than actually exist on the slot machine Adjust the cost per line to alter the slot machine’s financial calculations From here, it’s a matter of trial and error. Attempting the first two potential exploits returns a Server Error response. That leaves only one option - but how can we use the cpl variable to our advantage? Crafting a malicious request So here’s the plan: We’re going to set cpl to a negative value to confuse the game if this input isn’t validated. For example, if we set it to -100 and there are 20 lines to play, that spin would cost us -2,000 credits to play and therefore actually increase our credit by 2,000 (and any winnings from the spin). To do this, we hit the Spin button on the slot machine’s GUI. Burp Suite intercepts the request. We must very quickly change the cpl value to -100 and hit the Forward button before the spin times out. It looks like the server doesn’t check that cpl is set to a valid value before executing the spin. All goes to plan and when the play is complete our credit is now in the thousands! Mission complete - almost. Our task was actually to find a value in the JSON response once our credit exceeds 1,000. To do this, we can take a look for the relevant request and response on Burp Suite’s HTTP history page. Sure enough, in the data.response field, we can see the following message from casino security: And that’s our flag: I'm going to have some bouncer trolls bounce you right out of this casino! Hopefully we have enough time to submit it and take the credit for our hacking first. If you found this walkthrough interesting, please consider following me on Twitter, and don’t forget to sign up for the next SANS Holiday Hack Challenge circa December 2022!</summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://mattcasmith.net/wp-content/uploads/2022/01/sans-holiday-challenge-slots_1.png" /><media:content medium="image" url="https://mattcasmith.net/wp-content/uploads/2022/01/sans-holiday-challenge-slots_1.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry></feed>