CTF-做题记录

Write-up:

几道比较基础的ctf题,题目来源:西安电子科技大学网安竞赛暑期培训

Reverse:

1.Click:

题目描述:题目来源: 2023 52pojie 新春 第3题

解题方法:题目附件下载下来之后是一个apk的文件,把它安装到手机上,是一个程序,进去界面就是一直Click点击,当点击次数等于999时就可以得到flag

这里我们把它放进jadx-gui里面,去查看一下他的源码,找到他的main函数来分析它的源码

截取里面关键的两段源码并且将注释加到他的源码上,便于分析:

/**
 * Lambda表达式,用于处理MainActivity的onCreate方法中的特定逻辑。
 *
 * @param this$0 MainActivity的实例,表示当前上下文。
 * @param key 用于显示文本的TextView。
 * @param view 视图对象,可能是某个用户界面元素。
 */
public static final void m19onCreate$lambda0(MainActivity this$0, TextView key, View view) {
    // 检查参数不为空,如果为空,则抛出NullPointerException。
    Intrinsics.checkNotNullParameter(this$0, "this$0");
    Intrinsics.checkNotNullParameter(key, "$key");

    // 获取MainActivity的实例,并调用jntm方法。
    MainActivity mainActivity = this$0;
    this$0.jntm(mainActivity);

    // 将TextView的文本设置为MainActivity实例的num值的字符串表示。
    key.setText(String.valueOf(this$0.num));

    // 检查MainActivity实例的check方法的返回值是否为999。
    if (this$0.check() == 999) {
        // 如果是,显示Toast提示信息。
        Toast.makeText(mainActivity, "快去论坛领CB吧!", 1).show();

        // 将TextView的文本设置为使用decrypt方法解密的特定字符串。
        key.setText(this$0.decrypt("hnci}|jwfclkczkppkcpmwckng\u007f", 2));
    }
}

/**
 * 解密方法,将加密的文本还原为原始文本。
 *
 * @param encryptTxt 要解密的文本。
 * @param i 解密时使用的偏移量。
 * @return 解密后的原始文本。
 */
public final String decrypt(String encryptTxt, int i) {
    // 检查参数不为空,如果为空,则抛出NullPointerException。
    Intrinsics.checkNotNullParameter(encryptTxt, "encryptTxt");

    // 将加密文本转换为字符数组。
    char[] charArray = encryptTxt.toCharArray();
    // 检查字符数组不为空,如果为空,则抛出NullPointerException。
    Intrinsics.checkNotNullExpressionValue(charArray, "this as java.lang.String).toCharArray()");

    // 创建StringBuilder对象,用于存储解密后的文本。
    StringBuilder sb = new StringBuilder();
    for (char c : charArray) {
        // 将每个字符减去偏移量,并追加到StringBuilder中。
        sb.append((char) (c - i));
    }

    // 将StringBuilder转换为字符串并返回。
    String sb2 = sb.toString();
    // 检查字符串不为空,如果为空,则抛出NullPointerException。
    Intrinsics.checkNotNullExpressionValue(sb2, "with(StringBuilder()) {\n…     toString()\n        }");
    return sb2;
}

这里的逻辑很简单,就是当我们点击到check==999时:

 // 将TextView的文本设置为使用decrypt方法解密的特定字符串。
        key.setText(this$0.decrypt("hnci}|jwfclkczkppkcpmwckng\u007f", 2));

然后在解密函数里面,它将传下来的字符串减去2个偏移量,就可以返回解密后的原始文本即flag

现在我们编写python脚本就可以得到flag:

str = 'hnci}|jwfclkczkppkcpmwckng\u007f'

flag = ''
for i in range(len(str)):
    flag += chr(ord(str[i])-2)
print(flag)

运行得到flag:flag{zhudajiaxinniankuaile}

2.T1:

题目描述:来自buu

解题方法:题目附件下载下来之后,发现是一个exe文件

把它放进exeinfope.exe里面查看一下它的属性:

这里显示它是一个64位的无壳的exe,现在把它放进IDA里面,F5反汇编一下,查看它的伪代码:

