-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathindex.html
More file actions
122 lines (108 loc) · 4.62 KB
/
index.html
File metadata and controls
122 lines (108 loc) · 4.62 KB
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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>GitHub Project Scanner</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container py-5">
<h1>GitHub Backdoor Scanner</h1>
<h5 class="mb-3">Scan for infected MSBuild project files</h5>
<div class="alert alert-warning">
This tool only scans for common indicators of potentially infected files. Handle untrusted code with caution.
</div>
<form id="scanForm">
<div class="mb-3">
<label for="repoUrl" class="form-label">GitHub Repository URL</label>
<input type="url" class="form-control" id="repoUrl" placeholder="https://github.com/owner/repo" required>
</div>
<div class="mb-3">
<label for="token" class="form-label">GitHub API Token (optional)</label>
<input type="text" class="form-control" id="token" placeholder="github_pat_YourTokenHere">
</div>
<button type="submit" class="btn btn-primary">Scan Repository</button>
</form>
<hr>
<div id="errors" class="mt-4"></div>
<div id="results" class="mt-4"></div>
</div>
<script>
const TARGET_STRINGS = [
"PreBuildEvent",
"PostBuildEvent",
"Exec",
"Command=",
"@echo off",
"cscript",
"//nologo",
"cmd.exe",
"-hidden",
"ExecutionPolicy",
"powershell.exe",
"wscript.exe",
".vbs",
"CreateObject(",
""WScript.Shell"",
];
document.getElementById('scanForm').addEventListener('submit', async (e) => {
e.preventDefault();
const repoUrl = document.getElementById('repoUrl').value.trim();
const token = document.getElementById('token').value.trim();
const resultsDiv = document.getElementById('results');
const errorsDiv = document.getElementById('errors');
resultsDiv.innerHTML = '<div class="spinner-border" role="status"></div> Scanning...';
try {
const match = repoUrl.match(/github\.com\/(.+?)\/(.+?)(\.git)?(\/$)?$/);
if (!match) throw new Error("Invalid GitHub repository URL. Please enter a valid GitHub repository URL");
const owner = encodeURIComponent(match[1]);
const repo = encodeURIComponent(match[2]);
const headers = {
'Accept': 'application/vnd.github.v3+json'
};
if (token) headers['Authorization'] = `token ${token}`;
const repoInfo = await fetch(`https://api.github.com/repos/${owner}/${repo}`, { headers });
if (!repoInfo.ok) throw new Error("Failed to fetch repository info.");
const repoData = await repoInfo.json();
const branch = repoData.default_branch;
const treeResp = await fetch(`https://api.github.com/repos/${owner}/${repo}/git/trees/${branch}?recursive=1`, { headers });
if (!treeResp.ok) throw new Error("Failed to fetch repository tree.");
const treeData = await treeResp.json();
const projectFiles = treeData.tree.filter(item => item.type === 'blob' && (item.path.endsWith('.csproj') || item.path.endsWith('.vbproj' || item.path.endsWith('.vcxproj'))));
const matches = [];
for (const file of projectFiles) {
const contentResp = await fetch(`https://api.github.com/repos/${owner}/${repo}/contents/${encodeURIComponent(file.path)}`, { headers }); // Encode URL values to avoid errors with special chatacters for strings like C#
if (!contentResp.ok) {
errorsDiv.innerHTML += `<div class="alert alert-danger">Error fetching file content: ${contentResp.statusText}</div>`;
continue;
}
const contentData = await contentResp.json();
const content = atob(contentData.content || '');
const found = TARGET_STRINGS.filter(str => content.includes(str));
if (found.length >= 2) {
matches.push({ path: file.path, found });
}
}
if (matches.length === 0) {
resultsDiv.innerHTML = '<div class="alert alert-info">No matches found.</div>';
} else {
let html = '<h4>Matches Found:</h4><ul class="list-group">';
for (const match of matches) {
html += `<li class="list-group-item"><div class="alert alert-danger"><strong>${match.path}</strong><br>Matched: ${match.found.join(', ')}</div></li>`;
}
html += '</ul>';
resultsDiv.innerHTML = html;
}
} catch (err) {
resultsDiv.innerHTML = `<div class="alert alert-danger">Error: ${err.message}</div>`;
}
});
</script>
<footer class="footer text-center">
<div class="container">
<p>Developed by <a href="https://x.com/cod3nym/">Jonathan Peters</a></p>
</div>
</footer>
</body>
</html>