[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)
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值