#include <stdio.h>
#include <string.h>
void sub_1400111D1(const char *message);
void sub_14001128F(const char *format, ...);
int __cdecl main_0(int argc, const char **argv, const char **envp)
{
  char *v3; 
  __int64 i; 
  size_t v5; 
  char v7; 
  int j; 
  char Str1[224]; 
  __int64 v10; 

  v3 = &v7;
  for (i = 82i64; i; --i)
  {
    *(_DWORD *)v3 = -858993460;
    v3 += 4;
  }

  // 遍历字符串 Str2 中的字符,将所有 'o' 替换为 '0'
  for (j = 0; ; ++j)
  {
    v10 = j;
    if (j > j_strlen(Str2))
      break;
    if (Str2[j] == 111)
      Str2[j] = 48;
  }

  // 输出提示信息,要求用户输入标志
  sub_1400111D1("input the flag:");

  // 接受用户输入的字符串,最多 20 个字符
  sub_14001128F("%20s", Str1);

  // 获取字符串 Str2 的长度
  v5 = j_strlen(Str2);

  // 比较用户输入的字符串和 Str2 是否相同
  if (!strncmp(Str1, Str2, v5))
    sub_1400111D1("this is the right flag!\n");
  else
    sub_1400111D1("wrong flag\n");

  return 0;
}

这里的逻辑很清楚,我们的输入为Str1,然后将我们的输入Str1和Str2进行比较,相同就输出这是正确的flag,所以这里我们的思路很清楚,Str2里面的值就是我们的flag,但是这里有一个条件,在上面对Str2进行了转换,遍历字符串 Str2 中的字符,将所有 'o' 替换为 '0'

我们先看Str2里面的值:

flag{hello~~}

转换一下,将所有 'o' 替换为 '0',就是我们正确的flag:

flag{hell0~~}

3.T2:

题目描述:easy algorithm

解题方法:题目附件下载下来之后,发现是一个exe文件

把它放进exeinfope.exe里面查看一下它的属性:

这里显示它是一个64位的无壳的exe,现在把它放进IDA里面,F5反汇编一下,查看它的伪代码:

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char v4[32]; // [rsp+20h] [rbp-40h]
  __int64 v5[2]; // [rsp+40h] [rbp-20h] BYREF
  int v6; // [rsp+50h] [rbp-10h]
  int i; // [rsp+5Ch] [rbp-4h]

  _main(argc, argv, envp);
  v5[0] = 0i64;
  v5[1] = 0i64;
  v6 = 0;
  v4[0] = 110;
  v4[1] = -108;
  v4[2] = 97;
  v4[3] = 111;
  v4[4] = -101;
  v4[5] = 114;
  v4[6] = 109;
  v4[7] = -98;
  v4[8] = 109;
  v4[9] = -110;
  v4[10] = -109;
  v4[11] = 109;
  v4[12] = 103;
  v4[13] = 81;
  v4[14] = -109;
  v4[15] = 103;
  v4[16] = 110;
  v4[17] = -99;
  v4[18] = -106;
  v4[19] = 0x85;
  puts("Hi!CTFer!please input your flag:");
  scanf("%20s", v5);  //这里是我们的输入v5
  for ( i = 0; i <= 19; ++i )
  {    //对我们的输入v5进行一系列操作,遍历v5将所有字符的偏移量加上20,再和0x14进行异或
    *((_BYTE *)v5 + i) += 20;
    *((_BYTE *)v5 + i) ^= 0x14u;
  }
  for ( i = 0; i <= 19; ++i )
  {                              
    if ( *((_BYTE *)v5 + i) != v4[i] ) //这里将处理过后的v5和v4进行比较,相同就输出你是对的
    {
      puts("qwq~ something worng!");
      exit(0);
    }
  }
  puts("you are right!");
  return 0;
}

分析到这里,我们的思路就很清晰,我们的输入v5进行一系列的操作之后和v4进行比较,那我们就逆向思维过来,我们shift+E提取出v4的值,然后将v4的值进行逆向回去,就可以得到我们的输入的正确的flag:

编写脚本:

str = [0x6E,0x94,0x61,0x6F,0x9B,0x72,0x6D,0x9E,0x6D,0x92,0x93,0x6D,0x67,0x51,
       0x93,0x67,0x6E,0x9D,0x96,0x85]

flag = ''
for i in range(len(str)):
    flag += chr((str[i]^0x14)-20)
print(flag)

得到flag:flag{Reverse_1s_fun}

4.简单注册机:

题目描述:来自buuctf re区

解题方法:附件下载下来发现是一个apk文件

这里我们把它放进jadx-gui里面,去查看一下他的源码,找到他的main函数来分析它的源码:

