static void Main(string[] args) { Console.WriteLine("Start: Code Analyze Server"); /* 연도별 CVE JSON 파일 로드 */ CVE_JSON.AutoLoad(); /* 크롤러 타입 */ var crawler = new VulnC(); /* 매칭을 위한 자료구조 Bloom Filter */ int capacity = 50000000; var filter = new Filter <string>(capacity); /* AWS 계정 정보 파일 읽음 */ string txt = File.ReadAllText(@"Account.xml"); // string xml = aes.AESDecrypt128(txt, key); string xml = txt; AWS.LoadAccount(xml); AWS.Account account = AWS.account; /* AWS 정보 출력 */ Console.WriteLine($"Endpoint: {account.Endpoint}, ID: {account.Id}"); try { /* DB 접속 시도 */ VulnRDS.Connect(account, "vuln"); } catch (Exception e) { Console.WriteLine($"Connection Error :: {e.ToString()}"); return; } /* AWS 연결 여부 확인 */ if (VulnRDS.Conn.State == System.Data.ConnectionState.Open) { Console.WriteLine("Connection Success"); } else { Console.WriteLine("Fail Connection"); return; } while (true) { string userId = string.Empty; string repoPath = string.Empty; Stopwatch repoWatch = new Stopwatch(); repoWatch.Start(); while (true) { var elapsedSeconds = repoWatch.Elapsed.TotalSeconds; if (elapsedSeconds < 10) { continue; } Console.WriteLine("Checking User DB..."); var reposits = VulnRDS.SelectAllReposit(); foreach (var(userName, repository) in reposits) { if (string.IsNullOrWhiteSpace(repository)) { continue; } var repoBytes = Encoding.Unicode.GetBytes(repository); var repoBase64 = Convert.ToBase64String(repoBytes); var repoDir = new DirectoryInfo($@"C:\Repo\{repoBase64}"); if (repoDir.Exists) { continue; } repoDir.Create(); Console.WriteLine($"Clone... Path : {repoDir.FullName}, Url : {repository}"); Clone(repoDir.FullName, repository); repoPath = repoDir.FullName; userId = userName; } if (!string.IsNullOrWhiteSpace(repoPath) && !string.IsNullOrWhiteSpace(userId)) { break; } repoWatch.Restart(); } //Console.WriteLine("엔터를 누르세요"); //Console.ReadLine(); /* hashDict = 사용된 사용자 함수 정보 */ var hashDict = new Dictionary <int, HashSet <VulnAbstractCrawler.UserBlock> >(); /* 경과 시간 체크 */ Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); DirectoryInfo dirInfo = new DirectoryInfo(repoPath); /* 모든 .c 파일 탐색 */ var codeFiles = dirInfo.EnumerateFiles("*.c", SearchOption.AllDirectories); int totalFileCount = codeFiles.Count(); int count = 0; foreach (var codeFile in codeFiles) { Console.WriteLine(codeFile.FullName); using (var reader = codeFile.OpenText()) { /* 사용자 코드를 함수별로 나눔 */ var dict = crawler.CrawlUserCode(reader); foreach (var item in dict) { /* hashDict의 키와 item.key는 함수 블록의 코드 길이 */ if (!hashDict.ContainsKey(item.Key)) { hashDict[item.Key] = new HashSet <VulnAbstractCrawler.UserBlock>(); } /* item.Value는 각 코드 길이 마다의 블록 정보 * Bloom Filter에 코드 블록 해쉬값 기록 */ foreach (var hash in item.Value) { hash.Path = codeFile.FullName; hashDict[item.Key].Add(hash); filter.Add(hash.Hash); } } count++; double per = ((double)count / (double)totalFileCount) * 100; Console.WriteLine($"{count} / {totalFileCount} :: {per.ToString("#0.0")}%, 개체 수 : {hashDict.Count}"); } } var findBlocks = new Queue <VulnAbstractCrawler.UserBlock>(); var vulnDict = new Dictionary <string, IEnumerable <VulnRDS._Vuln> >(); foreach (var set in hashDict) { /* 사용자 코드의 길이 마다 DB로 부터 같은 길이의 CVE 레코드 목록 가져옴 */ var cveList = VulnRDS.SelectVulnbyLen(set.Key).Select(v => v.Cve).Distinct(); foreach (var cve in cveList) { if (!vulnDict.ContainsKey(cve)) { vulnDict[cve] = new HashSet <VulnRDS._Vuln>(); var vulnHashSet = vulnDict[cve] as HashSet <VulnRDS._Vuln>; /* 같은 길이의 CVE에서 또 같은 종류의 CVE 레코드 목록 가져옴 * 같은 종류의 CVE 레코드들이 사용자 코드에서 모두 포함되어야 * CVE를 가지고 있다고 인정하는 프로그램 정책 때문 */ var searchedCveHashList = VulnRDS.SelectVulnbyCve(cve); Console.WriteLine($"CVE:{cve}, Received Count : {searchedCveHashList.Count()}"); foreach (var s in searchedCveHashList) { vulnHashSet.Add(s); } } } } var findCveDict = new Dictionary <string, List <VulnAbstractCrawler.UserBlock> >(); var findCveList = new HashSet <string>(); /* 본격적인 취약점 매칭 부분 */ foreach (var vulnSet in vulnDict) { Console.WriteLine($"-----cve:{vulnSet.Key}"); bool match = false; foreach (var vuln in vulnSet.Value) { /* 사용자 코드 해쉬 저장해논 bloom filter에 취약점 레코드 해쉬값들이 포함되는지 확인 * 포함이 된다는 건 해당 취약점 레코드가 사용자 코드에도 있다는 뜻(취약점) * 같은 종류의 CVE 레코드가 전부 필터에 포함된다면 취약점으로 판단한다. */ if (filter.Contains(vuln.BlockHash)) { if (hashDict.ContainsKey(vuln.LenFunc)) { /* Bloom Filter는 아쉽게도 포함 여부만 알 수 있기에 * 포함되었음을 알았다면 검색해서 정보를 구한다. */ var userBlock = hashDict[vuln.LenFunc].FirstOrDefault(b => b.Hash == vuln.BlockHash); if (userBlock == null) { continue; } /* 해당 유저 블록을 임시 저장한다. * 밑에서 블록 정보를 DB로 전송하기 위해서다. */ if (!findCveDict.ContainsKey(vuln.Cve)) { findCveDict[vuln.Cve] = new List <VulnAbstractCrawler.UserBlock>(); } userBlock.Url = vuln.Url; findCveDict[vuln.Cve].Add(userBlock); match = true; } } else { match = false; break; } } /* 취약점 레코드가 전부 있어야 CVE 찾음 인정 */ if (match) { Console.WriteLine($"Matched CVE : {vulnSet.Key}"); /* 찾았으면 cve값을 기록함 밑에서 찾은 cve 정보 전송하기 위해 */ findCveList.Add(vulnSet.Key); } else { Console.WriteLine("Not"); } } stopwatch.Stop(); /* 매칭 끝 후처리 (출력, DB 전송 등) */ var hours = stopwatch.Elapsed.Hours; var minutes = stopwatch.Elapsed.Minutes; var seconds = stopwatch.Elapsed.Seconds; Console.WriteLine($"Elapsed Time : {hours.ToString("00")}:{minutes.ToString("00")}:{seconds.ToString("00")}"); Console.WriteLine($"Matched CVE Count : {findCveList.Count}"); var yearMatch = new Regex(@"CVE-(\d{4})-(\d+)"); foreach (var cve in findCveList) { Console.WriteLine(cve); var c = yearMatch.Match(cve); int year = int.Parse(c.Groups[1].Value); if (!CVE_JSON.CveDict.ContainsKey(year)) { continue; } if (!CVE_JSON.CveDict[year].ContainsKey(cve)) { continue; } var data = CVE_JSON.CveDict[year][cve]; /* 취약점 타입 분류 */ string type = "NORMAL"; if (data.Detail.IndexOf("overflow", StringComparison.CurrentCultureIgnoreCase) > 0) { type = "OVERFLOW"; } else if (data.Detail.IndexOf("xss", StringComparison.CurrentCultureIgnoreCase) > 0) { type = "XSS"; } else if (data.Detail.IndexOf("injection", StringComparison.CurrentCultureIgnoreCase) > 0) { type = "SQLINJECTION"; } else if (data.Detail.IndexOf("dos", StringComparison.CurrentCultureIgnoreCase) > 0) { type = "DOS"; } else if (data.Detail.IndexOf("Memory", StringComparison.CurrentCultureIgnoreCase) > 0) { type = "MEMORY"; } else if (data.Detail.IndexOf("CSRF", StringComparison.CurrentCultureIgnoreCase) > 0) { type = "CSRF"; } else if (data.Detail.IndexOf("inclusion", StringComparison.CurrentCultureIgnoreCase) > 0) { type = "FILEINCLUSION"; } else if (data.Detail.IndexOf("EXCUTE", StringComparison.CurrentCultureIgnoreCase) > 0) { type = "EXCUTE"; } var urlBytes = Convert.FromBase64String(findCveDict[cve].FirstOrDefault().Url); string url = Encoding.Unicode.GetString(urlBytes); var vulnDetail = new VulnRDS.Vuln_detail { CveName = data.Code, Type = type, Level = data.Level.ToString(), Year = data.Year.ToString(), CveDetail = data.Detail, Publish_date = data.Publish_Date.ToString("yyyy-MM-dd"), Update_date = data.Update_Date.ToString("yyyy-MM-dd"), UserName = userId, Url = url, FileName = findCveDict[cve].FirstOrDefault().Path.Replace(repoPath, ""), FuncName = findCveDict[cve].FirstOrDefault().FuncName, Product = data.Type, }; Console.WriteLine("추가 완료"); /* DB 전송 */ VulnRDS.InsertVulnDetail(vulnDetail); Console.WriteLine($"Added CVE: {vulnDetail.CveName}, Type: {vulnDetail.Type}, CVSS: {vulnDetail.Level}"); } } }