## Description
```Category: CryptoDifficulty: EasyAuthor: LiveOverflowFirst Blood: Cybernatural
Prove that you can win 0xf times against the house. Then go to Vegas.
Challenge Files: aaslr.zip```
## Analysis
What happens when we just run `aaslr`?
```kali@kali:~/Downloads/allesctf/aslr1$ ./aaslrWelcome To The Actual-ASLR (AASLR) Demo  1. throw dice  2. create entry  3. read entry  4. take a guess
Select menu Item:1[>] Threw dice: 3Welcome To The Actual-ASLR (AASLR) Demo  1. throw dice  2. create entry  3. read entry  4. take a guess
Select menu Item:4(1/16) guess next dice roll:1(2/16) guess next dice roll:2(3/16) guess next dice roll:3(4/16) guess next dice roll:4(5/16) guess next dice roll:^C```
You can throw dice, guess upcoming dice rolls, and create / read entries. This was a 2 part problem, and I only worked on the first part that was centered around the dice rolls.
Decompile `aaslr` with Ghidra and walk through the functions.
```cvoid main(void)
{  int iVar1;  uint uVar2;  code **ppcVar3;  long in_FS_OFFSET;  char local_118 [264];  undefined8 local_10;   local_10 = *(undefined8 *)(in_FS_OFFSET + 0x28);  init_buffering();  init_signal();  ppcVar3 = (code **)aaslr_malloc(8);  *ppcVar3 = throw_dice;  ppcVar3[1] = create_entry;  ppcVar3[2] = read_entry;  ppcVar3[3] = take_guess;  ppcVar3[4] = error_case;  ENTRY = aaslr_malloc(0x7f8);  do {    while( true ) {      while( true ) {        print_menu();        memset(local_118,0,0xff);        read_input(0,local_118,0xff);        iVar1 = strcmp(local_118,"1");        if (iVar1 != 0) break;        uVar2 = (**ppcVar3)();        printf("[>] Threw dice: %d\n",(ulong)uVar2);      }      iVar1 = strcmp(local_118,"2");      if (iVar1 != 0) break;      uVar2 = (*ppcVar3[1])();      printf("[>] Created new entry at index %d\n",(ulong)uVar2);    }    iVar1 = strcmp(local_118,"3");    if (iVar1 == 0) {      (*ppcVar3[2])();    }    else {      iVar1 = strcmp(local_118,"4");      if (iVar1 == 0) {        (*ppcVar3[3])();      }      else {        (*ppcVar3[4])(local_118);      }    }  } while( true );}```
`take_guess` will give you a flag if you get all the guesses correct:
```cvoid take_guess(void)
{  int iVar1;  int iVar2;  long in_FS_OFFSET;  int local_128;  int local_124;  char local_118 [264];  long local_10;   local_10 = *(long *)(in_FS_OFFSET + 0x28);  local_128 = 0;  local_124 = 0;  while (local_124 < 0xf) {    printf("(%d/16) guess next dice roll:\n",(ulong)(local_124 + 1));    memset(local_118,0,0xff);    read_input(0,local_118,0xff);    iVar1 = atoi(local_118);    iVar2 = throw_dice();    if (iVar1 == iVar2) {      local_128 = local_128 + 1;    }    local_124 = local_124 + 1;  }  if (local_128 == 0xf) {    puts("[>] CORRECT! You should go to Vegas.");    system("cat flag2");  }  else {    puts("[!] WRONG!");  }  if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {                    /* WARNING: Subroutine does not return */    __stack_chk_fail();  }  return;}```
`throw_dice` is pretty self explanatory:
```culong throw_dice(void)
{  ulong uVar1;   uVar1 = ranval();  return (ulong)((int)uVar1 + (int)(uVar1 / 6) * -6 + 1);}```
`ranval` does some mathy stuff:
```clong ranval(void)
{  long lVar1;  long lVar2;   lVar1 = RANCTX._0_8_ - (RANCTX._8_8_ >> 5 | RANCTX._8_8_ << 0x1b);  RANCTX._0_8_ = RANCTX._8_8_ ^ (RANCTX._16_8_ >> 0xf | RANCTX._16_8_ << 0x11);  lVar2 = RANCTX._0_8_ + lVar1;  RANCTX._8_8_ = RANCTX._16_8_ + RANCTX._24_8_;  RANCTX._16_8_ = RANCTX._24_8_ + lVar1;  RANCTX._24_8_ = lVar2;  return lVar2;}```
How does this thing get seeded though?
`aaslr_malloc` calls `init_heap`:
```clong aaslr_malloc(long param_1)
{  long lVar1;  ulong uVar2;   if (HEAP == 0) {    init_heap();  }  lVar1 = HEAP;  if (HEAP != 0) {    uVar2 = ranval();    return uVar2 % (0x10000U - param_1) + lVar1;  }                    /* WARNING: Subroutine does not return */  _exit(0);}```
And `init_heap` gets the time in seconds since the epoch and passes the value into `raninit`:
```cvoid init_heap(void)
{  time_t tVar1;   HEAP = mmap((void *)0x0,0x10000,3,0x22,-1,0);  tVar1 = time((time_t *)0x0);  raninit(tVar1);  return;}```
And this is where it gets seeded:
```cvoid raninit(undefined8 param_1)
{  ulong local_18;   RANCTX._0_8_ = 0xf1ea5eed;  local_18 = 0;  RANCTX._8_8_ = param_1;  RANCTX._16_8_ = param_1;  RANCTX._24_8_ = param_1;  while (local_18 < 0x14) {    ranval(RANCTX);    local_18 = local_18 + 1;  }  return;}```
## Solution
Since the granularity is in seconds, we can start a local process at the same time as we create a connection to the remote host, and if they start within the same second (assuming no major clock skew) then the seed will be the same and we can feed the dice rolls from the local process into the guesses for the remote process.
```python#!/usr/bin/python3from pwn import *
#context.log_level='DEBUG'p = process(['./aaslr'])#r = process(['./aaslr'])r = process(['ncat', '--ssl', '7b000000f298cf5bdd7b82ed.challenges.broker2.allesctf.net', '1337'])
p.recvuntil('Select menu Item:\n')r.recvuntil('Select menu Item:\n')r.sendline('4')
for i in range(15):    p.sendline('1')    p.recvuntil('[>] Threw dice: ')    roll = p.recvline().rstrip()    print(roll)    #r.recvuntil('guess next dice roll:\n')    r.sendline(roll)
r.recvuntil('[>] CORRECT! You should go to Vegas.\n')flag = r.recvline()print(flag)```
Run it and we get the flag:
```kali@kali:~/Downloads/allesctf/aslr1$ ./solve.py [+] Starting local process './aaslr': pid 13523[+] Starting local process '/usr/bin/ncat': pid 13525b'2'b'4'b'1'b'1'b'6'b'5'b'2'b'5'b'2'b'2'b'2'b'2'b'6'b'4'b'3'b'ALLES{ILLEGAL_CARD_COUNTING!_BANNED}\n'```