这里我们分析它生成注册码的这段核心代码:

            public void onClick(View v) {
                int flag = 1;
                String xx = editview.getText().toString();
                if (xx.length() != 32 || xx.charAt(31) != 'a' || xx.charAt(1) != 'b' || (xx.charAt(0) + xx.charAt(2)) - 48 != 56) {
                    flag = 0;
                }
                if (flag == 1) {
                    char[] x = "dd2940c04462b4dd7c450528835cca15".toCharArray();
                    x[2] = (char) ((x[2] + x[3]) - 50);
                    x[4] = (char) ((x[2] + x[5]) - 48);
                    x[30] = (char) ((x[31] + x[9]) - 48);
                    x[14] = (char) ((x[27] + x[28]) - 97);
                    for (int i = 0; i < 16; i++) {
                        char a = x[31 - i];
                        x[31 - i] = x[i];
                        x[i] = a;
                    }
                    String bbb = String.valueOf(x);
                    textview.setText("flag{" + bbb + "}");
                    return;
                }
                textview.setText("输入注册码错误");
            }
        });
    }

这里注册码的生成算法很清晰明了,现在我们顺着它的算法来编写一个python脚本来生成注册码及我们flag:

def manipulate_string():
    x = list("dd2940c04462b4dd7c450528835cca15")

    x[2] = chr(ord(x[2]) + ord(x[3]) - 50)
    x[4] = chr(ord(x[2]) + ord(x[5]) - 48)
    x[30] = chr(ord(x[31]) + ord(x[9]) - 48)
    x[14] = chr(ord(x[27]) + ord(x[28]) - 97)

    for i in range(16):
        a = x[31 - i]
        x[31 - i] = x[i]
        x[i] = a

    bbb = ''.join(x)
    return "flag{" + bbb + "}"


result = manipulate_string()
print(result)

得到注册码(flag):

flag{59acc538825054c7de4b26440c0999dd}

Pwn:

1.ez_ret:

题目描述:最简单的栈溢出

解题方法:附件下载下来是一个ELF文件,在linux里面checksec一下,查看一下它开启了哪些保护:

这里发现是一个64位的ELF文件,而且它的保护机制就开启了NX保护,其他的栈保护,地址随机化都没有开启,说明难度不大

我们把它放进IDA里面,分析一下它的反汇编代码:

这里我们发现一个栈溢出的漏洞:

这里的buf定义的大小是128位,但是read函数可读取0x200位,说明可能存在栈溢出漏洞

查看一下它的栈空间:

这里满足栈溢出漏洞的条件,可以发现buf就只有0x80字节的大小,输入超过0x80个字节以后,你就会溢出,你会覆盖返回地址。所以如果你先填充字节,然后修改掉返回地址就可以调转到你想要跳转的地方。

存在溢出条件,接下来就是要找后门函数地址了。(为什么/bin/sh在前面,因为64位先传参数,其次找函数的地址就是找system函数以及它的参数)

