-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathChypass_pro.java
2102 lines (1774 loc) · 91.2 KB
/
Chypass_pro.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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
package burp;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.concurrent.atomic.AtomicReference;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableCellRenderer;
import org.json.JSONObject;
import org.json.JSONArray;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
public class BurpExtender implements IBurpExtender, ITab, IContextMenuFactory {
private IBurpExtenderCallbacks callbacks;
private IExtensionHelpers helpers;
private JPanel mainPanel;
private JSplitPane splitPane;
private JButton sendToAIButton;
private JButton stopButton;
private JTextArea requestTextArea;
private JTextArea responseTextArea;
private JTextArea aiResponseTextArea;
private JTextArea logTextArea;
private JTable historyTable;
private HistoryTableModel historyTableModel;
private List<RequestResponseAIPair> history;
private boolean isRunning = false;
private String apiKey = "";
private JTextField apiKeyField;
private PrintWriter stdout;
private PrintWriter stderr;
private IHttpService currentHttpService;
private JComboBox<String> apiProviderSelector;
private JTextField modelNameField;
private JLabel modelNameLabel;
private JPanel modelConfigPanel;
private CardLayout modelConfigLayout;
private String originalTemplate = null;
private List<JSONObject> messageHistory = new ArrayList<>();
private static final int MAX_HISTORY = 3; // 保持滑动窗口的3轮对话历史
private static final int MAX_TOKEN_LIMIT = 8000;
private Map<Integer, Integer> testPairIdToHistoryIndex = new HashMap<>(); // 新增:跟踪测试对ID到历史索引的映射
private static String INITIAL_PROMPT = "你是一个渗透测试专家,专注于XSS漏洞的检测和绕过WAF。我会给你完整的请求包和相应包。你需要通过以下步骤分析请求和响应:\n" +
"而且回答牢记这条一定牢记并遵守:每轮生成的xss的payload要不一样,不能和上一轮生成的一样,就算上一轮返回响应为200,响应信息中有绕过了waf关键词,下一轮也要生成处不同的payload,此外生成的payload不要存在空格,可以使用其他替代字符利用编码、拼接等等方式替代空格,另外每轮的payload要差别大一点,组合思路跳脱一点,而且更重要的一点,确保每次生成的xss的payload是正常的,可以使用的,不能随便瞎生成,此外payload中一定尽可能少的直接出现eval、xss、alert、console.log、javascript等这些xss拦截高危词,要使用要搭配一些分割组合等这种方式,不要直接完整的将检测关键词带入到生成的payload中一定要参考下面的的waf中xss的拦截正则的规则再就加上你自己的思路,最重要的一点【我将提供一个包含特殊占位符 <xss> 的HTTP请求模板,你的任务是只为该占位符生成一个可以绕过waf的XSS payload(条件如上),而不返回完整的HTTP请求,记住回复内容尽快短,此外一定注意生成的payload中不要保留 <xss> 占位符,只要生成的payload,并用最简单的话描述当前payload成功绕过waf,不要输出你的思考和判断依据,并用下面这个格式输出下一轮的payload:下一轮payload:xxxxx【注意:payload前后不要有多余的<、>、`等这类多余字符,payload原格式中的不要去除,此外一定牢记payload部分请不要包含换行符,所有内容都在同一行内,结束后附上标记 --payload-end--,例如:下一轮payload: <img/src=x%20onerror=window['a'+'lert'](1) --payload-end--】\n"+
"1. 判断是否存在XSS漏洞:\n" +
" - 检查输入点是否被正确处理/转义\n" +
" - 观察响应中是否包含未经过滤的注入代码\n" +
" - 查看JavaScript是否能成功执行(例如alert/console.log出现)\n" +
" - 检查HTML结构是否被破坏或修改\n\n" +
"2. 判断是否被WAF拦截:\n" +
" - 响应状态码是否为403/406/429等异常状态\n" +
" - 响应内容中是否包含拦截提示、安全警告或错误页面\n" +
" - 检查是否返回空白页面或与预期完全不同的内容\n" +
" - 注入的代码是否被完全删除或明显修改\n\n" +
"3. 请你参考下列这些xss的WAF绕过技术,但不只限于这些方法:\n" +
"而且生成的xss绕过payload尽可能的短一些,有利于限制长度的xss插入场景,此外尽可能的使用多种绕过思路进行组合绕过\n"+
" 此外这是一些常见waf的xss拦截的正则匹配规则,你生成的payload首先要想办法不触发这些waf对xss匹配的正则:<(iframe|script|body|img|layer|div|meta|style|base|object|input)\n" +
"(onmouseover|onerror|onload)=\n" +
"<a\\s+[^>]*href\\s*=\\s*['\"]?javascript:.*\n"+
"对符合进行编码,字符串分割与注释插入,将关键字拆分\n"+
"函数调用混淆和事件属性与非传统冷漠标签的代替等\n"+
"数据协议和流方式绕过、非常规事件触发绕过、逻辑级绕过、字符串拆解\n" +
"叠加、隐形iframe、数学表达式、字符串逆序运算组合等\n" +
"使用上述方法但不只限于上述思路,你也可以有自己的思路去构造多层混淆的Payload\n" +
"但根据目标WAF的特性,动态调整payload构造策略,最大限度规避正则和黑名单过滤\n\n" +
"一、WAF存在性检测阶段\n\n" +
"基础特征分析:\n" +
"检查HTTP响应头中是否包含场景WAF的标识\n" +
"分析响应状态码异常(如403/406/501非预期状态)\n" +
"计算请求响应时间差(>2秒可能触发行为分析)\n\n" +
"二、基础注入验证\n\n" +
"无害探针注入可以参考下面的,也按照你自己的思路来,随机生成,直接使用绕过思路生成即可,但生成的payload要符合xss注入的格式,不能瞎生成,每轮只生成一个就可以,参考但一定不只限于这个payload:\n" +
"<script>alert(document.cookie)</script>\n"+
"响应特征比对:\n" +
"原始payload留存率分析(完整度≥80%?)\n" +
"特殊字符存活统计(<>\"'/等字符过滤情况)\n" +
"上下文语义完整性检测(是否破坏原有HTML结构)\n\n" +
"三、XSS成功验证标准\n" +
"首先必须满足条件1,就是下面的状态码正常,此外再条件2条件才能判定XSS成功:\n\n" +
"1. 状态码正常:\n" +
" - HTTP响应为403、400、40X这类状态码,则表示" +
"请求被WAF阻断,AI直接判定为输出为被WAF拦截即可,不再进行分析\n\n" +
"2. 满足以下三项中的任意两项:\n" +
" - DOM变更检测:document.documentElement.innerHTML中包含有效payload\n" +
" - 新建script节点可见于DOM树\n" +
" - 错误诱导:生成非常规JS错误(如未定义函数故意调用)\n\n" +
"注意!!!!!!不需要过多回复,只需要给我结论,是否xss成功,是否被waf拦截,然后按照上述格式要求给出一个完整的修改后的HTTP请求,请确保请求格式完全正确,我会使用你提供的请求进行测试,并将结果返回给你继续分析,如果没有收到相应包,那就直接判断被拦截,xss失败";
/**
* 估算文本的token数,简单认为每4个字符为1个token
*/
private int estimateTokenCount(String text) {
if (text == null) return 0;
return text.length() / 4; // 简单估算:每4个字符算1 token
}
/**
* 计算消息历史中所有消息的总token数
*/
private int calculateTotalTokens(List<JSONObject> messages) {
int total = 0;
for (JSONObject msg : messages) {
total += estimateTokenCount(msg.optString("content", ""));
}
return total;
}
/**
* 对内容进行截断处理,防止过长。maxLength可根据需求调整
*/
private String truncateContent(String content, int maxLength) {
if (content == null) return "";
return content.length() > maxLength ? content.substring(0, maxLength) + "...(truncated)" : content;
}
/**
* 裁剪消息历史,始终保留系统消息,并从最新的消息开始往前累加,
* 直到总token数达到预设阈值
*/
private void trimMessageHistory() {
// 始终保留第一个系统提示
List<JSONObject> newHistory = new ArrayList<>();
if (!messageHistory.isEmpty()) {
newHistory.add(messageHistory.get(0));
}
int totalTokens = newHistory.isEmpty() ? 0 : estimateTokenCount(newHistory.get(0).optString("content", ""));
// 从最新的消息开始向前累加,直到达到token限制
for (int i = messageHistory.size() - 1; i >= 1; i--) {
JSONObject msg = messageHistory.get(i);
int msgTokens = estimateTokenCount(msg.optString("content", ""));
if (totalTokens + msgTokens < MAX_TOKEN_LIMIT) {
newHistory.add(1, msg); // 保持系统消息在最前面
totalTokens += msgTokens;
} else {
break;
}
}
messageHistory = newHistory;
}
@Override
public void registerExtenderCallbacks(IBurpExtenderCallbacks callbacks) {
this.callbacks = callbacks;
this.helpers = callbacks.getHelpers();
this.history = new ArrayList<>();
this.historyTableModel = new HistoryTableModel();
// 设置日志输出
this.stdout = new PrintWriter(callbacks.getStdout(), true);
this.stderr = new PrintWriter(callbacks.getStderr(), true);
callbacks.setExtensionName("Chypass_Pro");
logToConsole("Chypass_Pro安装完成,开搞!!");
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
mainPanel = new JPanel(new BorderLayout());
// 创建API设置面板 - 使用更好的布局
JPanel apiPanel = new JPanel(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.anchor = GridBagConstraints.WEST;
gbc.insets = new Insets(5, 5, 5, 5);
// API提供商选择器
gbc.gridx = 0;
gbc.gridy = 0;
gbc.gridwidth = 1;
apiPanel.add(new JLabel("API Provider:"), gbc);
gbc.gridx = 1;
gbc.gridwidth = 2;
apiProviderSelector = new JComboBox<>(new String[]{"DeepSeek", "SiliconFlow", "Kimi", "QwenAI"});
apiPanel.add(apiProviderSelector, gbc);
// 添加开发者信息标签(显示在右上角)
// 多行文本,使用HTML换行
// 4) "开发者信息" 多行文本
JLabel developerLabel = new JLabel("公众号:白昼信安\n by:M9");
GridBagConstraints gbc_dev = new GridBagConstraints();
gbc_dev.gridx = 3;
gbc_dev.gridy = 0;
gbc_dev.insets = new Insets(5, 5, 5, 5);
gbc_dev.anchor = GridBagConstraints.EAST; // 靠右
apiPanel.add(developerLabel, gbc_dev);
// API密钥
gbc.gridx = 0;
gbc.gridy = 1;
gbc.gridwidth = 1;
apiPanel.add(new JLabel("API Key:"), gbc);
gbc.gridx = 1;
gbc.gridwidth = 2;
gbc.fill = GridBagConstraints.HORIZONTAL;
apiKeyField = new JTextField(40);
apiPanel.add(apiKeyField, gbc);
//API 保存按钮
// 加载已保存的 API Key
String savedApiKey = callbacks.loadExtensionSetting("API_KEY");
if (savedApiKey != null && !savedApiKey.isEmpty()) {
apiKeyField.setText(savedApiKey);
logToUI("加载已保存的 API Key");
}
// 创建“保存 API Key”按钮
JButton saveApiKeyButton = new JButton("保存 API Key");
// 设置按钮位置:例如放在同一行右侧(这里假设将其放在 gridx=3)
gbc.gridx = 3;
gbc.gridy = 1;
gbc.gridwidth = 1;
gbc.fill = GridBagConstraints.NONE;
apiPanel.add(saveApiKeyButton, gbc);
// 添加保存按钮的事件监听器
saveApiKeyButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
String key = apiKeyField.getText().trim();
callbacks.saveExtensionSetting("API_KEY", key);
logToUI("API Key 保存成功!");
}
});
// 创建模型配置面板 - 使用CardLayout来根据API提供商切换
modelConfigPanel = new JPanel();
modelConfigLayout = new CardLayout();
modelConfigPanel.setLayout(modelConfigLayout);
// DeepSeek模型配置(空白,因为DeepSeek不需要指定模型)
JPanel deepseekPanel = new JPanel(new BorderLayout());
modelConfigPanel.add(deepseekPanel, "DeepSeek");
// SiliconFlow模型配置
JPanel siliconflowPanel = new JPanel(new GridBagLayout());
GridBagConstraints sfGbc = new GridBagConstraints();
sfGbc.anchor = GridBagConstraints.WEST;
sfGbc.insets = new Insets(5, 5, 5, 5);
sfGbc.gridx = 0;
sfGbc.gridy = 0;
siliconflowPanel.add(new JLabel("Model Name:"), sfGbc);
sfGbc.gridx = 1;
sfGbc.fill = GridBagConstraints.HORIZONTAL;
JTextField siliconFlowModelField = new JTextField("", 40);
siliconflowPanel.add(siliconFlowModelField, sfGbc);
modelConfigPanel.add(siliconflowPanel, "SiliconFlow");
// Kimi模型配置
JPanel kimiPanel = new JPanel(new GridBagLayout());
GridBagConstraints kimiGbc = new GridBagConstraints();
kimiGbc.anchor = GridBagConstraints.WEST;
kimiGbc.insets = new Insets(5, 5, 5, 5);
kimiGbc.gridx = 0;
kimiGbc.gridy = 0;
kimiPanel.add(new JLabel("Model Name:"), kimiGbc);
kimiGbc.gridx = 1;
kimiGbc.fill = GridBagConstraints.HORIZONTAL;
JTextField kimiModelField = new JTextField("moonshot-v1-8k", 40);
kimiPanel.add(kimiModelField, kimiGbc);
modelConfigPanel.add(kimiPanel, "Kimi");
// QwenAI模型配置
JPanel qwenPanel = new JPanel(new GridBagLayout());
GridBagConstraints qwenGbc = new GridBagConstraints();
qwenGbc.anchor = GridBagConstraints.WEST;
qwenGbc.insets = new Insets(5, 5, 5, 5);
qwenGbc.gridx = 0;
qwenGbc.gridy = 0;
qwenPanel.add(new JLabel("Model Name:"), qwenGbc);
qwenGbc.gridx = 1;
qwenGbc.fill = GridBagConstraints.HORIZONTAL;
JTextField qwenModelField = new JTextField("qwen-plus", 40);
qwenPanel.add(qwenModelField, qwenGbc);
modelConfigPanel.add(qwenPanel, "QwenAI");
// 添加模型配置面板
gbc.gridx = 0;
gbc.gridy = 2;
gbc.gridwidth = 3;
apiPanel.add(modelConfigPanel, gbc);
// 显示初始模型配置面板
modelConfigLayout.show(modelConfigPanel, (String) apiProviderSelector.getSelectedItem());
// 设置API提供商切换监听器
apiProviderSelector.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
if (e.getStateChange() == ItemEvent.SELECTED) {
String provider = (String) apiProviderSelector.getSelectedItem();
modelConfigLayout.show(modelConfigPanel, provider);
}
}
});
mainPanel.add(apiPanel, BorderLayout.NORTH);
// 创建按钮面板
JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
sendToAIButton = new JButton("开启AI分析");
stopButton = new JButton("停止分析");
JButton clearHistoryButton = new JButton("清除全部记录");
buttonPanel.add(sendToAIButton);
buttonPanel.add(stopButton);
buttonPanel.add(clearHistoryButton);
// 添加“保存模板”按钮,将当前请求文本保存为模板
JButton saveTemplateButton = new JButton("保存模板");
buttonPanel.add(saveTemplateButton);
saveTemplateButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// 将右侧请求文本框内容作为模板保存到全局变量 originalTemplate 中
originalTemplate = requestTextArea.getText();
logToUI("已保存请求模板,模板中应包含 <xss> 占位符。");
}
});
// Create history table
historyTable = new JTable(historyTableModel);
historyTable.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
historyTable.getColumnModel().getColumn(0).setPreferredWidth(30);
historyTable.getColumnModel().getColumn(1).setPreferredWidth(200);
historyTable.getColumnModel().getColumn(2).setPreferredWidth(60);
historyTable.getColumnModel().getColumn(3).setPreferredWidth(60);
historyTable.getColumnModel().getColumn(4).setPreferredWidth(100);
// 设置历史表格的单元格渲染器,使文本垂直居中并进行截断
TableCellRenderer centerRenderer = new DefaultTableCellRenderer() {
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
Component c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
if (c instanceof JLabel) {
JLabel label = (JLabel) c;
label.setHorizontalAlignment(JLabel.CENTER);
if (value != null && value.toString().length() > 50) {
label.setToolTipText(value.toString());
}
}
return c;
}
};
// 应用渲染器到所有列
for (int i = 0; i < historyTable.getColumnCount(); i++) {
historyTable.getColumnModel().getColumn(i).setCellRenderer(centerRenderer);
}
JScrollPane historyScrollPane = new JScrollPane(historyTable);
requestTextArea = new JTextArea(10, 50);
responseTextArea = new JTextArea(10, 50);
aiResponseTextArea = new JTextArea(10, 50);
logTextArea = new JTextArea(5, 50);
logTextArea.setEditable(false);
JScrollPane requestScrollPane = new JScrollPane(requestTextArea);
JScrollPane responseScrollPane = new JScrollPane(responseTextArea);
JScrollPane aiResponseScrollPane = new JScrollPane(aiResponseTextArea);
JScrollPane logScrollPane = new JScrollPane(logTextArea);
// 在 requestTextArea 初始化后,添加右键菜单
requestTextArea.addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
if (e.isPopupTrigger()) {
showPopup(e);
}
}
@Override
public void mouseReleased(MouseEvent e) {
if (e.isPopupTrigger()) {
showPopup(e);
}
}
private void showPopup(MouseEvent e) {
JPopupMenu popupMenu = new JPopupMenu();
// 创建“Send to Repeater”菜单项
JMenuItem sendToRepeaterItem = new JMenuItem("Send to Repeater");
sendToRepeaterItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent evt) {
// 这里是点击菜单后真正执行的逻辑
sendCurrentRequestToRepeater();
}
});
popupMenu.add(sendToRepeaterItem);
popupMenu.show(e.getComponent(), e.getX(), e.getY());
}
});
JPanel requestPanel = new JPanel(new BorderLayout());
requestPanel.add(new JLabel("Request:"), BorderLayout.NORTH);
requestPanel.add(requestScrollPane, BorderLayout.CENTER);
JPanel responsePanel = new JPanel(new BorderLayout());
responsePanel.add(new JLabel("Response:"), BorderLayout.NORTH);
responsePanel.add(responseScrollPane, BorderLayout.CENTER);
JPanel aiResponsePanel = new JPanel(new BorderLayout());
aiResponsePanel.add(new JLabel("AI 分析结果:"), BorderLayout.NORTH);
aiResponsePanel.add(aiResponseScrollPane, BorderLayout.CENTER);
JPanel logPanel = new JPanel(new BorderLayout());
logPanel.add(new JLabel("Logs:"), BorderLayout.NORTH);
logPanel.add(logScrollPane, BorderLayout.CENTER);
JTabbedPane tabbedPane = new JTabbedPane();
tabbedPane.addTab("Request", requestPanel);
tabbedPane.addTab("Response", responsePanel);
tabbedPane.addTab("AI 分析结果", aiResponsePanel);
JPanel promptTabPanel = new JPanel(new BorderLayout());
// 提示词编辑区
JTextArea promptTextArea = new JTextArea(INITIAL_PROMPT, 10, 50);
promptTextArea.setLineWrap(true);
promptTextArea.setWrapStyleWord(true);
JScrollPane promptScrollPane = new JScrollPane(promptTextArea);
// “保存提示词”按钮
JButton savePromptButton = new JButton("保存提示词");
savePromptButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
INITIAL_PROMPT = promptTextArea.getText();
logToUI("AI提示词已更新!");
}
});
// 布局到面板
promptTabPanel.add(promptScrollPane, BorderLayout.CENTER);
promptTabPanel.add(savePromptButton, BorderLayout.SOUTH);
// 把这个面板添加为一个新的标签
tabbedPane.addTab("当前AI提示词", promptTabPanel);
JSplitPane mainContentPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
mainContentPane.setTopComponent(tabbedPane);
mainContentPane.setBottomComponent(logPanel);
mainContentPane.setResizeWeight(0.8);
JSplitPane mainSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
mainSplitPane.setLeftComponent(historyScrollPane);
mainSplitPane.setRightComponent(mainContentPane);
mainSplitPane.setResizeWeight(0.3);
JPanel centerPanel = new JPanel(new BorderLayout());
centerPanel.add(buttonPanel, BorderLayout.NORTH);
centerPanel.add(mainSplitPane, BorderLayout.CENTER);
mainPanel.add(centerPanel, BorderLayout.CENTER);
// 存储模型字段引用
modelNameField = siliconFlowModelField;
// 创建一个对象用于存储当前迭代状态
final AtomicReference<AISessionState> sessionState = new AtomicReference<>(new AISessionState());
sendToAIButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// 根据当前选择的API提供商获取正确的模型名称
String provider = (String) apiProviderSelector.getSelectedItem();
if ("SiliconFlow".equals(provider)) {
modelNameField = siliconFlowModelField;
} else if ("Kimi".equals(provider)) {
modelNameField = kimiModelField;
} else if ("QwenAI".equals(provider)) {
modelNameField = qwenModelField;
}
startAISession(sessionState.get());
}
});
stopButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
stopAISession();
}
});
clearHistoryButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// 清空对话历史和测试记录
messageHistory.clear();
history.clear();
testPairIdToHistoryIndex.clear();
historyTableModel.fireTableDataChanged();
// 清空所有文本区域
requestTextArea.setText("");
responseTextArea.setText("");
aiResponseTextArea.setText("");
logTextArea.setText("");
// 清除请求模板
originalTemplate = "";
logToUI("全部面板内容和请求模板已清除");
}
});
historyTable.getSelectionModel().addListSelectionListener(e -> {
if (!e.getValueIsAdjusting()) {
int selectedRow = historyTable.getSelectedRow();
if (selectedRow >= 0 && selectedRow < history.size()) {
RequestResponseAIPair pair = history.get(selectedRow);
requestTextArea.setText(new String(pair.getRequest(), StandardCharsets.UTF_8));
responseTextArea.setText(new String(pair.getResponse(), StandardCharsets.UTF_8));
aiResponseTextArea.setText(pair.getAiResponse());
// 自动切换到AI Analysis选项卡来显示AI分析
tabbedPane.setSelectedIndex(2);
}
}
});
callbacks.customizeUiComponent(mainPanel);
callbacks.addSuiteTab(BurpExtender.this);
callbacks.registerContextMenuFactory(BurpExtender.this);
logToUI("UI已初始化,随时可以使用");
}
});
}
// 新增:AI会话状态类,用于在不同迭代之间保持状态
private class AISessionState {
private int iterationCount = 0;
private String lastAIResponse = null;
private boolean waitingForManualFix = false;
private String manualFixedRequest = null;
private int lastTestPairId = -1; // 跟踪最后添加的测试对的ID
public int getIterationCount() {
return iterationCount;
}
public void incrementIterationCount() {
this.iterationCount++;
}
public String getLastAIResponse() {
return lastAIResponse;
}
public void setLastAIResponse(String lastAIResponse) {
this.lastAIResponse = lastAIResponse;
}
public boolean isWaitingForManualFix() {
return waitingForManualFix;
}
public void setWaitingForManualFix(boolean waitingForManualFix) {
this.waitingForManualFix = waitingForManualFix;
}
public String getManualFixedRequest() {
return manualFixedRequest;
}
public void setManualFixedRequest(String manualFixedRequest) {
this.manualFixedRequest = manualFixedRequest;
}
public int getLastTestPairId() {
return lastTestPairId;
}
public void setLastTestPairId(int lastTestPairId) {
this.lastTestPairId = lastTestPairId;
}
public void reset() {
iterationCount = 0;
lastAIResponse = null;
waitingForManualFix = false;
manualFixedRequest = null;
lastTestPairId = -1;
}
}
@Override
public String getTabCaption() {
return "Chypass_Pro";
}
@Override
public Component getUiComponent() {
return mainPanel;
}
@Override
public List<JMenuItem> createMenuItems(IContextMenuInvocation invocation) {
List<JMenuItem> menuItems = new ArrayList<>();
JMenuItem sendToPluginMenuItem = new JMenuItem("Send to Chypass_Pro");
sendToPluginMenuItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
IHttpRequestResponse[] messages = invocation.getSelectedMessages();
if (messages != null && messages.length > 0) {
IHttpRequestResponse message = messages[0];
byte[] request = message.getRequest();
byte[] response = message.getResponse();
// 保存当前HTTP服务信息,以便后续使用
currentHttpService = message.getHttpService();
logToConsole("接收到的HTTP服务详情: " +
currentHttpService.getHost() + ":" +
currentHttpService.getPort() + " (" +
currentHttpService.getProtocol() + ")");
requestTextArea.setText(new String(request, StandardCharsets.UTF_8));
if (response != null) {
responseTextArea.setText(new String(response, StandardCharsets.UTF_8));
} else {
responseTextArea.setText("");
}
logToUI("从上下文菜单加载请求");
}
}
});
menuItems.add(sendToPluginMenuItem);
return menuItems;
}
// 修改:使用AISessionState作为参数
private void startAISession(AISessionState sessionState) {
if (isRunning) {
return;
}
if (currentHttpService == null) {
JOptionPane.showMessageDialog(mainPanel,
"没有可用的HTTP服务详细信息。请右键单击Burp中的请求,然后选择“发送至Chypass_Pro",
"缺少HTTP服务详细信息",
JOptionPane.ERROR_MESSAGE);
logToUI("错误:缺少HTTP服务详细信息");
return;
}
apiKey = apiKeyField.getText().trim();
if (apiKey.isEmpty()) {
JOptionPane.showMessageDialog(mainPanel, "请输入API密钥", "需要API密钥", JOptionPane.ERROR_MESSAGE);
logToUI("这密钥对吗老弟!");
return;
}
String requestContent = requestTextArea.getText();
if (requestContent.isEmpty()) {
JOptionPane.showMessageDialog(mainPanel, "首先加载一个请求", "没有请求", JOptionPane.ERROR_MESSAGE);
logToUI("这请求不对吧哥");
return;
}
isRunning = true;
sendToAIButton.setEnabled(false);
stopButton.setEnabled(true);
// 重置状态
sessionState.reset();
String apiProvider = (String) apiProviderSelector.getSelectedItem();
logToUI("开始AI会话 " + apiProvider + " API");
//如果为空,则初始化消息历史(第一次运行)
if (messageHistory.isEmpty()) {
// Add system message
JSONObject systemMessage = new JSONObject();
systemMessage.put("role", "system");
systemMessage.put("content", INITIAL_PROMPT);
messageHistory.add(systemMessage);
}
// 在后台线程中启动
new Thread(() -> {
String initialConversation = "Initial HTTP Request:\n\n" + requestContent;
String responseContent = responseTextArea.getText();
if (!responseContent.isEmpty()) {
// 对响应内容进行截断,防止过长
responseContent = truncateContent(responseContent, 5000);
initialConversation += "\n\nInitial HTTP Response:\n\n" + responseContent;
}
// 添加用户的初始消息
JSONObject userMessage = new JSONObject();
userMessage.put("role", "user");
userMessage.put("content", initialConversation);
// 使用基于token数量的裁剪方式管理对话历史
// 始终先调用 trimMessageHistory() 确保历史不会超限,然后添加最新用户消息
trimMessageHistory();
messageHistory.add(userMessage);
processAIIteration(sessionState);
}).start();
}
// 新增:处理AI迭代的核心方法
private void processAIIteration(AISessionState sessionState) {
if (!isRunning) { return; }
try {
sessionState.incrementIterationCount();
String apiProvider = (String) apiProviderSelector.getSelectedItem();
logToUI("正在向 " + apiProvider + " API 发送对话...(第 " + sessionState.getIterationCount() + " 次迭代)");
logToConsole("消息历史记录大小:" + messageHistory.size());
final String currentRequest = requestTextArea.getText();
final String currentResponse = responseTextArea.getText();
final byte[] currentRequestBytes = currentRequest.getBytes(StandardCharsets.UTF_8);
final byte[] currentResponseBytes = currentResponse.getBytes(StandardCharsets.UTF_8);
String aiResponse;
if ("DeepSeek".equals(apiProvider)) {
aiResponse = sendToDeepSeekAI();
} else if ("SiliconFlow".equals(apiProvider)) {
aiResponse = sendToSiliconFlowAI();
} else if ("QwenAI".equals(apiProvider)) {
aiResponse = sendToQwenAI();
} else { // 默认Kimi
aiResponse = sendToKimiAIWithRetry(0);
}
logToUI("从 " + apiProvider + " AI 接收到回复");
sessionState.setLastAIResponse(aiResponse);
JSONObject assistantMessage = new JSONObject();
assistantMessage.put("role", "assistant");
assistantMessage.put("content", aiResponse);
messageHistory.add(assistantMessage);
final String finalAiResponse = aiResponse;
RequestResponseAIPair currentPair = new RequestResponseAIPair(
currentRequestBytes,
currentResponseBytes,
finalAiResponse,
history.size() + 1
);
history.add(currentPair);
// Update AI response area and history table
SwingUtilities.invokeLater(() -> {
aiResponseTextArea.setText(finalAiResponse);
historyTableModel.fireTableDataChanged();
if (!history.isEmpty()) {
historyTable.setRowSelectionInterval(history.size() - 1, history.size() - 1);
}
});
// 定义最大重试次数
int maxRetries = 5;
int retryCount = 0;
String extractedPayload = null;
// 尝试循环提取payload
while (retryCount < maxRetries) {
extractedPayload = extractPayloadFromAIResponse(aiResponse);
if (extractedPayload != null && !extractedPayload.isEmpty()) {
break;
}
retryCount++;
logToUI("提取payload失败,等待2秒后重试 (第 " + retryCount + " 次)...");
try {
Thread.sleep(2000);
} catch (InterruptedException ie) {
// 如果被中断,则跳出循环
break;
}
}
// 判断是否成功提取payload
if (extractedPayload != null && !extractedPayload.isEmpty()) {
logToUI("从AI响应中提取到payload: " + extractedPayload);
// 1) 用模板替换 <xss>
if (originalTemplate == null || originalTemplate.isEmpty()) {
handleRequestExtractionFailure(sessionState, "originalTemplate 为空,请先确认模板");
return;
}
String finalRequest = buildFinalRequest(originalTemplate, extractedPayload);
// 2) 校验并修正请求
finalRequest = validateAndFixRequest(finalRequest);
if (finalRequest == null) {
handleRequestExtractionFailure(sessionState, "无法验证拼接后的请求");
return;
}
// 3) 发送请求
processValidRequest(finalRequest, sessionState);
} else {
// 如果重试多次后依然没有提取到payload,则发送原始数据包
logToUI("重试 " + maxRetries + " 次后仍未提取到有效payload,使用原始请求发送数据包");
processValidRequest(requestTextArea.getText(), sessionState);
}
// 为不同的API提供商设置不同的延迟时间
String currentProvider = (String) apiProviderSelector.getSelectedItem();
if ("Kimi".equals(currentProvider)) {
logToUI("为Kimi API添加额外的延迟以避免速率限制...");
Thread.sleep(3000); // Kimi API使用3秒延迟
} else if ("DeepSeek".equals(currentProvider)) {
Thread.sleep(1500); // DeepSeek使用1.5秒延迟
} else if ("QwenAI".equals(currentProvider)) {
Thread.sleep(2000); // QwenAI使用2秒延迟
} else {
Thread.sleep(1000); // 其他API使用1秒延迟
}
// 继续下一次迭代
processAIIteration(sessionState);
} catch (Exception e) {
String errorMessage = "Error: " + e.getMessage();
logToUI(errorMessage);
logToConsole("Exception: " + e.toString());
e.printStackTrace(stderr);
JOptionPane.showMessageDialog(mainPanel, errorMessage, "Error", JOptionPane.ERROR_MESSAGE);
// 恢复UI状态
SwingUtilities.invokeLater(() -> {
isRunning = false;
sendToAIButton.setEnabled(true);
stopButton.setEnabled(false);
logToUI("由于错误,AI会话停止");
});
}
}
/**
* 判断响应文本中是否包含 WAF 拦截的关键词
*/
private boolean isWafIntercepted(String responseText) {
if (responseText == null) {
return false;
}
String lowerText = responseText.toLowerCase();
return lowerText.contains("waf") || lowerText.contains("被拦截") ||
lowerText.contains("安全狗") || lowerText.contains("雷池") ||
lowerText.contains("防火墙拦截") || lowerText.contains("造成威胁") ||
lowerText.contains("攻击行为") || lowerText.contains("反馈误报") ||
lowerText.contains("黑客攻击") || lowerText.contains("危险内容") ||
lowerText.contains("不合法") || lowerText.contains("拦截") ||
lowerText.contains("宝塔") || lowerText.contains("创宇盾");
}
// 新增:处理请求提取失败的方法
private void handleRequestExtractionFailure(AISessionState sessionState, String errorMessage) {
logToUI("Error: " + errorMessage);
logToConsole(errorMessage);
// 在UI线程中更新UI状态
SwingUtilities.invokeLater(() -> {
// 提示用户修复请求
String message = errorMessage + "\n,AI卡死报错,点击停止分析,点击清除全部记录,重新开始!!!'\n";
JOptionPane.showMessageDialog(
mainPanel,
message,
"得,AI被你搞坏了吧",
JOptionPane.WARNING_MESSAGE
);
// 显示AI分析和当前请求,以便用户可以编辑
aiResponseTextArea.setText(sessionState.getLastAIResponse());
requestTextArea.setText(""); // 清空请求框,等待用户输入新请求
});
}
// 新增:使用手动修复的请求继续AI会话
private void continuteAISessionWithFixedRequest(AISessionState sessionState) {
if (!isRunning || !sessionState.isWaitingForManualFix()) {
return;
}
// 使用手动修复的请求
String fixedRequest = sessionState.getManualFixedRequest();
if (fixedRequest == null || fixedRequest.isEmpty()) {
logToUI("错误:未提供固定请求");
return;
}
// 重置状态
sessionState.setWaitingForManualFix(false);
sessionState.setManualFixedRequest(null);
try {
// 处理修复后的请求
processValidRequest(fixedRequest, sessionState);
// 为下一轮迭代添加延迟
Thread.sleep(1000);
// 继续下一次迭代
processAIIteration(sessionState);
} catch (Exception e) {
String errorMessage = "错误处理固定请求: " + e.getMessage();
logToUI(errorMessage);
logToConsole(errorMessage);
e.printStackTrace(stderr);
// 恢复UI状态
SwingUtilities.invokeLater(() -> {
isRunning = false;
sendToAIButton.setEnabled(true);
stopButton.setEnabled(false);
logToUI("AI会话因错误而停止");
});
}
}
// 新增:处理有效请求的方法
private void processValidRequest(String validRequest, AISessionState sessionState) throws Exception {
// 使用HTTP请求的字节形式
byte[] requestBytes = validRequest.getBytes(StandardCharsets.UTF_8);
logToUI("向目标发送经过验证的请求: " +
currentHttpService.getHost() + ":" +
currentHttpService.getPort());
// 使用Burp的IHttpService发送请求
IHttpRequestResponse httpRequestResponse = null;
try {
// 使用Burp的IRequestInfo分析请求以确保格式正确
IRequestInfo requestInfo = helpers.analyzeRequest(requestBytes);
if (requestInfo.getMethod() == null || requestInfo.getUrl() == null) {
throw new Exception("无效的请求格式:缺少方法或URL");
}
httpRequestResponse = callbacks.makeHttpRequest(
currentHttpService,
requestBytes);
} catch (Exception e) {
logToUI("发送请求错误: " + e.getMessage());
// 尝试使用紧急修复
String fixedRequest = tryToFixHttpRequestFormat(validRequest);
if (fixedRequest != null) {
logToUI("尝试使用紧急固定请求格式");
requestBytes = fixedRequest.getBytes(StandardCharsets.UTF_8);
try {
httpRequestResponse = callbacks.makeHttpRequest(
currentHttpService,
requestBytes);
// 更新提取的请求为修复后的请求
validRequest = fixedRequest;
} catch (Exception ex) {
logToUI("发送固定请求错误: " + ex.getMessage());
throw ex; // 重新抛出异常以终止当前迭代
}
} else {
throw e; // 如果无法修复,重新抛出原始异常
}
}
//检查响应
if (httpRequestResponse == null || httpRequestResponse.getResponse() == null) {
throw new Exception("未收到目标响应");
}