发现system(“/bin/sh")函数地址,只要我们能控制程序返回到0x40059A,就可以得到系统的shell了

现在我们就编写exp:

from pwn import *

p = remote('192.168.0.113',57522)

payload = b'a'*(0x80+8)+p64(0x40059A)

p.sendline(payload)
p.interactive()

进行攻击就可以拿到它的shell来获取flag:

flag{ret2text_so_ez}

Crypto:

1.Caesar_Cipher:

题目描述:简单的凯撒加密

解题方法:题目下载下来得到一串编码

yetz{rhn_atox_extKg_ahP_Mh_wxvKriM_Max_vtxltK_vbiaxK_n9byqb9b!CZhyFC3}

放进凯撒解密中枚举一下就可以得到flag:

flag{you_have_leaRn_hoW_To_decRypT_The_caesaR_cipheR_u9ifxi9i!JGofMJ3}

2.ez_operation:

题目描述:扣1恢复flag,hint:你听说过xor吗

解题方法:附件下载下来得到一串编码:

V11QVkpIXmRuVVhCUl5HVENUVW5FWVRuV11QdhBufGJdfHZYYgJ7dV0IAUNoYlhee1JYV2IHB1hddXxnAnVSWGhXEF1hTA==

还没有思路,待解

3.ez_Rsa:

题目描述:difficulty: moe (dbt version)

解题方法:将题目附件下载下来之后,查看它的加密源码:

from Crypto.Util.number import *

p = getPrime(1024)
q = getPrime(1024)

with open("flag.txt","r") as f:
    flag = f.read().strip().encode()

m = bytes_to_long(flag)
n = p * q
e1 = 0x114514
e2 = 19198101

c1 = pow(m,e1,n)
c2 = pow(m,e2,n)
print(c1) # 11243012629649045184257669052855431188426619870857851755340357869962705251529124481978887404032231030311954715759443327334649701826838249497627422162723529080187074305359133800382818757112524981361511272720347671484879744564726772477690953799909596578674760833743895999872098342933776047006084882941079225529272699832145082693784520673369701708282010624156314804803765740561724885216012240803579559575196257659976536322512513863415544971227511774584604634702835852394154465564382214825073417512177133660720176845005417197321877972124248232888049633785306052049643162715767407206695527961925841675825471402231322773302
print(c2) # 13908838404665714525469872111009048142275774516574817012612303360182929186976326418845853832220462041187678521684854412144556576290260102114111686997756058286551332862979054266959478356427295041005818617274634710655405047308232376569501906325318765681777539302233123140590830201288731581246481265419559455962387111544368410947264170181057169070947670779846942329795518253622039238288373931184324649505146866669830975544230836334469713021340098330110095926531355748694050179572044376525329427421760700641904670475366316321600442973851120434246304778738771046543700225744237743249815624721738402920323747409352615724417
print(n) # 19988202562091079609089308705614482303944446998739929311122975838789540619557621657313791322188360240322479546623228234498987716237291487911715123662141533437516467656819081087822927677844909809145858009790123612288587521757088942729679608789475060418234226845941528147374848444623701986375339203883428139471506212500029711951340175962051544266932829183904033759349848307144227177119955562243057882815461375557809109473763829649791991995312572272515672264651244155391860835180863961730834954937594945747086499622858587453845632609942765805190071473098082158621841666644919898891980108801709943506274123300448153887451

分析发现是RSA中的两组e和两组c的情况,编写脚本一把梭哈:

import gmpy2
from Crypto.Util.number import long_to_bytes

#p =
#q =
#n = p * q
n = 19988202562091079609089308705614482303944446998739929311122975838789540619557621657313791322188360240322479546623228234498987716237291487911715123662141533437516467656819081087822927677844909809145858009790123612288587521757088942729679608789475060418234226845941528147374848444623701986375339203883428139471506212500029711951340175962051544266932829183904033759349848307144227177119955562243057882815461375557809109473763829649791991995312572272515672264651244155391860835180863961730834954937594945747086499622858587453845632609942765805190071473098082158621841666644919898891980108801709943506274123300448153887451
e1 = 0x114514
c1 = 11243012629649045184257669052855431188426619870857851755340357869962705251529124481978887404032231030311954715759443327334649701826838249497627422162723529080187074305359133800382818757112524981361511272720347671484879744564726772477690953799909596578674760833743895999872098342933776047006084882941079225529272699832145082693784520673369701708282010624156314804803765740561724885216012240803579559575196257659976536322512513863415544971227511774584604634702835852394154465564382214825073417512177133660720176845005417197321877972124248232888049633785306052049643162715767407206695527961925841675825471402231322773302
e2 = 19198101
c2 = 13908838404665714525469872111009048142275774516574817012612303360182929186976326418845853832220462041187678521684854412144556576290260102114111686997756058286551332862979054266959478356427295041005818617274634710655405047308232376569501906325318765681777539302233123140590830201288731581246481265419559455962387111544368410947264170181057169070947670779846942329795518253622039238288373931184324649505146866669830975544230836334469713021340098330110095926531355748694050179572044376525329427421760700641904670475366316321600442973851120434246304778738771046543700225744237743249815624721738402920323747409352615724417
s = gmpy2.gcdext(e1, e2)
s1 = s[1]
s2 = s[2]
if s1 < 0:
    s1 = - s1
    c1 = gmpy2.invert(c1, n)
elif s2 < 0:
    s2 = - s2
    c2 = gmpy2.invert(c2, n)
m = pow(c1, s1, n) * pow(c2, s2, n) % n

print(long_to_bytes(m))

得到flag:flag{deebaTo_is_God!_6!M6oSMuliPPcA36DxPlMluS3lJfGcArGuJV3l06DcYlM!xM}

Misc:

1.GIF:

题目描述:附件下载下来后发现是一张.gif的动图

解题方法:仔细观察发现这张动图里面有黑色的字母在闪烁,猜测它的flag信息可能就隐藏在里面:

编写python脚本来将它逐帧分离:

from PIL import Image
im = Image.open(r'D:\桌面/flag.gif')   #读入一个GIF文件
try:
    im.save(r'D:\桌面\逐帧图片\{}.png'.format(im.tell()))
    while True:
        im.seek(im.tell()+1)
        im.save(r'D:\桌面\逐帧图片\{}.png'.format(im.tell()))
except:
    print('处理结束')

分离后可以看到有201张图片:

查看了每一张图片的属性,发现他们的宽度都为2像素,这里就可以想到要将这些图片进行排序组合成一张宽度为201x2像素的一张大图,编写python脚本:

from PIL import Image

# 设置大图的宽度和高度
big_image_width = 2 * 201  # 每张照片宽度为2像素,共201张照片
big_image_height = 600

# 创建一张空白的大图
big_image = Image.new('RGB', (big_image_width, big_image_height))

# 将所有照片按顺序拼接到大图上
for i in range(201):
    # 读取每张照片
    filename = f"D:\桌面\逐帧图片/{i}.png"  # 更改为你的照片文件路径和命名规则
    img = Image.open(filename)

    # 将照片缩放到与大图一样的高度并粘贴到大图上,依次排列
    img_resized = img.resize((2, 600))
    big_image.paste(img_resized, (i * 2, 0))  # 每张照片的宽度为2像素,依次排列

# 保存拼接完成的大图
big_image.save("D:\桌面\逐帧图片/big_image_sorted.png")  # 更改为你想保存的大图路径和文件名

最后就组合成一张大图:

这里我们就可以得到它的flag

2.其他隐写:

题目描述:压缩包

解题方法:题目附件下载下来之后,是一个压缩包,解压时发现有密码

把它放进010_editor里面,查看一下它的十六进制:

这里我们发现是压缩包的伪加密,它的标志位是偶数说明没有加密,但是flag.txt哪里的标志位是奇数,说明是伪加密,修改一下:

保存后解压一下就得到一个flag.txt,打开就得到我们的flag:

flag{I_am_here}

3.图片隐写1:

题目描述:png隐写

解题方法:把图片下载下来

把图片放进010_editor里面,查看一下它的十六进制:

在底部发现了一个隐藏压缩包的,压缩包里面有个flag.txt文档:

现在我们的思路很清晰,用foremost把图片分离一下:

分离出一个压缩包,解压后得到一个flag.txt文档,打开后就得到flag:

flag{Yoimiya_is_so_cute}

4.文档隐写:

题目描述:根据课上所学

解题方法:题目附件下载下来后发现是一个doc的Word文件

猜测是word隐写,打开后全选更改颜色,显示隐藏信息,也没有发现flag

把它放进010_editor里面查看一下它的十六进制:

发现里面有好多PK压缩包文件,将doc的word文档后缀改成.zip,然后解压出许多文件

最后在里面找到了flag:

flag: flag{1t_w@s_d1sc0vere6!}

5.流量分析:

题目描述:结合课上所教,不只是考察wireshark的用法
题目来源于buuctf
公安机关近期截获到某网络犯罪团伙在线交流的数据包,但无法分析出具体的交流内容,聪明的你能帮公安机关找到线索吗?

解题方法:题目附件下载下来是一个pcapng文件

用wireshark打开分析一下里面的数据流量包:

我们追踪它的http流,发现了一段类似base64的编码,但是它的编码特别多,猜测一下是base64转图片

将这段编码放进base64转图片里面,就得到了一张图片:

这里我们就得到了flag

6.编码:

题目描述:编码

解题方法:附件下载下来得到一串编码:

RzVBVE1OUlhHVTNEQ04yQ0dRWlRLUlJXR1kzREdOUlZHNDRUS1JSV0lVM0RTTktHR1pBVE1OUlhHVTNUR05LR0c0M0RPTkpXSVEzVFNOMkU=

一眼看是base64编码,进行base64解码得到:

G5ATMNRXGU3DCN2CGQZTKRRWGY3DGNRVG44TKRRWIU3DSNKGGZATMNRXGU3TGNKGG43DONJWIQ3TSN2E

这里全是大写字母和数字组成,可能是base32编码,进行base32解码得到:

7A6675617B435F666365795F6E695F6A6675735F76756D797D

得到一串由数字和字母组成,但是字母的范围是A-F,说明是hex编码,进行hex解码:

zfua{C_fcey_ni_jfus_vumy}

一眼看是凯撒编码,进行凯撒枚举解码得到flag:

flag{I_like_to_play_base}

总结:

还是特别基础的几道ctf题,对新手很友好

posted @   张伟文  阅读(179)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
点击右上角即可分享
微信分享提示